| 1 | package daemon |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "net/http" |
| 7 | "net/http/httptest" |
| 8 | "testing" |
| 9 | "time" |
| 10 | |
| 11 | "github.com/FelineStateMachine/tinyrelay/internal/policy" |
| 12 | ) |
| 13 | |
| 14 | func TestCapabilityRegistryReportsHonestOptionalFeatures(t *testing.T) { |
| 15 | monitor, err := NewPeerMonitor(PeerMonitorConfig{Peers: []string{"https://peer.example" }, Publish: false}) |
| 16 | if err != nil { |
| 17 | t.Fatal(err) |
| 18 | } |
| 19 | capabilities := CapabilityRegistryWithServices(policy.Policy{Features: policy.Features{Grasp: true}}, true, true, monitor) |
| 20 | got := map[string]Capability{} |
| 21 | for _, capability := range capabilities { |
| 22 | got[capability.ID] = capability |
| 23 | } |
| 24 | if got["NIP-34" ].Status != "enabled" || got["GRASP" ].Status != "enabled" { |
| 25 | t.Fatalf("Git capabilities = %#v" , got) |
| 26 | } |
| 27 | if got["BUD-01" ].Status != "enabled" || got["BUD-06" ].Status != "enabled" || got["NIP-66" ].Status != "configured" { |
| 28 | t.Fatalf("optional capabilities = %#v" , got) |
| 29 | } |
| 30 | if got["NIP-94" ].Status != "enabled" || got["NIP-96" ].Status != "enabled" { |
| 31 | t.Fatalf("file capabilities = %#v" , got) |
| 32 | } |
| 33 | if got["BUD-08" ].Status != "enabled" || got["BUD-13" ].Status != "enabled" { |
| 34 | t.Fatalf("file metadata and upload capabilities = %#v" , got) |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | func TestPublishPeerStatusStoresSignedDiscoveryEvent(t *testing.T) { |
| 39 | _, tenant := testTenant(t) |
| 40 | if err := tenant.PublishPeerStatus(context.Background(), []PeerStatus{{URL: "https://peer.example" , Up: true}}); err != nil { |
| 41 | t.Fatal(err) |
| 42 | } |
| 43 | var raw string |
| 44 | if err := tenant.store.DB().QueryRow(`SELECT raw FROM events WHERE kind=? ORDER BY seq DESC LIMIT 1` , 30166).Scan(&raw); err != nil { |
| 45 | t.Fatal(err) |
| 46 | } |
| 47 | var stored struct { |
| 48 | ID string `json:"id"` |
| 49 | PubKey string `json:"pubkey"` |
| 50 | Sig string `json:"sig"` |
| 51 | } |
| 52 | if err := json.Unmarshal([]byte(raw), &stored); err != nil { |
| 53 | t.Fatal(err) |
| 54 | } |
| 55 | if stored.ID == "" || stored.PubKey == "" || stored.Sig == "" { |
| 56 | t.Fatalf("discovery event is not signed: %#v" , stored) |
| 57 | } |
| 58 | if stored.PubKey != tenant.records.PublicKey() { |
| 59 | t.Fatalf("publisher = %q, relay identity = %q" , stored.PubKey, tenant.records.PublicKey()) |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | func TestCapabilityRegistryDisablesUnmountedBlobDoors(t *testing.T) { |
| 64 | for _, capability := range CapabilityRegistry(policy.Policy{}, false, nil) { |
| 65 | if len(capability.ID) > 4 && capability.ID[:4] == "BUD-" && capability.Status == "enabled" { |
| 66 | t.Fatalf("unmounted blob capability advertised: %#v" , capability) |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | func TestPeerMonitorProbesOnlyConfiguredPeers(t *testing.T) { |
| 72 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 73 | if r.Header.Get("Accept" ) != "application/nostr+json" { |
| 74 | t.Errorf("accept = %q" , r.Header.Get("Accept" )) |
| 75 | } |
| 76 | w.WriteHeader(http.StatusOK) |
| 77 | })) |
| 78 | defer server.Close() |
| 79 | var outcomes []string |
| 80 | monitor, err := NewPeerMonitor(PeerMonitorConfig{Peers: []string{server.URL}, Timeout: time.Second, Observe: func(outcome string, _ time.Duration) { outcomes = append(outcomes, outcome) }}) |
| 81 | if err != nil { |
| 82 | t.Fatal(err) |
| 83 | } |
| 84 | if err := monitor.Probe(context.Background()); err != nil { |
| 85 | t.Fatal(err) |
| 86 | } |
| 87 | peers := monitor.Peers() |
| 88 | if len(peers) != 1 || peers[0] != server.URL { |
| 89 | t.Fatalf("peers = %#v" , peers) |
| 90 | } |
| 91 | snapshot := monitor.Snapshot() |
| 92 | if len(snapshot) != 1 || !snapshot[0].Up || snapshot[0].LastSuccess.IsZero() || len(outcomes) != 1 || outcomes[0] != "ok" { |
| 93 | t.Fatalf("snapshot = %#v outcomes = %#v" , snapshot, outcomes) |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | func TestPeerMonitorRejectsUnboundedConfiguration(t *testing.T) { |
| 98 | if _, err := NewPeerMonitor(PeerMonitorConfig{Peers: []string{"wss://public.example" , "not a URL" }}); err == nil { |
| 99 | t.Fatal("invalid peer configuration accepted" ) |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | func TestPeerMonitorPublicationRequiresPublisher(t *testing.T) { |
| 104 | withoutPublisher, err := NewPeerMonitor(PeerMonitorConfig{Peers: []string{"https://peer.example" }, Publish: true}) |
| 105 | if err != nil { |
| 106 | t.Fatal(err) |
| 107 | } |
| 108 | if withoutPublisher.PublicationEnabled() { |
| 109 | t.Fatal("publication marked enabled without publisher" ) |
| 110 | } |
| 111 | withPublisher, err := NewPeerMonitor(PeerMonitorConfig{Peers: []string{"https://peer.example" }, Publish: true, PublishFunc: func(context.Context, []PeerStatus) error { return nil }}) |
| 112 | if err != nil { |
| 113 | t.Fatal(err) |
| 114 | } |
| 115 | if !withPublisher.PublicationEnabled() { |
| 116 | t.Fatal("configured publisher not recognized" ) |
| 117 | } |
| 118 | } |