tinyrelayfile
tinyrelay/tinygit/private_repair_acceptance_test.go/

tinygit/private_repair_acceptance_test.go raw
1package tinygit
2
3import (
4 "context"
5 "crypto/x509"
6 "encoding/pem"
7 "io"
8 "net/http"
9 "net/http/httptest"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "strconv"
14 "strings"
15 "sync/atomic"
16 "testing"
17
18 "github.com/FelineStateMachine/tinyrelay/internal/policy"
19 "github.com/FelineStateMachine/tinyrelay/internal/storage"
20)
21
22func TestFetchMissingHTTPSRepairAndNoop(t *testing.T) {
23 root := t.TempDir()
24 remoteRoot := filepath.Join(root, "remote")
25 work := filepath.Join(root, "work")
26 gitRepairRun(t, "init", "--bare", filepath.Join(remoteRoot, "repo.git"))
27 gitRepairRun(t, "init", work)
28 gitRepairRun(t, "-C", work, "config", "user.email", "repair@example.test")
29 gitRepairRun(t, "-C", work, "config", "user.name", "Repair")
30 if err := os.WriteFile(filepath.Join(work, "README"), []byte("repair\n"), 0600); err != nil {
31 t.Fatal(err)
32 }
33 gitRepairRun(t, "-C", work, "add", "README")
34 gitRepairRun(t, "-C", work, "commit", "-m", "repair")
35 commit := strings.TrimSpace(gitRepairOutput(t, "-C", work, "rev-parse", "HEAD"))
36 gitRepairRun(t, "-C", work, "push", filepath.Join(remoteRoot, "repo.git"), "HEAD:refs/heads/main")
37
38 var requests atomic.Int32
39 server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
40 requests.Add(1)
41 serveGitBackend(t, w, r, remoteRoot)
42 }))
43 t.Cleanup(server.Close)
44 caFile := filepath.Join(root, "ca.pem")
45 cert, err := x509.ParseCertificate(server.Certificate().Raw)
46 if err != nil {
47 t.Fatal(err)
48 }
49 if err := os.WriteFile(caFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}), 0600); err != nil {
50 t.Fatal(err)
51 }
52 t.Setenv("GIT_SSL_CAINFO", caFile)
53
54 store, err := storage.Open(context.Background(), filepath.Join(root, "relay.db"))
55 if err != nil {
56 t.Fatal(err)
57 }
58 t.Cleanup(func() { _ = store.Close() })
59 p := policy.Defaults("")
60 g, err := New(Config{Store: tinyStore(store), Root: filepath.Join(root, "git"), Policy: func() Policy { return fromInternalPolicy(p) }, AllowPrivateRelays: true})
61 if err != nil {
62 t.Fatal(err)
63 }
64 repo := Repository{Owner: strings.Repeat("a", 64), Identifier: "repo", Clone: []string{server.URL + "/repo.git"}, Refs: map[string]string{"refs/heads/main": commit}}
65 if err := g.FetchMissing(context.Background(), repo, repo.Clone, repo.Refs); err != nil {
66 t.Fatal(err)
67 }
68 if requests.Load() == 0 || !gitRepairObject(t, filepath.Join(root, "git", repo.Owner, "repo.git"), commit) {
69 t.Fatalf("repair did not fetch commit: requests=%d", requests.Load())
70 }
71 firstRequests := requests.Load()
72 if err := g.FetchMissing(context.Background(), repo, repo.Clone, repo.Refs); err != nil {
73 t.Fatal(err)
74 }
75 if requests.Load() != firstRequests {
76 t.Fatalf("noop repair made network requests: before=%d after=%d", firstRequests, requests.Load())
77 }
78}
79
80func serveGitBackend(t *testing.T, w http.ResponseWriter, r *http.Request, root string) {
81 t.Helper()
82 env := append(os.Environ(), "GIT_PROJECT_ROOT="+root, "GIT_HTTP_EXPORT_ALL=1", "PATH_INFO="+r.URL.Path, "REQUEST_METHOD="+r.Method, "QUERY_STRING="+r.URL.RawQuery, "CONTENT_TYPE="+r.Header.Get("Content-Type"), "CONTENT_LENGTH="+strconv.FormatInt(r.ContentLength, 10))
83 cmd := exec.Command("git", "http-backend")
84 cmd.Env = env
85 cmd.Stdin = r.Body
86 out, err := cmd.Output()
87 if err != nil {
88 http.Error(w, err.Error(), http.StatusBadGateway)
89 return
90 }
91 parts := strings.SplitN(string(out), "\r\n\r\n", 2)
92 if len(parts) != 2 {
93 http.Error(w, "invalid git backend response", http.StatusBadGateway)
94 return
95 }
96 for _, line := range strings.Split(parts[0], "\r\n") {
97 fields := strings.SplitN(line, ": ", 2)
98 if len(fields) == 2 {
99 w.Header().Set(fields[0], fields[1])
100 }
101 }
102 _, _ = io.WriteString(w, parts[1])
103}
104
105func gitRepairRun(t *testing.T, args ...string) {
106 t.Helper()
107 if out, err := exec.Command("git", args...).CombinedOutput(); err != nil {
108 t.Fatalf("git %v: %v: %s", args, err, out)
109 }
110}
111func gitRepairOutput(t *testing.T, args ...string) string {
112 t.Helper()
113 out, err := exec.Command("git", args...).Output()
114 if err != nil {
115 t.Fatal(err)
116 }
117 return string(out)
118}
119func gitRepairObject(t *testing.T, dir, object string) bool {
120 t.Helper()
121 return exec.Command("git", "--git-dir", dir, "cat-file", "-e", object).Run() == nil
122}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs