1package tinygit_test23import (4 "bytes"5 "context"6 "encoding/json"7 "net/http"8 "net/http/httptest"9 "strings"10 "testing"1112 "github.com/FelineStateMachine/tinyrelay/internal/event"13 "github.com/FelineStateMachine/tinyrelay/tinygit"14)1516func 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 e23}2425func 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 w34}3536func 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}6768func 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 string77 e tinygit.Event78 }{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}