| 1 | package tinygit_test |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "encoding/json" |
| 7 | "net/http" |
| 8 | "net/http/httptest" |
| 9 | "strings" |
| 10 | "testing" |
| 11 | |
| 12 | "github.com/FelineStateMachine/tinyrelay/internal/event" |
| 13 | "github.com/FelineStateMachine/tinyrelay/tinygit" |
| 14 | ) |
| 15 | |
| 16 | func signedAnnouncement(t *testing.T, secret string, tags ...[]string) tinygit.Event { |
| 17 | t.Helper() |
| 18 | e := tinygit.Event{Kind: 30617, CreatedAt: 100, Tags: append([][]string{{"d" , "demo" }}, tags...)} |
| 19 | if err := event.Sign(&e, secret); err != nil { |
| 20 | t.Fatal(err) |
| 21 | } |
| 22 | return e |
| 23 | } |
| 24 | |
| 25 | func postEvent(t *testing.T, s http.Handler, e tinygit.Event) *httptest.ResponseRecorder { |
| 26 | t.Helper() |
| 27 | body, err := json.Marshal(e) |
| 28 | if err != nil { |
| 29 | t.Fatal(err) |
| 30 | } |
| 31 | w := httptest.NewRecorder() |
| 32 | s.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/events" , bytes.NewReader(body))) |
| 33 | return w |
| 34 | } |
| 35 | |
| 36 | func TestStandaloneServerPersistsSignedAnnouncement(t *testing.T) { |
| 37 | e := signedAnnouncement(t, strings.Repeat("0" , 63)+"1" ) |
| 38 | cfg := tinygit.ServerConfig{DataDir: t.TempDir(), Owner: e.PubKey} |
| 39 | s, err := tinygit.OpenServer(context.Background(), cfg) |
| 40 | if err != nil { |
| 41 | t.Fatal(err) |
| 42 | } |
| 43 | w := postEvent(t, s, e) |
| 44 | if w.Code != http.StatusOK { |
| 45 | t.Fatalf("publish = %d %s" , w.Code, w.Body.String()) |
| 46 | } |
| 47 | // Retrying a signed event is safe even if the caller lost the first response. |
| 48 | if w := postEvent(t, s, e); w.Code != http.StatusOK { |
| 49 | t.Fatalf("retry = %d %s" , w.Code, w.Body.String()) |
| 50 | } |
| 51 | if err := s.Close(); err != nil { |
| 52 | t.Fatal(err) |
| 53 | } |
| 54 | s, err = tinygit.OpenServer(context.Background(), cfg) |
| 55 | if err != nil { |
| 56 | t.Fatal(err) |
| 57 | } |
| 58 | defer s.Close() |
| 59 | // Bech32 public key for the test's secret scalar 1. |
| 60 | npub := "npub10xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqpkge6d" |
| 61 | w = httptest.NewRecorder() |
| 62 | s.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/" +npub+"/demo.git/info/refs?service=git-upload-pack" , nil)) |
| 63 | if w.Code != http.StatusOK || !strings.Contains(w.Header().Get("Content-Type" ), "git-upload-pack-advertisement" ) { |
| 64 | t.Fatalf("reopened Git discovery = %d %s" , w.Code, w.Body.String()) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | func TestStandaloneServerRestrictsRepositoryAdmission(t *testing.T) { |
| 69 | ownerEvent := signedAnnouncement(t, strings.Repeat("0" , 63)+"1" ) |
| 70 | s, err := tinygit.OpenServer(context.Background(), tinygit.ServerConfig{DataDir: t.TempDir(), Owner: ownerEvent.PubKey}) |
| 71 | if err != nil { |
| 72 | t.Fatal(err) |
| 73 | } |
| 74 | defer s.Close() |
| 75 | for _, tc := range []struct { |
| 76 | name string |
| 77 | e tinygit.Event |
| 78 | }{ |
| 79 | {"foreign owner" , signedAnnouncement(t, strings.Repeat("0" , 63)+"2" )}, |
| 80 | {"private repository" , signedAnnouncement(t, strings.Repeat("0" , 63)+"1" , []string{"private" , "true" })}, |
| 81 | } { |
| 82 | t.Run(tc.name, func(t *testing.T) { |
| 83 | if w := postEvent(t, s, tc.e); w.Code != http.StatusForbidden { |
| 84 | t.Fatalf("admission = %d %s" , w.Code, w.Body.String()) |
| 85 | } |
| 86 | }) |
| 87 | } |
| 88 | if w := postEvent(t, s, ownerEvent); w.Code != http.StatusOK { |
| 89 | t.Fatalf("owner's public announcement = %d %s" , w.Code, w.Body.String()) |
| 90 | } |
| 91 | } |