summaryrefslogtreecommitdiffstats
path: root/filter_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'filter_test.go')
-rw-r--r--filter_test.go415
1 files changed, 415 insertions, 0 deletions
diff --git a/filter_test.go b/filter_test.go
new file mode 100644
index 0000000..ebe2b1d
--- /dev/null
+++ b/filter_test.go
@@ -0,0 +1,415 @@
1package nostr
2
3import (
4 "encoding/json"
5 "testing"
6)
7
8func TestFilterMarshalJSON(t *testing.T) {
9 since := int64(1704067200)
10 until := int64(1704153600)
11
12 filter := Filter{
13 IDs: []string{"abc123"},
14 Kinds: []int{1, 7},
15 Authors: []string{"def456"},
16 Tags: map[string][]string{
17 "e": {"event1", "event2"},
18 "p": {"pubkey1"},
19 },
20 Since: &since,
21 Until: &until,
22 Limit: 100,
23 }
24
25 data, err := filter.MarshalJSON()
26 if err != nil {
27 t.Fatalf("MarshalJSON() error = %v", err)
28 }
29
30 // Parse and check structure
31 var m map[string]interface{}
32 if err := json.Unmarshal(data, &m); err != nil {
33 t.Fatalf("Failed to unmarshal: %v", err)
34 }
35
36 // Check regular fields
37 if _, ok := m["ids"]; !ok {
38 t.Error("ids field missing")
39 }
40 if _, ok := m["kinds"]; !ok {
41 t.Error("kinds field missing")
42 }
43 if _, ok := m["authors"]; !ok {
44 t.Error("authors field missing")
45 }
46 if _, ok := m["since"]; !ok {
47 t.Error("since field missing")
48 }
49 if _, ok := m["until"]; !ok {
50 t.Error("until field missing")
51 }
52 if _, ok := m["limit"]; !ok {
53 t.Error("limit field missing")
54 }
55
56 // Check tag filters with # prefix
57 if _, ok := m["#e"]; !ok {
58 t.Error("#e field missing")
59 }
60 if _, ok := m["#p"]; !ok {
61 t.Error("#p field missing")
62 }
63}
64
65func TestFilterMarshalJSONOmitsEmpty(t *testing.T) {
66 filter := Filter{
67 Kinds: []int{1},
68 }
69
70 data, err := filter.MarshalJSON()
71 if err != nil {
72 t.Fatalf("MarshalJSON() error = %v", err)
73 }
74
75 var m map[string]interface{}
76 if err := json.Unmarshal(data, &m); err != nil {
77 t.Fatalf("Failed to unmarshal: %v", err)
78 }
79
80 if _, ok := m["ids"]; ok {
81 t.Error("empty ids should be omitted")
82 }
83 if _, ok := m["authors"]; ok {
84 t.Error("empty authors should be omitted")
85 }
86 if _, ok := m["since"]; ok {
87 t.Error("nil since should be omitted")
88 }
89 if _, ok := m["until"]; ok {
90 t.Error("nil until should be omitted")
91 }
92 if _, ok := m["limit"]; ok {
93 t.Error("zero limit should be omitted")
94 }
95}
96
97func TestFilterUnmarshalJSON(t *testing.T) {
98 jsonData := `{
99 "ids": ["abc123"],
100 "kinds": [1, 7],
101 "authors": ["def456"],
102 "#e": ["event1", "event2"],
103 "#p": ["pubkey1"],
104 "since": 1704067200,
105 "until": 1704153600,
106 "limit": 100
107 }`
108
109 var filter Filter
110 if err := json.Unmarshal([]byte(jsonData), &filter); err != nil {
111 t.Fatalf("UnmarshalJSON() error = %v", err)
112 }
113
114 if len(filter.IDs) != 1 || filter.IDs[0] != "abc123" {
115 t.Errorf("IDs = %v, want [abc123]", filter.IDs)
116 }
117 if len(filter.Kinds) != 2 {
118 t.Errorf("Kinds length = %d, want 2", len(filter.Kinds))
119 }
120 if len(filter.Authors) != 1 || filter.Authors[0] != "def456" {
121 t.Errorf("Authors = %v, want [def456]", filter.Authors)
122 }
123 if filter.Since == nil || *filter.Since != 1704067200 {
124 t.Errorf("Since = %v, want 1704067200", filter.Since)
125 }
126 if filter.Until == nil || *filter.Until != 1704153600 {
127 t.Errorf("Until = %v, want 1704153600", filter.Until)
128 }
129 if filter.Limit != 100 {
130 t.Errorf("Limit = %d, want 100", filter.Limit)
131 }
132
133 // Check tag filters
134 if len(filter.Tags["e"]) != 2 {
135 t.Errorf("Tags[e] length = %d, want 2", len(filter.Tags["e"]))
136 }
137 if len(filter.Tags["p"]) != 1 {
138 t.Errorf("Tags[p] length = %d, want 1", len(filter.Tags["p"]))
139 }
140}
141
142func TestFilterMatchesIDs(t *testing.T) {
143 filter := Filter{
144 IDs: []string{"abc", "def456"},
145 }
146
147 tests := []struct {
148 id string
149 want bool
150 }{
151 {"abc123", true}, // matches prefix "abc"
152 {"abcdef", true}, // matches prefix "abc"
153 {"def456", true}, // exact match
154 {"def456xyz", true}, // matches prefix "def456"
155 {"xyz789", false}, // no match
156 {"ab", false}, // "ab" doesn't start with "abc"
157 }
158
159 for _, tt := range tests {
160 event := &Event{ID: tt.id}
161 if got := filter.Matches(event); got != tt.want {
162 t.Errorf("Matches() with ID %s = %v, want %v", tt.id, got, tt.want)
163 }
164 }
165}
166
167func TestFilterMatchesAuthors(t *testing.T) {
168 filter := Filter{
169 Authors: []string{"pubkey1", "pubkey2"},
170 }
171
172 tests := []struct {
173 pubkey string
174 want bool
175 }{
176 {"pubkey1", true},
177 {"pubkey1abc", true}, // Prefix match
178 {"pubkey2", true},
179 {"pubkey3", false},
180 }
181
182 for _, tt := range tests {
183 event := &Event{PubKey: tt.pubkey}
184 if got := filter.Matches(event); got != tt.want {
185 t.Errorf("Matches() with PubKey %s = %v, want %v", tt.pubkey, got, tt.want)
186 }
187 }
188}
189
190func TestFilterMatchesKinds(t *testing.T) {
191 filter := Filter{
192 Kinds: []int{1, 7},
193 }
194
195 tests := []struct {
196 kind int
197 want bool
198 }{
199 {1, true},
200 {7, true},
201 {0, false},
202 {4, false},
203 }
204
205 for _, tt := range tests {
206 event := &Event{Kind: tt.kind}
207 if got := filter.Matches(event); got != tt.want {
208 t.Errorf("Matches() with Kind %d = %v, want %v", tt.kind, got, tt.want)
209 }
210 }
211}
212
213func TestFilterMatchesSince(t *testing.T) {
214 since := int64(1704067200)
215 filter := Filter{
216 Since: &since,
217 }
218
219 tests := []struct {
220 createdAt int64
221 want bool
222 }{
223 {1704067200, true}, // Equal
224 {1704067201, true}, // After
225 {1704067199, false}, // Before
226 }
227
228 for _, tt := range tests {
229 event := &Event{CreatedAt: tt.createdAt}
230 if got := filter.Matches(event); got != tt.want {
231 t.Errorf("Matches() with CreatedAt %d = %v, want %v", tt.createdAt, got, tt.want)
232 }
233 }
234}
235
236func TestFilterMatchesUntil(t *testing.T) {
237 until := int64(1704067200)
238 filter := Filter{
239 Until: &until,
240 }
241
242 tests := []struct {
243 createdAt int64
244 want bool
245 }{
246 {1704067200, true}, // Equal
247 {1704067199, true}, // Before
248 {1704067201, false}, // After
249 }
250
251 for _, tt := range tests {
252 event := &Event{CreatedAt: tt.createdAt}
253 if got := filter.Matches(event); got != tt.want {
254 t.Errorf("Matches() with CreatedAt %d = %v, want %v", tt.createdAt, got, tt.want)
255 }
256 }
257}
258
259func TestFilterMatchesTags(t *testing.T) {
260 filter := Filter{
261 Tags: map[string][]string{
262 "e": {"event1"},
263 "p": {"pubkey1", "pubkey2"},
264 },
265 }
266
267 tests := []struct {
268 name string
269 tags Tags
270 want bool
271 }{
272 {
273 name: "matches all",
274 tags: Tags{{"e", "event1"}, {"p", "pubkey1"}},
275 want: true,
276 },
277 {
278 name: "matches with different p",
279 tags: Tags{{"e", "event1"}, {"p", "pubkey2"}},
280 want: true,
281 },
282 {
283 name: "missing e tag",
284 tags: Tags{{"p", "pubkey1"}},
285 want: false,
286 },
287 {
288 name: "wrong e value",
289 tags: Tags{{"e", "event2"}, {"p", "pubkey1"}},
290 want: false,
291 },
292 {
293 name: "extra tags ok",
294 tags: Tags{{"e", "event1"}, {"p", "pubkey1"}, {"t", "test"}},
295 want: true,
296 },
297 }
298
299 for _, tt := range tests {
300 t.Run(tt.name, func(t *testing.T) {
301 event := &Event{Tags: tt.tags}
302 if got := filter.Matches(event); got != tt.want {
303 t.Errorf("Matches() = %v, want %v", got, tt.want)
304 }
305 })
306 }
307}
308
309func TestFilterMatchesEmpty(t *testing.T) {
310 // Empty filter matches everything
311 filter := Filter{}
312 event := &Event{
313 ID: "abc123",
314 PubKey: "pubkey1",
315 CreatedAt: 1704067200,
316 Kind: 1,
317 Tags: Tags{{"e", "event1"}},
318 Content: "test",
319 }
320
321 if !filter.Matches(event) {
322 t.Error("Empty filter should match all events")
323 }
324}
325
326func TestFilterClone(t *testing.T) {
327 since := int64(1704067200)
328 until := int64(1704153600)
329
330 original := &Filter{
331 IDs: []string{"id1", "id2"},
332 Kinds: []int{1, 7},
333 Authors: []string{"author1"},
334 Tags: map[string][]string{
335 "e": {"event1"},
336 },
337 Since: &since,
338 Until: &until,
339 Limit: 100,
340 }
341
342 clone := original.Clone()
343
344 // Modify original
345 original.IDs[0] = "modified"
346 original.Kinds[0] = 999
347 original.Authors[0] = "modified"
348 original.Tags["e"][0] = "modified"
349 *original.Since = 0
350 *original.Until = 0
351 original.Limit = 0
352
353 // Clone should be unchanged
354 if clone.IDs[0] != "id1" {
355 t.Error("Clone IDs was modified")
356 }
357 if clone.Kinds[0] != 1 {
358 t.Error("Clone Kinds was modified")
359 }
360 if clone.Authors[0] != "author1" {
361 t.Error("Clone Authors was modified")
362 }
363 if clone.Tags["e"][0] != "event1" {
364 t.Error("Clone Tags was modified")
365 }
366 if *clone.Since != 1704067200 {
367 t.Error("Clone Since was modified")
368 }
369 if *clone.Until != 1704153600 {
370 t.Error("Clone Until was modified")
371 }
372 if clone.Limit != 100 {
373 t.Error("Clone Limit was modified")
374 }
375}
376
377func TestFilterJSONRoundTrip(t *testing.T) {
378 since := int64(1704067200)
379 original := Filter{
380 IDs: []string{"abc123"},
381 Kinds: []int{1},
382 Authors: []string{"def456"},
383 Tags: map[string][]string{
384 "e": {"event1"},
385 },
386 Since: &since,
387 Limit: 50,
388 }
389
390 data, err := json.Marshal(original)
391 if err != nil {
392 t.Fatalf("Marshal error: %v", err)
393 }
394
395 var decoded Filter
396 if err := json.Unmarshal(data, &decoded); err != nil {
397 t.Fatalf("Unmarshal error: %v", err)
398 }
399
400 if len(decoded.IDs) != 1 || decoded.IDs[0] != "abc123" {
401 t.Errorf("IDs mismatch")
402 }
403 if len(decoded.Kinds) != 1 || decoded.Kinds[0] != 1 {
404 t.Errorf("Kinds mismatch")
405 }
406 if len(decoded.Tags["e"]) != 1 || decoded.Tags["e"][0] != "event1" {
407 t.Errorf("Tags mismatch")
408 }
409 if decoded.Since == nil || *decoded.Since != since {
410 t.Errorf("Since mismatch")
411 }
412 if decoded.Limit != 50 {
413 t.Errorf("Limit mismatch")
414 }
415}