crawler_test.mx raw
1 package crawler
2
3 import (
4 "os"
5 "testing"
6
7 "git.smesh.lol/nostr/pkg/event"
8 "git.smesh.lol/nostr/pkg/kind"
9 "git.smesh.lol/nostr/pkg/signer/p8k"
10 "git.smesh.lol/nostr/pkg/tag"
11 "git.smesh.lol/morly/pkg/store"
12 )
13
14 // openTmp opens a store in a fresh directory. The caller owns both: defer
15 // os.RemoveAll(dir) and defer eng.Close(). A t.Cleanup closure would have to
16 // capture eng, which Moxie closures do not do.
17 func openTmp(t *testing.T) (p *store.Engine, dir string) {
18 t.Helper()
19 dir, derr := os.MkdirTemp("", "moxie-crawler")
20 if derr != nil {
21 t.Fatal(derr)
22 }
23 eng, err := store.Open(dir)
24 if err != nil {
25 t.Fatal(err)
26 }
27 return eng, dir
28 }
29
30 func makeRelayList(t *testing.T, s *p8k.Signer, relays ...string) (p *event.E) {
31 t.Helper()
32 kind.Ensure()
33 tags := tag.NewSWithCap(len(relays))
34 for _, r := range relays {
35 // tags is a *tag.S, whose element slice is T; pushing onto the struct
36 // itself is `push on non-slice type` (stage4 used to emit IR for it
37 // instead of the error, which read as an IR-emit bug).
38 tags.T = push(tags.T, tag.NewFromBytesSlice([]byte("r"), []byte(r)))
39 }
40 ev := &event.E{
41 CreatedAt: 1700000000,
42 Kind: kind.RelayListMetadata.K,
43 Tags: tags,
44 }
45 if err := ev.Sign(s); err != nil {
46 t.Fatal(err)
47 }
48 return ev
49 }
50
51 func TestDiscoverRelays(t *testing.T) {
52 eng, dir := openTmp(t)
53 defer os.RemoveAll(dir)
54 defer eng.Close()
55 s := p8k.MustNew()
56 if err := s.Generate(); err != nil {
57 t.Fatal(err)
58 }
59
60 ev := makeRelayList(t, s, "wss://relay.example.com", "wss://another.relay")
61 if err := eng.SaveEvent(ev); err != nil {
62 t.Fatal(err)
63 }
64
65 c := New(eng)
66 relays := c.Discover()
67 if len(relays) != 2 {
68 t.Fatalf("expected 2 relays, got %d", len(relays))
69 }
70
71 urls := map[string]bool{}
72 for _, r := range relays {
73 urls[r.URL] = true
74 // No hint means both read+write
75 if !r.Read || !r.Write {
76 t.Errorf("relay %s should be read+write", r.URL)
77 }
78 }
79 if !urls["wss://relay.example.com"] || !urls["wss://another.relay"] {
80 t.Error("missing expected relay URLs")
81 }
82 }
83
84 func TestDiscoverDedup(t *testing.T) {
85 eng, dir := openTmp(t)
86 defer os.RemoveAll(dir)
87 defer eng.Close()
88
89 s1 := p8k.MustNew()
90 if err := s1.Generate(); err != nil {
91 t.Fatal(err)
92 }
93 s2 := p8k.MustNew()
94 if err := s2.Generate(); err != nil {
95 t.Fatal(err)
96 }
97
98 // Two users list the same relay
99 if err := eng.SaveEvent(makeRelayList(t, s1, "wss://shared.relay")); err != nil {
100 t.Fatal(err)
101 }
102 if err := eng.SaveEvent(makeRelayList(t, s2, "wss://shared.relay")); err != nil {
103 t.Fatal(err)
104 }
105
106 c := New(eng)
107 relays := c.Discover()
108 if len(relays) != 1 {
109 t.Fatalf("expected 1 deduplicated relay, got %d", len(relays))
110 }
111 }
112
113 func TestDiscoverURLs(t *testing.T) {
114 eng, dir := openTmp(t)
115 defer os.RemoveAll(dir)
116 defer eng.Close()
117 s := p8k.MustNew()
118 if err := s.Generate(); err != nil {
119 t.Fatal(err)
120 }
121
122 if err := eng.SaveEvent(makeRelayList(t, s, "wss://relay.one")); err != nil {
123 t.Fatal(err)
124 }
125
126 c := New(eng)
127 urls := c.DiscoverURLs()
128 if len(urls) != 1 || urls[0] != "wss://relay.one" {
129 t.Fatalf("expected [wss://relay.one], got %v", urls)
130 }
131 }
132