This repository has no description
3.1 kB
99 lines
1package repoverify
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11
12 "github.com/bluesky-social/indigo/atproto/identity"
13 "tangled.org/core/api/tangled"
14 "tangled.org/core/idresolver"
15 "tangled.org/core/repoident"
16)
17
18const (
19 testRepoDid = repoident.RepoDid("did:plc:limpet")
20 testOwnerDid = "did:plc:akshay"
21)
22
23func describeRepoServer(t *testing.T, describedRepoDid repoident.RepoDid) *httptest.Server {
24 t.Helper()
25 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26 if r.URL.Path != "/xrpc/"+tangled.RepoDescribeRepoNSID {
27 http.NotFound(w, r)
28 return
29 }
30 w.Header().Set("Content-Type", "application/json")
31 _ = json.NewEncoder(w).Encode(tangled.RepoDescribeRepo_Output{
32 RepoDid: describedRepoDid.String(),
33 OwnerDid: testOwnerDid,
34 Rkey: "3kkkkkkkkkkkk",
35 })
36 }))
37 t.Cleanup(srv.Close)
38 return srv
39}
40
41func verifyKnot(t *testing.T, knotURL string, dev bool) (Result, error) {
42 t.Helper()
43 resolver := idresolver.NewMockResolver(idresolver.MockDirectory{Ident: &identity.Identity{
44 Services: map[string]identity.ServiceEndpoint{
45 repoident.KnotServiceID: {Type: repoident.KnotServiceType, URL: knotURL},
46 },
47 }})
48 return New(resolver, dev)(context.Background(), testRepoDid)
49}
50
51func TestNew_DevModeAcceptsHttpKnotEndpointAndStripsThePath(t *testing.T) {
52 srv := describeRepoServer(t, testRepoDid)
53
54 result, err := verifyKnot(t, srv.URL+"/repo/m5326fp3qemiriiqypxv6rrhai", true)
55 if err != nil {
56 t.Fatalf("dev mode should accept an http knot endpoint: %v", err)
57 }
58 if result.OwnerDid.String() != testOwnerDid {
59 t.Errorf("OwnerDid = %q, want %q", result.OwnerDid, testOwnerDid)
60 }
61 if result.KnotURL.String() != srv.URL {
62 t.Errorf("KnotURL = %q, want %q", result.KnotURL, srv.URL)
63 }
64}
65
66func TestNew_Rejections(t *testing.T) {
67 target := describeRepoServer(t, testRepoDid)
68 otherRepo := describeRepoServer(t, "did:plc:anemone")
69 redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
70 http.Redirect(w, r, target.URL+r.URL.Path, http.StatusFound)
71 }))
72 t.Cleanup(redirector.Close)
73
74 cases := map[string]struct {
75 knotURL string
76 dev bool
77 want string
78 sentinel error
79 }{
80 "http endpoint outside dev mode": {target.URL, false, "must use https", nil},
81 "knot redirects elsewhere": {redirector.URL, true, "describeRepo on", nil},
82 "identity declares no knot": {"", true, "", repoident.ErrNoKnotService},
83 "describeRepo answers for another repo": {otherRepo.URL, true, `repoDid "did:plc:anemone"`, ErrKnotAnswer},
84 }
85 for name, tc := range cases {
86 t.Run(name, func(t *testing.T) {
87 _, err := verifyKnot(t, tc.knotURL, tc.dev)
88 if err == nil {
89 t.Fatalf("verify accepted a knot it should reject")
90 }
91 if !strings.Contains(err.Error(), tc.want) {
92 t.Errorf("error = %v, want one mentioning %q", err, tc.want)
93 }
94 if tc.sentinel != nil && !errors.Is(err, tc.sentinel) {
95 t.Errorf("error = %v, want one matching %v", err, tc.sentinel)
96 }
97 })
98 }
99}