directory_test.go raw
1 package spider
2
3 import (
4 "context"
5 "testing"
6 "time"
7
8 "next.orly.dev/pkg/nostr/encoders/event"
9 "next.orly.dev/pkg/nostr/encoders/kind"
10 "next.orly.dev/pkg/nostr/encoders/tag"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 )
14
15 func TestExtractRelaysFromEvents(t *testing.T) {
16 ds := &DirectorySpider{}
17
18 tests := []struct {
19 name string
20 events []*event.E
21 expected []string
22 }{
23 {
24 name: "empty events",
25 events: []*event.E{},
26 expected: []string{},
27 },
28 {
29 name: "single event with relays",
30 events: []*event.E{
31 {
32 Kind: kind.RelayListMetadata.K,
33 Tags: &tag.S{
34 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay1.example.com")),
35 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay2.example.com")),
36 },
37 },
38 },
39 expected: []string{"wss://relay1.example.com", "wss://relay2.example.com"},
40 },
41 {
42 name: "multiple events with duplicate relays",
43 events: []*event.E{
44 {
45 Kind: kind.RelayListMetadata.K,
46 Tags: &tag.S{
47 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay1.example.com")),
48 },
49 },
50 {
51 Kind: kind.RelayListMetadata.K,
52 Tags: &tag.S{
53 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay1.example.com")),
54 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay3.example.com")),
55 },
56 },
57 },
58 expected: []string{"wss://relay1.example.com", "wss://relay3.example.com"},
59 },
60 {
61 name: "event with empty r tags",
62 events: []*event.E{
63 {
64 Kind: kind.RelayListMetadata.K,
65 Tags: &tag.S{
66 tag.NewFromBytesSlice([]byte("r")), // empty value
67 tag.NewFromBytesSlice([]byte("r"), []byte("wss://valid.relay.com")),
68 },
69 },
70 },
71 expected: []string{"wss://valid.relay.com"},
72 },
73 {
74 name: "normalizes relay URLs",
75 events: []*event.E{
76 {
77 Kind: kind.RelayListMetadata.K,
78 Tags: &tag.S{
79 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay.example.com")),
80 tag.NewFromBytesSlice([]byte("r"), []byte("wss://relay.example.com/")), // duplicate with trailing slash
81 },
82 },
83 },
84 expected: []string{"wss://relay.example.com"},
85 },
86 }
87
88 for _, tt := range tests {
89 t.Run(tt.name, func(t *testing.T) {
90 result := ds.extractRelaysFromEvents(tt.events)
91
92 // For empty case, check length
93 if len(tt.expected) == 0 {
94 assert.Empty(t, result)
95 return
96 }
97
98 // Check that all expected relays are present (order may vary)
99 assert.Len(t, result, len(tt.expected))
100 for _, expected := range tt.expected {
101 assert.Contains(t, result, expected)
102 }
103 })
104 }
105 }
106
107 func TestDirectorySpiderLifecycle(t *testing.T) {
108 ctx, cancel := context.WithCancel(context.Background())
109 defer cancel()
110
111 // Create spider without database (will return error)
112 _, err := NewDirectorySpider(ctx, nil, nil, 0, 0)
113 require.Error(t, err)
114 assert.Contains(t, err.Error(), "database cannot be nil")
115 }
116
117 func TestDirectorySpiderDefaults(t *testing.T) {
118 // Test that defaults are applied correctly
119 assert.Equal(t, 24*time.Hour, DirectorySpiderDefaultInterval)
120 assert.Equal(t, 3, DirectorySpiderDefaultMaxHops)
121 assert.Equal(t, 30*time.Second, DirectorySpiderRelayTimeout)
122 assert.Equal(t, 60*time.Second, DirectorySpiderQueryTimeout)
123 assert.Equal(t, 500*time.Millisecond, DirectorySpiderRelayDelay)
124 assert.Equal(t, 5000, DirectorySpiderMaxEventsPerQuery)
125 }
126
127 func TestTriggerNow(t *testing.T) {
128 ctx, cancel := context.WithCancel(context.Background())
129 defer cancel()
130
131 ds := &DirectorySpider{
132 ctx: ctx,
133 triggerChan: make(chan struct{}, 1),
134 }
135
136 // First trigger should succeed
137 ds.TriggerNow()
138
139 // Verify trigger was sent
140 select {
141 case <-ds.triggerChan:
142 // Expected
143 default:
144 t.Error("trigger was not sent")
145 }
146
147 // Second trigger while channel is empty should also succeed
148 ds.TriggerNow()
149
150 // But if we trigger again without draining, it should not block
151 ds.TriggerNow() // Should not block due to select default case
152 }
153
154 func TestLastRun(t *testing.T) {
155 ds := &DirectorySpider{}
156
157 // Initially should be zero
158 assert.True(t, ds.LastRun().IsZero())
159
160 // Set a time
161 now := time.Now()
162 ds.lastRun = now
163
164 // Should return the set time
165 assert.Equal(t, now, ds.LastRun())
166 }
167