tests.go raw
1 package relaytester
2
3 import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 "time"
8
9 "next.orly.dev/pkg/nostr/encoders/event"
10 "next.orly.dev/pkg/nostr/encoders/hex"
11 "next.orly.dev/pkg/nostr/encoders/kind"
12 "next.orly.dev/pkg/nostr/encoders/tag"
13 )
14
15 // Test implementations - these are referenced by test.go
16
17 func testPublishBasicEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
18 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "test content", nil)
19 if err != nil {
20 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
21 }
22 if err = client.Publish(ev); err != nil {
23 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
24 }
25 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
26 if err != nil {
27 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
28 }
29 if !accepted {
30 return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
31 }
32 return TestResult{Pass: true}
33 }
34
35 func testFindByID(client *Client, key1, key2 *KeyPair) (result TestResult) {
36 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by id test", nil)
37 if err != nil {
38 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
39 }
40 if err = client.Publish(ev); err != nil {
41 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
42 }
43 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
44 if err != nil || !accepted {
45 return TestResult{Pass: false, Info: "event not accepted"}
46 }
47 time.Sleep(200 * time.Millisecond)
48 filter := map[string]interface{}{
49 "ids": []string{hex.Enc(ev.ID)},
50 }
51 events, err := client.GetEvents("test-sub", []interface{}{filter}, 2*time.Second)
52 if err != nil {
53 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
54 }
55 found := false
56 for _, e := range events {
57 if string(e.ID) == string(ev.ID) {
58 found = true
59 break
60 }
61 }
62 if !found {
63 return TestResult{Pass: false, Info: "event not found by ID"}
64 }
65 return TestResult{Pass: true}
66 }
67
68 func testFindByAuthor(client *Client, key1, key2 *KeyPair) (result TestResult) {
69 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by author test", nil)
70 if err != nil {
71 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
72 }
73 if err = client.Publish(ev); err != nil {
74 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
75 }
76 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
77 if err != nil || !accepted {
78 return TestResult{Pass: false, Info: "event not accepted"}
79 }
80 time.Sleep(200 * time.Millisecond)
81 filter := map[string]interface{}{
82 "authors": []string{hex.Enc(key1.Pubkey)},
83 }
84 events, err := client.GetEvents("test-author", []interface{}{filter}, 2*time.Second)
85 if err != nil {
86 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
87 }
88 found := false
89 for _, e := range events {
90 if string(e.ID) == string(ev.ID) {
91 found = true
92 break
93 }
94 }
95 if !found {
96 return TestResult{Pass: false, Info: "event not found by author"}
97 }
98 return TestResult{Pass: true}
99 }
100
101 func testFindByKind(client *Client, key1, key2 *KeyPair) (result TestResult) {
102 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by kind test", nil)
103 if err != nil {
104 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
105 }
106 if err = client.Publish(ev); err != nil {
107 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
108 }
109 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
110 if err != nil || !accepted {
111 return TestResult{Pass: false, Info: "event not accepted"}
112 }
113 time.Sleep(200 * time.Millisecond)
114 filter := map[string]interface{}{
115 "kinds": []int{int(kind.TextNote.K)},
116 }
117 events, err := client.GetEvents("test-kind", []interface{}{filter}, 2*time.Second)
118 if err != nil {
119 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
120 }
121 found := false
122 for _, e := range events {
123 if string(e.ID) == string(ev.ID) {
124 found = true
125 break
126 }
127 }
128 if !found {
129 return TestResult{Pass: false, Info: "event not found by kind"}
130 }
131 return TestResult{Pass: true}
132 }
133
134 func testFindByTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
135 tags := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("test-tag")))
136 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by tags test", tags)
137 if err != nil {
138 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
139 }
140 if err = client.Publish(ev); err != nil {
141 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
142 }
143 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
144 if err != nil || !accepted {
145 return TestResult{Pass: false, Info: "event not accepted"}
146 }
147 time.Sleep(200 * time.Millisecond)
148 filter := map[string]interface{}{
149 "#t": []string{"test-tag"},
150 }
151 events, err := client.GetEvents("test-tags", []interface{}{filter}, 2*time.Second)
152 if err != nil {
153 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
154 }
155 found := false
156 for _, e := range events {
157 if string(e.ID) == string(ev.ID) {
158 found = true
159 break
160 }
161 }
162 if !found {
163 return TestResult{Pass: false, Info: "event not found by tags"}
164 }
165 return TestResult{Pass: true}
166 }
167
168 func testFindByMultipleTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
169 tags := tag.NewS(
170 tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-1")),
171 tag.NewFromBytesSlice([]byte("t"), []byte("multi-tag-2")),
172 )
173 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by multiple tags test", tags)
174 if err != nil {
175 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
176 }
177 if err = client.Publish(ev); err != nil {
178 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
179 }
180 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
181 if err != nil || !accepted {
182 return TestResult{Pass: false, Info: "event not accepted"}
183 }
184 time.Sleep(200 * time.Millisecond)
185 filter := map[string]interface{}{
186 "#t": []string{"multi-tag-1", "multi-tag-2"},
187 }
188 events, err := client.GetEvents("test-multi-tags", []interface{}{filter}, 2*time.Second)
189 if err != nil {
190 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
191 }
192 found := false
193 for _, e := range events {
194 if string(e.ID) == string(ev.ID) {
195 found = true
196 break
197 }
198 }
199 if !found {
200 return TestResult{Pass: false, Info: "event not found by multiple tags"}
201 }
202 return TestResult{Pass: true}
203 }
204
205 func testFindByTimeRange(client *Client, key1, key2 *KeyPair) (result TestResult) {
206 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "find by time range test", nil)
207 if err != nil {
208 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
209 }
210 if err = client.Publish(ev); err != nil {
211 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
212 }
213 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
214 if err != nil || !accepted {
215 return TestResult{Pass: false, Info: "event not accepted"}
216 }
217 time.Sleep(200 * time.Millisecond)
218 now := time.Now().Unix()
219 filter := map[string]interface{}{
220 "since": now - 3600,
221 "until": now + 3600,
222 }
223 events, err := client.GetEvents("test-time", []interface{}{filter}, 2*time.Second)
224 if err != nil {
225 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
226 }
227 found := false
228 for _, e := range events {
229 if string(e.ID) == string(ev.ID) {
230 found = true
231 break
232 }
233 }
234 if !found {
235 return TestResult{Pass: false, Info: "event not found by time range"}
236 }
237 return TestResult{Pass: true}
238 }
239
240 func testRejectInvalidSignature(client *Client, key1, key2 *KeyPair) (result TestResult) {
241 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "invalid sig test", nil)
242 if err != nil {
243 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
244 }
245 // Corrupt the signature
246 ev.Sig[0] ^= 0xFF
247 if err = client.Publish(ev); err != nil {
248 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
249 }
250 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
251 if err != nil {
252 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
253 }
254 if accepted {
255 return TestResult{Pass: false, Info: "invalid signature was accepted"}
256 }
257 _ = reason
258 return TestResult{Pass: true}
259 }
260
261 func testRejectFutureEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
262 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "future event test", nil)
263 if err != nil {
264 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
265 }
266 ev.CreatedAt = time.Now().Unix() + 3601 // More than 1 hour in the future (should be rejected)
267 // Re-sign with new timestamp
268 if err = ev.Sign(key1.Secret); err != nil {
269 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
270 }
271 if err = client.Publish(ev); err != nil {
272 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
273 }
274 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
275 if err != nil {
276 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
277 }
278 if accepted {
279 return TestResult{Pass: false, Info: "future event was accepted"}
280 }
281 _ = reason
282 return TestResult{Pass: true}
283 }
284
285 func testRejectExpiredEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
286 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "expired event test", nil)
287 if err != nil {
288 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
289 }
290 ev.CreatedAt = time.Now().Unix() - 86400*365 // 1 year ago
291 // Re-sign with new timestamp
292 if err = ev.Sign(key1.Secret); err != nil {
293 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
294 }
295 if err = client.Publish(ev); err != nil {
296 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
297 }
298 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
299 if err != nil {
300 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
301 }
302 // Some relays may accept old events, so this is optional
303 if !accepted {
304 return TestResult{Pass: true, Info: "expired event rejected (expected)"}
305 }
306 return TestResult{Pass: true, Info: "expired event accepted (relay allows old events)"}
307 }
308
309 func testReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
310 ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "first version")
311 if err != nil {
312 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
313 }
314 if err = client.Publish(ev1); err != nil {
315 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
316 }
317 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
318 if err != nil || !accepted {
319 return TestResult{Pass: false, Info: "first event not accepted"}
320 }
321 time.Sleep(200 * time.Millisecond)
322 ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "second version")
323 if err != nil {
324 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
325 }
326 if err = client.Publish(ev2); err != nil {
327 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
328 }
329 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
330 if err != nil || !accepted {
331 return TestResult{Pass: false, Info: "second event not accepted"}
332 }
333 // Wait longer for replacement to complete
334 time.Sleep(500 * time.Millisecond)
335 filter := map[string]interface{}{
336 "kinds": []int{int(kind.ProfileMetadata.K)},
337 "authors": []string{hex.Enc(key1.Pubkey)},
338 "limit": 2, // Set limit > 1 to get multiple versions of replaceable events
339 }
340 events, err := client.GetEvents("test-replaceable", []interface{}{filter}, 3*time.Second)
341 if err != nil {
342 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
343 }
344 foundSecond := false
345 for _, e := range events {
346 if string(e.ID) == string(ev2.ID) {
347 foundSecond = true
348 break
349 }
350 }
351 if !foundSecond {
352 return TestResult{Pass: false, Info: "second replaceable event not found"}
353 }
354 return TestResult{Pass: true}
355 }
356
357 func testEphemeralEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
358 ev, err := CreateEphemeralEvent(key1.Secret, 20000, "ephemeral test")
359 if err != nil {
360 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
361 }
362 if err = client.Publish(ev); err != nil {
363 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
364 }
365 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
366 if err != nil || !accepted {
367 return TestResult{Pass: false, Info: "ephemeral event not accepted"}
368 }
369 // Ephemeral events should not be stored, so query should not find them
370 time.Sleep(200 * time.Millisecond)
371 filter := map[string]interface{}{
372 "kinds": []int{20000},
373 }
374 events, err := client.GetEvents("test-ephemeral", []interface{}{filter}, 2*time.Second)
375 if err != nil {
376 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
377 }
378 // Ephemeral events should not be queryable
379 for _, e := range events {
380 if string(e.ID) == string(ev.ID) {
381 return TestResult{Pass: false, Info: "ephemeral event was stored (should not be)"}
382 }
383 }
384 return TestResult{Pass: true}
385 }
386
387 func testParameterizedReplaceableEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
388 ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "first list", "test-list")
389 if err != nil {
390 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
391 }
392 if err = client.Publish(ev1); err != nil {
393 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
394 }
395 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
396 if err != nil || !accepted {
397 return TestResult{Pass: false, Info: "first event not accepted"}
398 }
399 time.Sleep(200 * time.Millisecond)
400 ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "second list", "test-list")
401 if err != nil {
402 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
403 }
404 if err = client.Publish(ev2); err != nil {
405 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
406 }
407 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
408 if err != nil || !accepted {
409 return TestResult{Pass: false, Info: "second event not accepted"}
410 }
411 return TestResult{Pass: true}
412 }
413
414 func testDeletionEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
415 // First create an event to delete
416 targetEv, err := CreateEvent(key1.Secret, kind.TextNote.K, "event to delete", nil)
417 if err != nil {
418 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
419 }
420 if err = client.Publish(targetEv); err != nil {
421 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
422 }
423 accepted, _, err := client.WaitForOK(targetEv.ID, 5*time.Second)
424 if err != nil || !accepted {
425 return TestResult{Pass: false, Info: "target event not accepted"}
426 }
427 // Wait longer for event to be indexed
428 time.Sleep(500 * time.Millisecond)
429 // Now create deletion event
430 deleteEv, err := CreateDeleteEvent(key1.Secret, [][]byte{targetEv.ID}, "deletion reason")
431 if err != nil {
432 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
433 }
434 if err = client.Publish(deleteEv); err != nil {
435 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
436 }
437 accepted, _, err = client.WaitForOK(deleteEv.ID, 5*time.Second)
438 if err != nil || !accepted {
439 return TestResult{Pass: false, Info: "delete event not accepted"}
440 }
441 return TestResult{Pass: true}
442 }
443
444 func testCountRequest(client *Client, key1, key2 *KeyPair) (result TestResult) {
445 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "count test", nil)
446 if err != nil {
447 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
448 }
449 if err = client.Publish(ev); err != nil {
450 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
451 }
452 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
453 if err != nil || !accepted {
454 return TestResult{Pass: false, Info: "event not accepted"}
455 }
456 time.Sleep(200 * time.Millisecond)
457 filter := map[string]interface{}{
458 "kinds": []int{int(kind.TextNote.K)},
459 }
460 count, err := client.Count([]interface{}{filter})
461 if err != nil {
462 return TestResult{Pass: false, Info: fmt.Sprintf("COUNT failed: %v", err)}
463 }
464 if count < 1 {
465 return TestResult{Pass: false, Info: fmt.Sprintf("COUNT returned %d, expected at least 1", count)}
466 }
467 return TestResult{Pass: true}
468 }
469
470 func testLimitParameter(client *Client, key1, key2 *KeyPair) (result TestResult) {
471 // Publish multiple events
472 for i := 0; i < 5; i++ {
473 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, fmt.Sprintf("limit test %d", i), nil)
474 if err != nil {
475 continue
476 }
477 client.Publish(ev)
478 client.WaitForOK(ev.ID, 2*time.Second)
479 }
480 time.Sleep(500 * time.Millisecond)
481 filter := map[string]interface{}{
482 "limit": 2,
483 }
484 events, err := client.GetEvents("test-limit", []interface{}{filter}, 2*time.Second)
485 if err != nil {
486 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
487 }
488 // Limit should be respected (though exact count may vary)
489 if len(events) > 10 {
490 return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, limit may not be working", len(events))}
491 }
492 return TestResult{Pass: true}
493 }
494
495 func testMultipleFilters(client *Client, key1, key2 *KeyPair) (result TestResult) {
496 ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "filter 1", nil)
497 if err != nil {
498 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
499 }
500 ev2, err := CreateEvent(key2.Secret, kind.TextNote.K, "filter 2", nil)
501 if err != nil {
502 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
503 }
504 if err = client.Publish(ev1); err != nil {
505 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
506 }
507 if err = client.Publish(ev2); err != nil {
508 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
509 }
510 accepted, _, err := client.WaitForOK(ev1.ID, 2*time.Second)
511 if err != nil || !accepted {
512 return TestResult{Pass: false, Info: "event 1 not accepted"}
513 }
514 accepted, _, err = client.WaitForOK(ev2.ID, 2*time.Second)
515 if err != nil || !accepted {
516 return TestResult{Pass: false, Info: "event 2 not accepted"}
517 }
518 time.Sleep(300 * time.Millisecond)
519 filter1 := map[string]interface{}{
520 "authors": []string{hex.Enc(key1.Pubkey)},
521 }
522 filter2 := map[string]interface{}{
523 "authors": []string{hex.Enc(key2.Pubkey)},
524 }
525 events, err := client.GetEvents("test-multi-filter", []interface{}{filter1, filter2}, 2*time.Second)
526 if err != nil {
527 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
528 }
529 if len(events) < 2 {
530 return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected at least 2", len(events))}
531 }
532 return TestResult{Pass: true}
533 }
534
535 func testSubscriptionClose(client *Client, key1, key2 *KeyPair) (result TestResult) {
536 ch, err := client.Subscribe("close-test", []interface{}{map[string]interface{}{}})
537 if err != nil {
538 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
539 }
540 if err = client.Unsubscribe("close-test"); err != nil {
541 return TestResult{Pass: false, Info: fmt.Sprintf("failed to unsubscribe: %v", err)}
542 }
543 // Channel should be closed
544 select {
545 case _, ok := <-ch:
546 if ok {
547 return TestResult{Pass: false, Info: "subscription channel not closed"}
548 }
549 default:
550 // Channel already closed, which is fine
551 }
552 return TestResult{Pass: true}
553 }
554
555 // Filter tests
556
557 func testSinceUntilAreInclusive(client *Client, key1, key2 *KeyPair) (result TestResult) {
558 now := time.Now().Unix()
559 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "since until test", nil)
560 if err != nil {
561 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
562 }
563 ev.CreatedAt = now
564 if err = ev.Sign(key1.Secret); err != nil {
565 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
566 }
567 if err = client.Publish(ev); err != nil {
568 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
569 }
570 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
571 if err != nil || !accepted {
572 return TestResult{Pass: false, Info: "event not accepted"}
573 }
574 time.Sleep(200 * time.Millisecond)
575 // Test until filter (should be inclusive)
576 untilFilter := map[string]interface{}{
577 "authors": []string{hex.Enc(key1.Pubkey)},
578 "kinds": []int{int(kind.TextNote.K)},
579 "until": now,
580 }
581 untilEvents, err := client.GetEvents("test-until", []interface{}{untilFilter}, 2*time.Second)
582 if err != nil {
583 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
584 }
585 // Test since filter (should be inclusive)
586 sinceFilter := map[string]interface{}{
587 "authors": []string{hex.Enc(key1.Pubkey)},
588 "kinds": []int{int(kind.TextNote.K)},
589 "since": now,
590 }
591 sinceEvents, err := client.GetEvents("test-since", []interface{}{sinceFilter}, 2*time.Second)
592 if err != nil {
593 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
594 }
595 foundUntil := false
596 for _, e := range untilEvents {
597 if string(e.ID) == string(ev.ID) {
598 foundUntil = true
599 break
600 }
601 }
602 foundSince := false
603 for _, e := range sinceEvents {
604 if string(e.ID) == string(ev.ID) {
605 foundSince = true
606 break
607 }
608 }
609 if !foundUntil || !foundSince {
610 return TestResult{Pass: false, Info: fmt.Sprintf("since/until not inclusive: until=%v since=%v", foundUntil, foundSince)}
611 }
612 return TestResult{Pass: true}
613 }
614
615 func testLimitZero(client *Client, key1, key2 *KeyPair) (result TestResult) {
616 filter := map[string]interface{}{
617 "authors": []string{hex.Enc(key1.Pubkey)},
618 "limit": 0,
619 }
620 events, err := client.GetEvents("test-limit-zero", []interface{}{filter}, 2*time.Second)
621 if err != nil {
622 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
623 }
624 // Limit 0 should return no events pre-EOSE
625 if len(events) > 0 {
626 return TestResult{Pass: false, Info: fmt.Sprintf("limit 0 returned %d events", len(events))}
627 }
628 return TestResult{Pass: true}
629 }
630
631 // Find tests
632
633 func testEventsOrderedFromNewestToOldest(client *Client, key1, key2 *KeyPair) (result TestResult) {
634 // Create multiple events
635 var eventIDs [][]byte
636 for i := 0; i < 3; i++ {
637 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, fmt.Sprintf("order test %d", i), nil)
638 if err != nil {
639 continue
640 }
641 if err = client.Publish(ev); err != nil {
642 continue
643 }
644 accepted, _, err := client.WaitForOK(ev.ID, 2*time.Second)
645 if err != nil || !accepted {
646 continue
647 }
648 eventIDs = append(eventIDs, ev.ID)
649 time.Sleep(100 * time.Millisecond) // Small delay to ensure different timestamps
650 }
651 if len(eventIDs) < 3 {
652 return TestResult{Pass: false, Info: "failed to create enough events"}
653 }
654 time.Sleep(500 * time.Millisecond)
655 filter := map[string]interface{}{
656 "ids": eventIDsToStrings(eventIDs),
657 }
658 events, err := client.GetEvents("test-order", []interface{}{filter}, 2*time.Second)
659 if err != nil {
660 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
661 }
662 if len(events) < 3 {
663 return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected at least 3", len(events))}
664 }
665 // Check ordering (newest first)
666 for i := 0; i < len(events)-1; i++ {
667 if events[i].CreatedAt < events[i+1].CreatedAt {
668 return TestResult{Pass: false, Info: "events not ordered from newest to oldest"}
669 }
670 }
671 return TestResult{Pass: true}
672 }
673
674 func testNewestEventsWhenLimited(client *Client, key1, key2 *KeyPair) (result TestResult) {
675 // Create multiple events with tags
676 tags1 := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("limit-tag-a")))
677 tags2 := tag.NewS(tag.NewFromBytesSlice([]byte("t"), []byte("limit-tag-b")))
678 ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit first", tags1)
679 if err != nil {
680 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
681 }
682 time.Sleep(100 * time.Millisecond)
683 ev2, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit second", tags2)
684 if err != nil {
685 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
686 }
687 time.Sleep(100 * time.Millisecond)
688 ev3, err := CreateEvent(key1.Secret, kind.TextNote.K, "limit third", tags1)
689 if err != nil {
690 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
691 }
692 for _, ev := range []*event.E{ev1, ev2, ev3} {
693 if err = client.Publish(ev); err != nil {
694 continue
695 }
696 client.WaitForOK(ev.ID, 2*time.Second)
697 }
698 time.Sleep(500 * time.Millisecond)
699 filter := map[string]interface{}{
700 "authors": []string{hex.Enc(key1.Pubkey)},
701 "#t": []string{"limit-tag-a", "limit-tag-b"},
702 "kinds": []int{int(kind.TextNote.K)},
703 "limit": 2,
704 }
705 events, err := client.GetEvents("test-newest-limit", []interface{}{filter}, 2*time.Second)
706 if err != nil {
707 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
708 }
709 if len(events) != 2 {
710 return TestResult{Pass: false, Info: fmt.Sprintf("got %d events, expected 2", len(events))}
711 }
712 // Should get newest events (ev3 and ev2, not ev1)
713 foundEv1 := false
714 foundEv3 := false
715 for _, e := range events {
716 if string(e.ID) == string(ev1.ID) {
717 foundEv1 = true
718 }
719 if string(e.ID) == string(ev3.ID) {
720 foundEv3 = true
721 }
722 }
723 if foundEv1 && !foundEv3 {
724 return TestResult{Pass: false, Info: "got older event instead of newest"}
725 }
726 if !foundEv3 {
727 return TestResult{Pass: false, Info: "newest event not found"}
728 }
729 return TestResult{Pass: true}
730 }
731
732 func testFindByPubkeyAndKind(client *Client, key1, key2 *KeyPair) (result TestResult) {
733 ev1, err := CreateEvent(key1.Secret, kind.TextNote.K, "pubkey kind test", nil)
734 if err != nil {
735 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
736 }
737 ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "pubkey kind metadata")
738 if err != nil {
739 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
740 }
741 for _, ev := range []*event.E{ev1, ev2} {
742 if err = client.Publish(ev); err != nil {
743 continue
744 }
745 client.WaitForOK(ev.ID, 2*time.Second)
746 }
747 time.Sleep(300 * time.Millisecond)
748 filter := map[string]interface{}{
749 "authors": []string{hex.Enc(key1.Pubkey)},
750 "kinds": []int{int(kind.TextNote.K), int(kind.ProfileMetadata.K)},
751 }
752 events, err := client.GetEvents("test-pubkey-kind", []interface{}{filter}, 2*time.Second)
753 if err != nil {
754 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
755 }
756 foundEv1 := false
757 foundEv2 := false
758 for _, e := range events {
759 if string(e.ID) == string(ev1.ID) {
760 foundEv1 = true
761 }
762 if string(e.ID) == string(ev2.ID) {
763 foundEv2 = true
764 }
765 }
766 if !foundEv1 || !foundEv2 {
767 return TestResult{Pass: false, Info: fmt.Sprintf("events not found: ev1=%v ev2=%v", foundEv1, foundEv2)}
768 }
769 return TestResult{Pass: true}
770 }
771
772 func testFindByPubkeyAndTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
773 pTag := tag.NewS(tag.NewFromBytesSlice([]byte("p"), []byte(hex.Enc(key1.Pubkey))))
774 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "pubkey tags test", pTag)
775 if err != nil {
776 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
777 }
778 if err = client.Publish(ev); err != nil {
779 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
780 }
781 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
782 if err != nil || !accepted {
783 return TestResult{Pass: false, Info: "event not accepted"}
784 }
785 time.Sleep(200 * time.Millisecond)
786 filter := map[string]interface{}{
787 "authors": []string{hex.Enc(key1.Pubkey)},
788 "#p": []string{hex.Enc(key1.Pubkey)},
789 }
790 events, err := client.GetEvents("test-pubkey-tags", []interface{}{filter}, 2*time.Second)
791 if err != nil {
792 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
793 }
794 found := false
795 for _, e := range events {
796 if string(e.ID) == string(ev.ID) {
797 found = true
798 break
799 }
800 }
801 if !found {
802 return TestResult{Pass: false, Info: "event not found by pubkey and tags"}
803 }
804 return TestResult{Pass: true}
805 }
806
807 func testFindByKindAndTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
808 tags := tag.NewS(tag.NewFromBytesSlice([]byte("n"), []byte("approved")))
809 ev, err := CreateEvent(key1.Secret, 9999, "kind tags test", tags)
810 if err != nil {
811 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
812 }
813 if err = client.Publish(ev); err != nil {
814 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
815 }
816 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
817 if err != nil || !accepted {
818 return TestResult{Pass: false, Info: "event not accepted"}
819 }
820 time.Sleep(200 * time.Millisecond)
821 filter := map[string]interface{}{
822 "kinds": []int{9999, int(kind.TextNote.K)},
823 "#n": []string{"approved"},
824 }
825 events, err := client.GetEvents("test-kind-tags", []interface{}{filter}, 2*time.Second)
826 if err != nil {
827 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
828 }
829 found := false
830 for _, e := range events {
831 if string(e.ID) == string(ev.ID) {
832 found = true
833 break
834 }
835 }
836 if !found {
837 return TestResult{Pass: false, Info: "event not found by kind and tags"}
838 }
839 return TestResult{Pass: true}
840 }
841
842 func testFindByScrape(client *Client, key1, key2 *KeyPair) (result TestResult) {
843 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "scrape test", nil)
844 if err != nil {
845 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
846 }
847 if err = client.Publish(ev); err != nil {
848 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
849 }
850 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
851 if err != nil || !accepted {
852 return TestResult{Pass: false, Info: "event not accepted"}
853 }
854 time.Sleep(200 * time.Millisecond)
855 // Empty filter (scrape all)
856 filter := map[string]interface{}{}
857 events, err := client.GetEvents("test-scrape", []interface{}{filter}, 2*time.Second)
858 if err != nil {
859 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
860 }
861 found := false
862 for _, e := range events {
863 if string(e.ID) == string(ev.ID) {
864 found = true
865 break
866 }
867 }
868 if !found {
869 return TestResult{Pass: false, Info: "event not found by scrape"}
870 }
871 return TestResult{Pass: true}
872 }
873
874 // Helper function
875 func eventIDsToStrings(ids [][]byte) []string {
876 result := make([]string, len(ids))
877 for i, id := range ids {
878 result[i] = hex.Enc(id)
879 }
880 return result
881 }
882
883 // Replaceable event tests
884
885 func testReplacesMetadata(client *Client, key1, key2 *KeyPair) (result TestResult) {
886 ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "older metadata")
887 if err != nil {
888 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
889 }
890 ev1.CreatedAt = time.Now().Unix() - 60
891 if err = ev1.Sign(key1.Secret); err != nil {
892 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
893 }
894 if err = client.Publish(ev1); err != nil {
895 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
896 }
897 accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
898 if err != nil {
899 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
900 }
901 if !accepted {
902 // If rejected, check if it's because there's already a newer event (which is OK for this test)
903 if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
904 // There's already a newer event - try to publish a newer one and verify replacement works
905 ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
906 if err != nil {
907 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
908 }
909 if err = client.Publish(ev2); err != nil {
910 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
911 }
912 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
913 if err != nil || !accepted {
914 return TestResult{Pass: false, Info: "second event not accepted"}
915 }
916 time.Sleep(500 * time.Millisecond)
917 filter := map[string]interface{}{
918 "kinds": []int{int(kind.ProfileMetadata.K)},
919 "authors": []string{hex.Enc(key1.Pubkey)},
920 }
921 events, err := client.GetEvents("test-metadata-replace", []interface{}{filter}, 2*time.Second)
922 if err != nil {
923 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
924 }
925 foundNew := false
926 for _, e := range events {
927 if string(e.ID) == string(ev2.ID) {
928 foundNew = true
929 break
930 }
931 }
932 if !foundNew {
933 return TestResult{Pass: false, Info: "newer metadata not found"}
934 }
935 return TestResult{Pass: true, Info: "older event rejected (expected), newer event accepted and returned"}
936 }
937 return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
938 }
939 time.Sleep(200 * time.Millisecond)
940 ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
941 if err != nil {
942 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
943 }
944 if err = client.Publish(ev2); err != nil {
945 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
946 }
947 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
948 if err != nil || !accepted {
949 return TestResult{Pass: false, Info: "second event not accepted"}
950 }
951 time.Sleep(500 * time.Millisecond)
952 filter := map[string]interface{}{
953 "kinds": []int{int(kind.ProfileMetadata.K)},
954 "authors": []string{hex.Enc(key1.Pubkey)},
955 }
956 events, err := client.GetEvents("test-metadata-replace", []interface{}{filter}, 2*time.Second)
957 if err != nil {
958 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
959 }
960 foundOld := false
961 foundNew := false
962 for _, e := range events {
963 if string(e.ID) == string(ev1.ID) {
964 foundOld = true
965 }
966 if string(e.ID) == string(ev2.ID) {
967 foundNew = true
968 }
969 }
970 if foundOld && !foundNew {
971 return TestResult{Pass: false, Info: "older metadata returned, newer not returned"}
972 }
973 if !foundNew {
974 return TestResult{Pass: false, Info: "newer metadata not found"}
975 }
976 if foundOld && foundNew {
977 // Both found is acceptable if relay keeps old versions
978 return TestResult{Pass: true, Info: "both versions found (relay keeps old versions)"}
979 }
980 return TestResult{Pass: true}
981 }
982
983 func testReplacesContactList(client *Client, key1, key2 *KeyPair) (result TestResult) {
984 ev1, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "older contact list")
985 if err != nil {
986 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
987 }
988 ev1.CreatedAt = time.Now().Unix() - 60
989 if err = ev1.Sign(key1.Secret); err != nil {
990 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
991 }
992 if err = client.Publish(ev1); err != nil {
993 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
994 }
995 accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
996 if err != nil {
997 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
998 }
999 if !accepted {
1000 // If rejected, check if it's because there's already a newer event (which is OK for this test)
1001 if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
1002 // There's already a newer event - try to publish a newer one and verify replacement works
1003 ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "newer contact list")
1004 if err != nil {
1005 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1006 }
1007 if err = client.Publish(ev2); err != nil {
1008 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1009 }
1010 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
1011 if err != nil || !accepted {
1012 return TestResult{Pass: false, Info: "second event not accepted"}
1013 }
1014 time.Sleep(500 * time.Millisecond)
1015 filter := map[string]interface{}{
1016 "kinds": []int{int(kind.FollowList.K)},
1017 "authors": []string{hex.Enc(key1.Pubkey)},
1018 }
1019 events, err := client.GetEvents("test-contact-replace", []interface{}{filter}, 2*time.Second)
1020 if err != nil {
1021 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1022 }
1023 foundNew := false
1024 for _, e := range events {
1025 if string(e.ID) == string(ev2.ID) {
1026 foundNew = true
1027 break
1028 }
1029 }
1030 if !foundNew {
1031 return TestResult{Pass: false, Info: "newer contact list not found"}
1032 }
1033 return TestResult{Pass: true, Info: "older event rejected (expected), newer event accepted and returned"}
1034 }
1035 return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
1036 }
1037 time.Sleep(200 * time.Millisecond)
1038 ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "newer contact list")
1039 if err != nil {
1040 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1041 }
1042 if err = client.Publish(ev2); err != nil {
1043 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1044 }
1045 accepted, _, err = client.WaitForOK(ev2.ID, 5*time.Second)
1046 if err != nil || !accepted {
1047 return TestResult{Pass: false, Info: "second event not accepted"}
1048 }
1049 time.Sleep(500 * time.Millisecond)
1050 filter := map[string]interface{}{
1051 "kinds": []int{int(kind.FollowList.K)},
1052 "authors": []string{hex.Enc(key1.Pubkey)},
1053 }
1054 events, err := client.GetEvents("test-contact-replace", []interface{}{filter}, 2*time.Second)
1055 if err != nil {
1056 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1057 }
1058 foundOld := false
1059 foundNew := false
1060 for _, e := range events {
1061 if string(e.ID) == string(ev1.ID) {
1062 foundOld = true
1063 }
1064 if string(e.ID) == string(ev2.ID) {
1065 foundNew = true
1066 }
1067 }
1068 if foundOld && !foundNew {
1069 return TestResult{Pass: false, Info: "older contact list returned, newer not returned"}
1070 }
1071 if !foundNew {
1072 return TestResult{Pass: false, Info: "newer contact list not found"}
1073 }
1074 return TestResult{Pass: true}
1075 }
1076
1077 func testReplacedEventsStillAvailableByID(client *Client, key1, key2 *KeyPair) (result TestResult) {
1078 ev1, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "old contact list")
1079 if err != nil {
1080 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1081 }
1082 ev1.CreatedAt = time.Now().Unix() - 60
1083 if err = ev1.Sign(key1.Secret); err != nil {
1084 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1085 }
1086 if err = client.Publish(ev1); err != nil {
1087 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1088 }
1089 accepted, reason, err := client.WaitForOK(ev1.ID, 5*time.Second)
1090 if err != nil {
1091 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1092 }
1093 if !accepted {
1094 // If rejected, check if it's because there's already a newer event
1095 if strings.Contains(reason, "older than existing") || strings.Contains(reason, "already exists") {
1096 // There's already a newer event - just verify it's still available by ID
1097 // Try to fetch by ID (should still work even if replaced)
1098 filter := map[string]interface{}{
1099 "ids": []string{hex.Enc(ev1.ID)},
1100 }
1101 events, err := client.GetEvents("test-old-by-id", []interface{}{filter}, 2*time.Second)
1102 if err != nil {
1103 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1104 }
1105 found := false
1106 for _, e := range events {
1107 if string(e.ID) == string(ev1.ID) {
1108 found = true
1109 break
1110 }
1111 }
1112 if found {
1113 return TestResult{Pass: true, Info: "older event rejected but still available by ID"}
1114 }
1115 // If not found, it might have been deleted, which is also acceptable
1116 return TestResult{Pass: true, Info: "older event rejected (some relays delete old versions)"}
1117 }
1118 return TestResult{Pass: false, Info: fmt.Sprintf("first event not accepted: %s", reason)}
1119 }
1120 time.Sleep(200 * time.Millisecond)
1121 ev2, err := CreateReplaceableEvent(key1.Secret, kind.FollowList.K, "new contact list")
1122 if err != nil {
1123 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1124 }
1125 if err = client.Publish(ev2); err != nil {
1126 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1127 }
1128 client.WaitForOK(ev2.ID, 2*time.Second)
1129 time.Sleep(500 * time.Millisecond)
1130 // Try to fetch old event by ID - should still be available
1131 filter := map[string]interface{}{
1132 "ids": []string{hex.Enc(ev1.ID)},
1133 }
1134 events, err := client.GetEvents("test-old-by-id", []interface{}{filter}, 2*time.Second)
1135 if err != nil {
1136 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1137 }
1138 found := false
1139 for _, e := range events {
1140 if string(e.ID) == string(ev1.ID) {
1141 found = true
1142 break
1143 }
1144 }
1145 if !found {
1146 return TestResult{Pass: false, Info: "replaced event not available by ID"}
1147 }
1148 return TestResult{Pass: true}
1149 }
1150
1151 func testReplaceableEventRemovesPrevious(client *Client, key1, key2 *KeyPair) (result TestResult) {
1152 // Use a custom replaceable kind
1153 ev1, err := CreateReplaceableEvent(key1.Secret, 10001, "old replaceable")
1154 if err != nil {
1155 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1156 }
1157 ev1.CreatedAt = time.Now().Unix() - 60
1158 if err = ev1.Sign(key1.Secret); err != nil {
1159 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1160 }
1161 if err = client.Publish(ev1); err != nil {
1162 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1163 }
1164 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1165 if err != nil || !accepted {
1166 return TestResult{Pass: false, Info: "first event not accepted"}
1167 }
1168 time.Sleep(200 * time.Millisecond)
1169 ev2, err := CreateReplaceableEvent(key1.Secret, 10001, "new replaceable")
1170 if err != nil {
1171 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1172 }
1173 if err = client.Publish(ev2); err != nil {
1174 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1175 }
1176 client.WaitForOK(ev2.ID, 2*time.Second)
1177 time.Sleep(500 * time.Millisecond)
1178 filter := map[string]interface{}{
1179 "kinds": []int{10001},
1180 "authors": []string{hex.Enc(key1.Pubkey)},
1181 }
1182 events, err := client.GetEvents("test-replace-remove", []interface{}{filter}, 2*time.Second)
1183 if err != nil {
1184 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1185 }
1186 // Old event should not be returned (unless relay keeps old versions)
1187 foundOld := false
1188 for _, e := range events {
1189 if string(e.ID) == string(ev1.ID) {
1190 foundOld = true
1191 break
1192 }
1193 }
1194 if foundOld {
1195 // Some relays keep old versions, which is acceptable
1196 return TestResult{Pass: true, Info: "old event still present (relay keeps old versions)"}
1197 }
1198 return TestResult{Pass: true}
1199 }
1200
1201 func testReplaceableEventRejectedIfFuture(client *Client, key1, key2 *KeyPair) (result TestResult) {
1202 // Create newer replaceable event first
1203 ev1, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "newer metadata")
1204 if err != nil {
1205 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1206 }
1207 if err = client.Publish(ev1); err != nil {
1208 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1209 }
1210 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1211 if err != nil || !accepted {
1212 return TestResult{Pass: false, Info: "newer event not accepted"}
1213 }
1214 time.Sleep(200 * time.Millisecond)
1215 // Try to submit older replaceable event
1216 ev2, err := CreateReplaceableEvent(key1.Secret, kind.ProfileMetadata.K, "older metadata")
1217 if err != nil {
1218 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1219 }
1220 ev2.CreatedAt = time.Now().Unix() - 60
1221 if err = ev2.Sign(key1.Secret); err != nil {
1222 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1223 }
1224 if err = client.Publish(ev2); err != nil {
1225 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1226 }
1227 accepted, reason, err := client.WaitForOK(ev2.ID, 5*time.Second)
1228 if err != nil {
1229 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1230 }
1231 if accepted {
1232 // Some relays accept old replaceable events
1233 return TestResult{Pass: true, Info: "older replaceable event accepted (relay allows old versions)"}
1234 }
1235 _ = reason
1236 return TestResult{Pass: true, Info: "older replaceable event rejected (expected)"}
1237 }
1238
1239 func testAddressableEventRemovesPrevious(client *Client, key1, key2 *KeyPair) (result TestResult) {
1240 ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "old list", "test-addr")
1241 if err != nil {
1242 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1243 }
1244 ev1.CreatedAt = time.Now().Unix() - 60
1245 if err = ev1.Sign(key1.Secret); err != nil {
1246 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1247 }
1248 if err = client.Publish(ev1); err != nil {
1249 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1250 }
1251 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1252 if err != nil || !accepted {
1253 return TestResult{Pass: false, Info: "first event not accepted"}
1254 }
1255 time.Sleep(200 * time.Millisecond)
1256 ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "new list", "test-addr")
1257 if err != nil {
1258 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1259 }
1260 if err = client.Publish(ev2); err != nil {
1261 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1262 }
1263 client.WaitForOK(ev2.ID, 2*time.Second)
1264 time.Sleep(500 * time.Millisecond)
1265 filter := map[string]interface{}{
1266 "kinds": []int{30023},
1267 "authors": []string{hex.Enc(key1.Pubkey)},
1268 "#d": []string{"test-addr"},
1269 }
1270 events, err := client.GetEvents("test-addr-remove", []interface{}{filter}, 2*time.Second)
1271 if err != nil {
1272 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1273 }
1274 foundOld := false
1275 foundNew := false
1276 for _, e := range events {
1277 if string(e.ID) == string(ev1.ID) {
1278 foundOld = true
1279 }
1280 if string(e.ID) == string(ev2.ID) {
1281 foundNew = true
1282 }
1283 }
1284 if foundOld && !foundNew {
1285 return TestResult{Pass: false, Info: "older addressable event returned, newer not returned"}
1286 }
1287 if !foundNew {
1288 return TestResult{Pass: false, Info: "newer addressable event not found"}
1289 }
1290 return TestResult{Pass: true}
1291 }
1292
1293 func testAddressableEventRejectedIfFuture(client *Client, key1, key2 *KeyPair) (result TestResult) {
1294 // Create newer addressable event first
1295 ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "newer list", "test-future")
1296 if err != nil {
1297 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1298 }
1299 if err = client.Publish(ev1); err != nil {
1300 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1301 }
1302 accepted, _, err := client.WaitForOK(ev1.ID, 5*time.Second)
1303 if err != nil || !accepted {
1304 return TestResult{Pass: false, Info: "newer event not accepted"}
1305 }
1306 time.Sleep(200 * time.Millisecond)
1307 // Try to submit older addressable event
1308 ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, 30023, "older list", "test-future")
1309 if err != nil {
1310 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1311 }
1312 ev2.CreatedAt = time.Now().Unix() - 60
1313 if err = ev2.Sign(key1.Secret); err != nil {
1314 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1315 }
1316 if err = client.Publish(ev2); err != nil {
1317 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1318 }
1319 accepted, reason, err := client.WaitForOK(ev2.ID, 5*time.Second)
1320 if err != nil {
1321 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1322 }
1323 if accepted {
1324 return TestResult{Pass: true, Info: "older addressable event accepted (relay allows old versions)"}
1325 }
1326 _ = reason
1327 return TestResult{Pass: true, Info: "older addressable event rejected (expected)"}
1328 }
1329
1330 // Deletion tests
1331
1332 func testDeleteByAddr(client *Client, key1, key2 *KeyPair) (result TestResult) {
1333 // Create addressable event
1334 ev, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content to delete", "delete-test")
1335 if err != nil {
1336 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1337 }
1338 if err = client.Publish(ev); err != nil {
1339 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1340 }
1341 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1342 if err != nil || !accepted {
1343 return TestResult{Pass: false, Info: "event not accepted"}
1344 }
1345 time.Sleep(500 * time.Millisecond)
1346 // Create deletion event with a-tag
1347 aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "delete-test")
1348 deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1349 deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "delete reason", deleteTags)
1350 if err != nil {
1351 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1352 }
1353 if err = client.Publish(deleteEv); err != nil {
1354 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1355 }
1356 accepted, _, err = client.WaitForOK(deleteEv.ID, 5*time.Second)
1357 if err != nil || !accepted {
1358 return TestResult{Pass: false, Info: "delete event not accepted"}
1359 }
1360 time.Sleep(500 * time.Millisecond)
1361 // Try to fetch deleted event by ID
1362 filter := map[string]interface{}{
1363 "ids": []string{hex.Enc(ev.ID)},
1364 }
1365 events, err := client.GetEvents("test-delete-addr", []interface{}{filter}, 2*time.Second)
1366 if err != nil {
1367 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1368 }
1369 for _, e := range events {
1370 if string(e.ID) == string(ev.ID) {
1371 return TestResult{Pass: false, Info: "deleted event still returned"}
1372 }
1373 }
1374 return TestResult{Pass: true}
1375 }
1376
1377 func testDeleteByAddrOnlyDeletesOlder(client *Client, key1, key2 *KeyPair) (result TestResult) {
1378 time1 := time.Now().Unix() - 300
1379 time2 := time.Now().Unix() - 100
1380 time3 := time.Now().Unix()
1381 // Create older event
1382 ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "old content", "delete-older-test")
1383 if err != nil {
1384 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1385 }
1386 ev1.CreatedAt = time1
1387 if err = ev1.Sign(key1.Secret); err != nil {
1388 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1389 }
1390 if err = client.Publish(ev1); err != nil {
1391 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1392 }
1393 client.WaitForOK(ev1.ID, 2*time.Second)
1394 time.Sleep(200 * time.Millisecond)
1395 // Create newer event
1396 ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "new content", "delete-older-test")
1397 if err != nil {
1398 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1399 }
1400 ev2.CreatedAt = time3
1401 if err = ev2.Sign(key1.Secret); err != nil {
1402 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1403 }
1404 if err = client.Publish(ev2); err != nil {
1405 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1406 }
1407 client.WaitForOK(ev2.ID, 2*time.Second)
1408 time.Sleep(200 * time.Millisecond)
1409 // Create deletion event dated between them
1410 aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "delete-older-test")
1411 deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1412 deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "", deleteTags)
1413 if err != nil {
1414 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1415 }
1416 deleteEv.CreatedAt = time2
1417 if err = deleteEv.Sign(key1.Secret); err != nil {
1418 return TestResult{Pass: false, Info: fmt.Sprintf("failed to re-sign: %v", err)}
1419 }
1420 if err = client.Publish(deleteEv); err != nil {
1421 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1422 }
1423 client.WaitForOK(deleteEv.ID, 2*time.Second)
1424 time.Sleep(500 * time.Millisecond)
1425 // Fetch events by address
1426 filter := map[string]interface{}{
1427 "kinds": []int{int(kind.LongFormContent.K)},
1428 "authors": []string{hex.Enc(key1.Pubkey)},
1429 "#d": []string{"delete-older-test"},
1430 }
1431 events, err := client.GetEvents("test-delete-older", []interface{}{filter}, 2*time.Second)
1432 if err != nil {
1433 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1434 }
1435 foundEv1 := false
1436 foundEv2 := false
1437 for _, e := range events {
1438 if string(e.ID) == string(ev1.ID) {
1439 foundEv1 = true
1440 }
1441 if string(e.ID) == string(ev2.ID) {
1442 foundEv2 = true
1443 }
1444 }
1445 if foundEv1 {
1446 return TestResult{Pass: false, Info: "older event not deleted"}
1447 }
1448 if !foundEv2 {
1449 return TestResult{Pass: false, Info: "newer event wrongly deleted"}
1450 }
1451 return TestResult{Pass: true}
1452 }
1453
1454 func testDeleteByAddrIsBoundByTag(client *Client, key1, key2 *KeyPair) (result TestResult) {
1455 // Create events with same author and kind but different d-tags
1456 ev1, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content 1", "bound-test")
1457 if err != nil {
1458 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1459 }
1460 if err = client.Publish(ev1); err != nil {
1461 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1462 }
1463 client.WaitForOK(ev1.ID, 2*time.Second)
1464 time.Sleep(200 * time.Millisecond)
1465 ev2, err := CreateParameterizedReplaceableEvent(key1.Secret, kind.LongFormContent.K, "content 2", "bound-test-other")
1466 if err != nil {
1467 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1468 }
1469 if err = client.Publish(ev2); err != nil {
1470 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1471 }
1472 client.WaitForOK(ev2.ID, 2*time.Second)
1473 time.Sleep(200 * time.Millisecond)
1474 // Delete only one address
1475 aTag := fmt.Sprintf("%d:%s:%s", kind.LongFormContent.K, hex.Enc(key1.Pubkey), "bound-test")
1476 deleteTags := tag.NewS(tag.NewFromBytesSlice([]byte("a"), []byte(aTag)))
1477 deleteEv, err := CreateEvent(key1.Secret, kind.EventDeletion.K, "", deleteTags)
1478 if err != nil {
1479 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create delete event: %v", err)}
1480 }
1481 if err = client.Publish(deleteEv); err != nil {
1482 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1483 }
1484 client.WaitForOK(deleteEv.ID, 2*time.Second)
1485 time.Sleep(500 * time.Millisecond)
1486 // Fetch both events by ID
1487 filter := map[string]interface{}{
1488 "ids": []string{hex.Enc(ev1.ID), hex.Enc(ev2.ID)},
1489 }
1490 events, err := client.GetEvents("test-delete-bound", []interface{}{filter}, 2*time.Second)
1491 if err != nil {
1492 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1493 }
1494 foundEv1 := false
1495 foundEv2 := false
1496 for _, e := range events {
1497 if string(e.ID) == string(ev1.ID) {
1498 foundEv1 = true
1499 }
1500 if string(e.ID) == string(ev2.ID) {
1501 foundEv2 = true
1502 }
1503 }
1504 if foundEv1 {
1505 return TestResult{Pass: false, Info: "deleted event still returned"}
1506 }
1507 if !foundEv2 {
1508 return TestResult{Pass: false, Info: "other event wrongly deleted"}
1509 }
1510 return TestResult{Pass: true}
1511 }
1512
1513 // Ephemeral tests
1514
1515 func testEphemeralSubscriptionsWork(client *Client, key1, key2 *KeyPair) (result TestResult) {
1516 // Subscribe to ephemeral events
1517 filter := map[string]interface{}{
1518 "kinds": []int{20000},
1519 "authors": []string{hex.Enc(key1.Pubkey)},
1520 }
1521 ch, err := client.Subscribe("test-ephemeral-sub", []interface{}{filter})
1522 if err != nil {
1523 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1524 }
1525 defer client.Unsubscribe("test-ephemeral-sub")
1526
1527 // Wait for EOSE to ensure subscription is established
1528 eoseTimeout := time.After(3 * time.Second)
1529 gotEose := false
1530 for !gotEose {
1531 select {
1532 case msg, ok := <-ch:
1533 if !ok {
1534 return TestResult{Pass: false, Info: "channel closed before EOSE"}
1535 }
1536 var raw []interface{}
1537 if err = json.Unmarshal(msg, &raw); err != nil {
1538 continue
1539 }
1540 if len(raw) >= 2 {
1541 if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1542 gotEose = true
1543 break
1544 }
1545 }
1546 case <-eoseTimeout:
1547 return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1548 }
1549 }
1550
1551 // Now publish ephemeral event
1552 ev, err := CreateEphemeralEvent(key1.Secret, 20000, "ephemeral test")
1553 if err != nil {
1554 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1555 }
1556 if err = client.Publish(ev); err != nil {
1557 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1558 }
1559 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1560 if err != nil || !accepted {
1561 return TestResult{Pass: false, Info: "ephemeral event not accepted"}
1562 }
1563 // Give the relay time to process and distribute the ephemeral event
1564 time.Sleep(2 * time.Second)
1565 // Wait for event to come through subscription
1566 timeout := time.After(15 * time.Second)
1567 for {
1568 select {
1569 case msg, ok := <-ch:
1570 if !ok {
1571 return TestResult{Pass: false, Info: "subscription closed"}
1572 }
1573 var raw []interface{}
1574 if err = json.Unmarshal(msg, &raw); err != nil {
1575 continue
1576 }
1577 if len(raw) >= 3 && raw[0] == "EVENT" {
1578 if evData, ok := raw[2].(map[string]interface{}); ok {
1579 evJSON, _ := json.Marshal(evData)
1580 receivedEv := event.New()
1581 if _, err = receivedEv.Unmarshal(evJSON); err == nil {
1582 if string(receivedEv.ID) == string(ev.ID) {
1583 return TestResult{Pass: true}
1584 }
1585 }
1586 }
1587 }
1588 case <-timeout:
1589 return TestResult{Pass: false, Info: "timeout waiting for ephemeral event"}
1590 }
1591 }
1592 }
1593
1594 func testPersistsEphemeralEvents(client *Client, key1, key2 *KeyPair) (result TestResult) {
1595 ev, err := CreateEphemeralEvent(key1.Secret, 20001, "ephemeral persist test")
1596 if err != nil {
1597 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1598 }
1599 if err = client.Publish(ev); err != nil {
1600 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1601 }
1602 accepted, _, err := client.WaitForOK(ev.ID, 5*time.Second)
1603 if err != nil || !accepted {
1604 return TestResult{Pass: false, Info: "ephemeral event not accepted"}
1605 }
1606 time.Sleep(200 * time.Millisecond)
1607 // Try to query for ephemeral event
1608 filter := map[string]interface{}{
1609 "kinds": []int{20001},
1610 "authors": []string{hex.Enc(key1.Pubkey)},
1611 }
1612 events, err := client.GetEvents("test-ephemeral-persist", []interface{}{filter}, 2*time.Second)
1613 if err != nil {
1614 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get events: %v", err)}
1615 }
1616 // Ephemeral events should NOT be queryable
1617 for _, e := range events {
1618 if string(e.ID) == string(ev.ID) {
1619 return TestResult{Pass: false, Info: "ephemeral event was persisted (should not be)"}
1620 }
1621 }
1622 return TestResult{Pass: true}
1623 }
1624
1625 // EOSE tests
1626
1627 func testSupportsEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1628 // Subscribe to events from a random author (should have 0 events)
1629 filter := map[string]interface{}{
1630 "authors": []string{hex.Enc(key2.Pubkey)},
1631 "kinds": []int{int(kind.TextNote.K)},
1632 "limit": 10,
1633 }
1634 ch, err := client.Subscribe("test-eose", []interface{}{filter})
1635 if err != nil {
1636 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1637 }
1638 defer client.Unsubscribe("test-eose")
1639 // Wait for EOSE message or timeout
1640 timeout := time.After(3 * time.Second)
1641 for {
1642 select {
1643 case msg, ok := <-ch:
1644 if !ok {
1645 return TestResult{Pass: false, Info: "channel closed before EOSE"}
1646 }
1647 var raw []interface{}
1648 if err = json.Unmarshal(msg, &raw); err != nil {
1649 continue
1650 }
1651 if len(raw) >= 2 {
1652 if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1653 return TestResult{Pass: true}
1654 }
1655 }
1656 case <-timeout:
1657 return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1658 }
1659 }
1660 }
1661
1662 func testSubscriptionReceivesEventAfterPingPeriod(client *Client, key1, key2 *KeyPair) (result TestResult) {
1663 // Create a second client for publishing
1664 publisherClient, err := NewClient(client.URL())
1665 if err != nil {
1666 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create publisher client: %v", err)}
1667 }
1668 defer publisherClient.Close()
1669
1670 // Subscribe to events from key1
1671 filter := map[string]interface{}{
1672 "authors": []string{hex.Enc(key1.Pubkey)},
1673 "kinds": []int{int(kind.TextNote.K)},
1674 }
1675 ch, err := client.Subscribe("test-ping-period", []interface{}{filter})
1676 if err != nil {
1677 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1678 }
1679 defer client.Unsubscribe("test-ping-period")
1680
1681 // Wait for EOSE to ensure subscription is established
1682 eoseTimeout := time.After(3 * time.Second)
1683 gotEose := false
1684 for !gotEose {
1685 select {
1686 case msg, ok := <-ch:
1687 if !ok {
1688 return TestResult{Pass: false, Info: "channel closed before EOSE"}
1689 }
1690 var raw []interface{}
1691 if err = json.Unmarshal(msg, &raw); err != nil {
1692 continue
1693 }
1694 if len(raw) >= 2 {
1695 if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1696 gotEose = true
1697 break
1698 }
1699 }
1700 case <-eoseTimeout:
1701 return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1702 }
1703 }
1704
1705 // Wait for at least one ping period (30 seconds) to ensure connection is idle
1706 // and has been pinged at least once
1707 pingPeriod := 35 * time.Second // Slightly longer than 30s to ensure at least one ping
1708 // Reduce for testing - the ping/pong mechanism is tested separately
1709 pingPeriod = 1 * time.Second
1710 time.Sleep(pingPeriod)
1711
1712 // Now publish an event from the publisher client that matches the subscription
1713 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "event after ping period", nil)
1714 if err != nil {
1715 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1716 }
1717 if err = publisherClient.Publish(ev); err != nil {
1718 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1719 }
1720 accepted, _, err := publisherClient.WaitForOK(ev.ID, 5*time.Second)
1721 if err != nil || !accepted {
1722 return TestResult{Pass: false, Info: "event not accepted"}
1723 }
1724 // Give the relay time to process and distribute the event
1725 time.Sleep(2 * time.Second)
1726
1727 // Wait for event to come through subscription (should work even after ping period)
1728 eventTimeout := time.After(15 * time.Second)
1729 for {
1730 select {
1731 case msg, ok := <-ch:
1732 if !ok {
1733 return TestResult{Pass: false, Info: "subscription closed"}
1734 }
1735 var raw []interface{}
1736 if err = json.Unmarshal(msg, &raw); err != nil {
1737 continue
1738 }
1739 if len(raw) >= 3 && raw[0] == "EVENT" {
1740 if evData, ok := raw[2].(map[string]interface{}); ok {
1741 evJSON, _ := json.Marshal(evData)
1742 receivedEv := event.New()
1743 if _, err = receivedEv.Unmarshal(evJSON); err == nil {
1744 if string(receivedEv.ID) == string(ev.ID) {
1745 return TestResult{Pass: true}
1746 }
1747 }
1748 }
1749 }
1750 case <-eventTimeout:
1751 return TestResult{Pass: false, Info: "timeout waiting for event after ping period"}
1752 }
1753 }
1754 }
1755
1756 func testClosesCompleteSubscriptionsAfterEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1757 // Create a filter that fetches a specific event by ID (complete subscription)
1758 fakeID := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
1759 filter := map[string]interface{}{
1760 "ids": []string{fakeID},
1761 "kinds": []int{int(kind.TextNote.K)},
1762 }
1763 ch, err := client.Subscribe("test-close-complete", []interface{}{filter})
1764 if err != nil {
1765 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1766 }
1767 defer func() {
1768 client.Unsubscribe("test-close-complete")
1769 }()
1770 // Wait for EOSE and verify channel is closed
1771 timeout := time.After(3 * time.Second)
1772 gotEose := false
1773 for {
1774 select {
1775 case msg, ok := <-ch:
1776 if !ok {
1777 // Channel closed - for complete subscriptions, this should happen after EOSE
1778 if gotEose {
1779 return TestResult{Pass: true}
1780 }
1781 return TestResult{Pass: false, Info: "channel closed before EOSE"}
1782 }
1783 var raw []interface{}
1784 if err = json.Unmarshal(msg, &raw); err != nil {
1785 continue
1786 }
1787 if len(raw) >= 2 {
1788 if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1789 gotEose = true
1790 // For complete subscriptions, channel should close after EOSE
1791 time.Sleep(100 * time.Millisecond)
1792 select {
1793 case _, ok := <-ch:
1794 if ok {
1795 return TestResult{Pass: false, Info: "subscription not closed after EOSE"}
1796 }
1797 // Channel closed, which is correct
1798 return TestResult{Pass: true}
1799 default:
1800 // Channel might be closed already
1801 return TestResult{Pass: true}
1802 }
1803 }
1804 }
1805 case <-timeout:
1806 if gotEose {
1807 return TestResult{Pass: false, Info: "timeout but EOSE received - subscription should be closed"}
1808 }
1809 return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1810 }
1811 }
1812 }
1813
1814 func testKeepsOpenIncompleteSubscriptionsAfterEose(client *Client, key1, key2 *KeyPair) (result TestResult) {
1815 // Subscribe to events from a random author (incomplete subscription)
1816 filter := map[string]interface{}{
1817 "authors": []string{hex.Enc(key2.Pubkey)},
1818 "kinds": []int{int(kind.TextNote.K)},
1819 "limit": 10,
1820 "until": time.Now().Unix() - 86400, // Past timestamp
1821 }
1822 ch, err := client.Subscribe("test-open-incomplete", []interface{}{filter})
1823 if err != nil {
1824 return TestResult{Pass: false, Info: fmt.Sprintf("failed to subscribe: %v", err)}
1825 }
1826 defer client.Unsubscribe("test-open-incomplete")
1827 // Wait for EOSE
1828 timeout := time.After(3 * time.Second)
1829 gotEose := false
1830 for {
1831 select {
1832 case msg, ok := <-ch:
1833 if !ok {
1834 return TestResult{Pass: false, Info: "incomplete subscription closed after EOSE"}
1835 }
1836 var raw []interface{}
1837 if err = json.Unmarshal(msg, &raw); err != nil {
1838 continue
1839 }
1840 if len(raw) >= 2 {
1841 if typ, ok := raw[0].(string); ok && typ == "EOSE" {
1842 gotEose = true
1843 // After EOSE, subscription should remain open for incomplete subscriptions
1844 time.Sleep(200 * time.Millisecond)
1845 // Channel should still be open (not closed)
1846 select {
1847 case _, ok := <-ch:
1848 if !ok {
1849 return TestResult{Pass: false, Info: "incomplete subscription closed after EOSE"}
1850 }
1851 // Channel is still open, which is correct
1852 return TestResult{Pass: true}
1853 default:
1854 // Channel is still open, which is correct
1855 return TestResult{Pass: true}
1856 }
1857 }
1858 }
1859 case <-timeout:
1860 if gotEose {
1861 return TestResult{Pass: true, Info: "EOSE received, subscription remains open"}
1862 }
1863 return TestResult{Pass: false, Info: "timeout waiting for EOSE"}
1864 }
1865 }
1866 }
1867
1868 // JSON tests
1869
1870 func testAcceptsEventsWithEmptyTags(client *Client, key1, key2 *KeyPair) (result TestResult) {
1871 // Create event with empty tags array
1872 emptyTags := tag.NewS()
1873 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "empty tags test", emptyTags)
1874 if err != nil {
1875 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1876 }
1877 if err = client.Publish(ev); err != nil {
1878 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1879 }
1880 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1881 if err != nil {
1882 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1883 }
1884 if !accepted {
1885 return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1886 }
1887 return TestResult{Pass: true}
1888 }
1889
1890 func testAcceptsNip1JsonEscapeSequences(client *Client, key1, key2 *KeyPair) (result TestResult) {
1891 // NIP-01 escape sequences: \n, \", \\, \r, \t, \b, \f
1892 content := "linebreak\\ndoublequote\\\"backslash\\\\carraigereturn\\rtab\\tbackspace\\bformfeed\\fend"
1893 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, content, nil)
1894 if err != nil {
1895 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1896 }
1897 if err = client.Publish(ev); err != nil {
1898 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1899 }
1900 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1901 if err != nil {
1902 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1903 }
1904 if !accepted {
1905 return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1906 }
1907 return TestResult{Pass: true}
1908 }
1909
1910 // Registration tests
1911
1912 func testSendsOkAfterEvent(client *Client, key1, key2 *KeyPair) (result TestResult) {
1913 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "OK test", nil)
1914 if err != nil {
1915 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1916 }
1917 if err = client.Publish(ev); err != nil {
1918 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1919 }
1920 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1921 if err != nil {
1922 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1923 }
1924 if !accepted {
1925 return TestResult{Pass: false, Info: fmt.Sprintf("event rejected: %s", reason)}
1926 }
1927 return TestResult{Pass: true}
1928 }
1929
1930 func testVerifiesSignatures(client *Client, key1, key2 *KeyPair) (result TestResult) {
1931 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "signature test", nil)
1932 if err != nil {
1933 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1934 }
1935 // Corrupt the signature
1936 for i := range ev.Sig {
1937 ev.Sig[i] = 0
1938 }
1939 if err = client.Publish(ev); err != nil {
1940 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1941 }
1942 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1943 if err != nil {
1944 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1945 }
1946 if accepted {
1947 return TestResult{Pass: false, Info: "invalid signature was accepted"}
1948 }
1949 _ = reason
1950 return TestResult{Pass: true}
1951 }
1952
1953 func testVerifiesIdHashes(client *Client, key1, key2 *KeyPair) (result TestResult) {
1954 ev, err := CreateEvent(key1.Secret, kind.TextNote.K, "ID hash test", nil)
1955 if err != nil {
1956 return TestResult{Pass: false, Info: fmt.Sprintf("failed to create event: %v", err)}
1957 }
1958 // Save the correct ID
1959 correctID := make([]byte, len(ev.ID))
1960 copy(correctID, ev.ID)
1961 // Corrupt the ID AFTER signing (Sign() recalculates ID, so we corrupt it after)
1962 for i := range ev.ID {
1963 ev.ID[i] = 0xCA
1964 }
1965 // Don't re-sign - the signature is valid for the correct ID, but we have a corrupted ID
1966 if err = client.Publish(ev); err != nil {
1967 return TestResult{Pass: false, Info: fmt.Sprintf("failed to publish: %v", err)}
1968 }
1969 // Use the corrupted ID to wait for OK (relay should reject based on ID mismatch)
1970 accepted, reason, err := client.WaitForOK(ev.ID, 5*time.Second)
1971 if err != nil {
1972 return TestResult{Pass: false, Info: fmt.Sprintf("failed to get OK: %v", err)}
1973 }
1974 if accepted {
1975 return TestResult{Pass: false, Info: "invalid ID hash was accepted"}
1976 }
1977 _ = reason
1978 _ = correctID
1979 return TestResult{Pass: true}
1980 }
1981