Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108package deliberi
import ( "context" "fmt" "net/http" "net/http/httptest" "testing" "time")
const collaboratorInvitesByBody = `{"items":[{"uri":"at://did:plc:scallop/sh.tangled.repo.collaboratorInvite/did:plc:limpet","knot":"did:web:knot.oyster.cafe","repo":"did:plc:scallop","addedBy":"did:plc:boltless","createdAt":"2026-06-01T00:00:00.000000Z"}],"pending":[],"truncated":false}`
func serving(t *testing.T, path, body string) *bobbinClient { t.Helper() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != path { t.Errorf("path = %q, want %q", r.URL.Path, path) http.NotFound(w, r) return } if r.URL.Query().Get("subject") == "" { t.Error("subject param missing; bobbin requires it") } w.Header().Set("Content-Type", "application/json") fmt.Fprint(w, body) })) t.Cleanup(srv.Close) return newBobbinClient(srv.URL)}
func TestListCollaboratorInvitesByDecodesBobbinsBody(t *testing.T) { c := serving(t, "/xrpc/"+repoListCollaboratorInvitesByNSID, collaboratorInvitesByBody)
answer, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet") if err != nil { t.Fatalf("ListCollaboratorInvitesBy: %v", err) } want := InviteOffer{ Uri: "at://did:plc:scallop/sh.tangled.repo.collaboratorInvite/did:plc:limpet", KnotDid: "did:web:knot.oyster.cafe", RepoDid: "did:plc:scallop", AddedBy: "did:plc:boltless", CreatedAt: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC), } if len(answer.Offers) != 1 || answer.Offers[0] != want { t.Fatalf("offers = %+v, want one %+v", answer.Offers, want) }}
func TestPartialAnswerListsWhatBobbinCouldntRead(t *testing.T) { const body = `{"items":[],"pending":["did:web:cold.knot","did:web:other.knot"],"truncated":true}` c := serving(t, "/xrpc/"+repoListCollaboratorInvitesByNSID, body)
answer, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet") if err != nil { t.Fatalf("ListCollaboratorInvitesBy: %v", err) } if len(answer.Pending) != 2 { t.Fatalf("pending = %v, want both knots", answer.Pending) } if !answer.Truncated { t.Fatal("truncated dropped in decode, so we'd read cut list as whole offer set") }}
func TestUnreadableCreatedAtFailsListing(t *testing.T) { const body = `{"items":[{"uri":"at://did:plc:scallop/sh.tangled.repo.collaboratorInvite/did:plc:limpet","knot":"did:web:knot.example","repo":"did:plc:scallop","addedBy":"did:plc:akshay","createdAt":"whenever"}]}` c := serving(t, "/xrpc/"+repoListCollaboratorInvitesByNSID, body)
if _, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet"); err == nil { t.Fatal("we can't stamp rows from timestamps that won't read") }}
func TestOfferMissingItsKnotFailsListing(t *testing.T) { const body = `{"items":[{"uri":"at://did:plc:scallop/sh.tangled.repo.collaboratorInvite/did:plc:limpet","repo":"did:plc:scallop","addedBy":"did:plc:akshay","createdAt":"2026-06-01T00:00:00.000000Z"}],"pending":[],"truncated":false}` c := serving(t, "/xrpc/"+repoListCollaboratorInvitesByNSID, body)
if _, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet"); err == nil { t.Fatal("offers missing their knot can't be checked against pending, so accepting one would make its absence look like withdrawal") }}
func TestOfferMissingItsRepoFailsListing(t *testing.T) { const body = `{"items":[{"uri":"at://did:plc:scallop/sh.tangled.repo.collaboratorInvite/did:plc:limpet","knot":"did:web:knot.example","addedBy":"did:plc:akshay","createdAt":"2026-06-01T00:00:00.000000Z"}],"pending":[],"truncated":false}` c := serving(t, "/xrpc/"+repoListCollaboratorInvitesByNSID, body)
if _, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet"); err == nil { t.Fatal("a repo-less offer has nothing to link or accept against, so the row would be unusable") }}
func TestWarming503FailsListing(t *testing.T) { const warming = `{"error":"Warming","message":"invite index is still warming, so it can't list offers yet"}` srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) fmt.Fprint(w, warming) })) t.Cleanup(srv.Close)
c := newBobbinClient(srv.URL) if _, err := c.ListCollaboratorInvitesBy(context.Background(), "did:plc:limpet"); err == nil { t.Fatal("503 decoded as empty listing, so reconcile would read every offer as withdrawn") }}