tinyrelayfile
tinyrelay/internal/daemon/routes_acceptance_test.go/

internal/daemon/routes_acceptance_test.go raw
1package daemon
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "net/http"
8 "net/http/httptest"
9 "testing"
10)
11
12func mountedRequest(t *testing.T, app *App, method, path string, body []byte, accept string, signed bool) *httptest.ResponseRecorder {
13 req := httptest.NewRequest(method, "http://relay.test/r/main"+path, bytes.NewReader(body))
14 if accept != "" {
15 req.Header.Set("Accept", accept)
16 }
17 if signed {
18 signRequest(t, req, string(body))
19 }
20 res := httptest.NewRecorder()
21 app.ServeHTTP(res, req)
22 return res
23}
24
25func TestMountedRouteNIP11AndBridgeAcceptance(t *testing.T) {
26 app, _ := testTenant(t)
27 info := mountedRequest(t, app, http.MethodGet, "/", nil, "application/nostr+json", false)
28 if info.Code != http.StatusOK || info.Header().Get("Content-Type") != "application/nostr+json" {
29 t.Fatalf("NIP-11 status/content-type=%d/%q", info.Code, info.Header().Get("Content-Type"))
30 }
31 var document map[string]any
32 if err := json.Unmarshal(info.Body.Bytes(), &document); err != nil {
33 t.Fatal(err)
34 }
35 if _, ok := document["fuel"]; ok {
36 t.Fatal("NIP-11 exposes removed fuel capability")
37 }
38
39 query := []byte(`[{"kinds":[1]}]`)
40 unauth := mountedRequest(t, app, http.MethodPost, "/query", query, "", false)
41 if unauth.Code != http.StatusUnauthorized {
42 t.Fatalf("unsigned bridge query=%d", unauth.Code)
43 }
44 auth := mountedRequest(t, app, http.MethodPost, "/query", query, "", true)
45 if auth.Code != http.StatusOK {
46 t.Fatalf("signed bridge query=%d body=%s", auth.Code, auth.Body.String())
47 }
48 bad := mountedRequest(t, app, http.MethodPost, "/query", []byte(`[{`), "", true)
49 if bad.Code != http.StatusBadRequest {
50 t.Fatalf("malformed bridge query=%d", bad.Code)
51 }
52}
53
54func TestMountedRouteNIP86CORSAndPublicPages(t *testing.T) {
55 app, _ := testTenant(t)
56 body := []byte(`{"method":"listviews","params":[]}`)
57 unauthReq := httptest.NewRequest(http.MethodPost, "http://relay.test/r/main/manage", bytes.NewReader(body))
58 unauthReq.Header.Set("Content-Type", "application/nostr+json+rpc")
59 unauthRes := httptest.NewRecorder()
60 app.ServeHTTP(unauthRes, unauthReq)
61 unauth := unauthRes
62 if unauth.Code != http.StatusUnauthorized {
63 t.Fatalf("unsigned NIP-86=%d", unauth.Code)
64 }
65 authReq := httptest.NewRequest(http.MethodPost, "http://relay.test/r/main/manage", bytes.NewReader(body))
66 authReq.Header.Set("Content-Type", "application/nostr+json+rpc")
67 signRequest(t, authReq, string(body))
68 authRes := httptest.NewRecorder()
69 app.ServeHTTP(authRes, authReq)
70 auth := authRes
71 if auth.Code != http.StatusOK {
72 t.Fatalf("signed NIP-86=%d body=%s", auth.Code, auth.Body.String())
73 }
74 for _, path := range []string{"/", "/people", "/card.json", "/connect.json", "/terms", "/invite/missing"} {
75 res := mountedRequest(t, app, http.MethodGet, path, nil, "", path == "/people" || path == "/connect.json")
76 if res.Code != http.StatusOK && !(path == "/invite/missing" && res.Code == http.StatusNotFound) {
77 t.Fatalf("public route %s=%d body=%s", path, res.Code, res.Body.String())
78 }
79 }
80 preflight := mountedRequest(t, app, http.MethodOptions, "/query", nil, "", false)
81 if preflight.Code != http.StatusNoContent || preflight.Header().Get("Access-Control-Allow-Origin") != "*" {
82 t.Fatalf("CORS preflight=%d origin=%q", preflight.Code, preflight.Header().Get("Access-Control-Allow-Origin"))
83 }
84}
85
86func TestMountedRouteNIP05WebAddressAndDataAuth(t *testing.T) {
87 app, tenant := testTenant(t)
88 p := tenant.Policy()
89 p.Features.Names = true
90 if err := tenant.applyPolicy(context.Background(), p); err != nil {
91 t.Fatal(err)
92 }
93 unknown := mountedRequest(t, app, http.MethodGet, "/.well-known/nostr.json?name=missing", nil, "", false)
94 if unknown.Code != http.StatusOK {
95 t.Fatalf("unknown NIP-05=%d", unknown.Code)
96 }
97 var emptyNIP05 struct {
98 Names map[string]string `json:"names"`
99 }
100 if err := json.Unmarshal(unknown.Body.Bytes(), &emptyNIP05); err != nil || len(emptyNIP05.Names) != 0 {
101 t.Fatalf("unknown NIP-05 document=%s", unknown.Body.String())
102 }
103 web := mountedRequest(t, app, http.MethodGet, "/.well-known/nostr.json?path=/npub1missing", nil, "", false)
104 if web.Code != http.StatusUnauthorized {
105 t.Fatalf("unsigned NIP-AD=%d", web.Code)
106 }
107 importRes := mountedRequest(t, app, http.MethodPut, "/import", []byte("{}\n"), "", false)
108 if importRes.Code != http.StatusForbidden {
109 t.Fatalf("unsigned import=%d", importRes.Code)
110 }
111 backup := mountedRequest(t, app, http.MethodGet, "/backups/missing.json", nil, "", false)
112 if backup.Code != http.StatusUnauthorized && backup.Code != http.StatusForbidden && backup.Code != http.StatusNotFound {
113 t.Fatalf("backup visibility=%d", backup.Code)
114 }
115 if tenant.Policy().Features.Files {
116 // The dedicated NIP-96 test covers upload/delete; this assertion keeps
117 // the mounted route's disabled-file behavior explicit for this matrix.
118 p := tenant.Policy()
119 p.Features.Files = false
120 if err := tenant.applyPolicy(context.Background(), p); err != nil {
121 t.Fatal(err)
122 }
123 blob := mountedRequest(t, app, http.MethodGet, "/nip96/missing", nil, "", false)
124 if blob.Code != http.StatusNotFound {
125 t.Fatalf("disabled blob route=%d", blob.Code)
126 }
127 }
128}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs