wasmdb_test.go raw
1 //go:build js && wasm
2
3 package wasmdb
4
5 import (
6 "bytes"
7 "context"
8 "testing"
9 "time"
10
11 "next.orly.dev/pkg/nostr/encoders/event"
12 "next.orly.dev/pkg/nostr/encoders/filter"
13 "next.orly.dev/pkg/nostr/encoders/kind"
14 "next.orly.dev/pkg/nostr/encoders/tag"
15 "next.orly.dev/pkg/database/indexes/types"
16 )
17
18 // TestDatabaseOpen tests that we can open an IndexedDB database
19 func TestDatabaseOpen(t *testing.T) {
20 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
21 defer cancel()
22
23 // Create a new database instance
24 db, err := New(ctx, cancel, "/tmp/test", "info")
25 if err != nil {
26 t.Fatalf("Failed to create database: %v", err)
27 }
28 defer db.Close()
29
30 // Wait for the database to be ready
31 select {
32 case <-db.Ready():
33 t.Log("Database ready")
34 case <-ctx.Done():
35 t.Fatal("Timeout waiting for database to be ready")
36 }
37
38 t.Log("Database opened successfully")
39 }
40
41 // TestDatabaseMetaStorage tests storing and retrieving metadata
42 func TestDatabaseMetaStorage(t *testing.T) {
43 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
44 defer cancel()
45
46 db, err := New(ctx, cancel, "/tmp/test", "info")
47 if err != nil {
48 t.Fatalf("Failed to create database: %v", err)
49 }
50 defer db.Close()
51
52 <-db.Ready()
53
54 // Test SetMarker and GetMarker
55 testKey := "test_key"
56 testValue := []byte("test_value_12345")
57
58 err = db.SetMarker(testKey, testValue)
59 if err != nil {
60 t.Fatalf("Failed to set marker: %v", err)
61 }
62
63 retrieved, err := db.GetMarker(testKey)
64 if err != nil {
65 t.Fatalf("Failed to get marker: %v", err)
66 }
67
68 if string(retrieved) != string(testValue) {
69 t.Errorf("Retrieved value %q doesn't match expected %q", retrieved, testValue)
70 }
71
72 // Test HasMarker
73 if !db.HasMarker(testKey) {
74 t.Error("HasMarker returned false for existing key")
75 }
76
77 if db.HasMarker("nonexistent_key") {
78 t.Error("HasMarker returned true for nonexistent key")
79 }
80
81 // Test DeleteMarker
82 err = db.DeleteMarker(testKey)
83 if err != nil {
84 t.Fatalf("Failed to delete marker: %v", err)
85 }
86
87 if db.HasMarker(testKey) {
88 t.Error("HasMarker returned true after deletion")
89 }
90 }
91
92 // TestDatabaseSerialCounters tests the serial number generation
93 func TestDatabaseSerialCounters(t *testing.T) {
94 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
95 defer cancel()
96
97 db, err := New(ctx, cancel, "/tmp/test", "info")
98 if err != nil {
99 t.Fatalf("Failed to create database: %v", err)
100 }
101 defer db.Close()
102
103 <-db.Ready()
104
105 // Generate some serials and verify they're incrementing
106 serial1, err := db.nextEventSerial()
107 if err != nil {
108 t.Fatalf("Failed to get first serial: %v", err)
109 }
110
111 serial2, err := db.nextEventSerial()
112 if err != nil {
113 t.Fatalf("Failed to get second serial: %v", err)
114 }
115
116 if serial2 != serial1+1 {
117 t.Errorf("Serials not incrementing: got %d and %d", serial1, serial2)
118 }
119
120 t.Logf("Generated serials: %d, %d", serial1, serial2)
121 }
122
123 // TestDatabaseRelayIdentity tests relay identity key management
124 func TestDatabaseRelayIdentity(t *testing.T) {
125 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
126 defer cancel()
127
128 db, err := New(ctx, cancel, "/tmp/test", "info")
129 if err != nil {
130 t.Fatalf("Failed to create database: %v", err)
131 }
132 defer db.Close()
133
134 <-db.Ready()
135
136 // First call should create a new identity
137 skb, err := db.GetOrCreateRelayIdentitySecret()
138 if err != nil {
139 t.Fatalf("Failed to get/create relay identity: %v", err)
140 }
141
142 if len(skb) != 32 {
143 t.Errorf("Expected 32-byte secret key, got %d bytes", len(skb))
144 }
145
146 // Second call should return the same key
147 skb2, err := db.GetOrCreateRelayIdentitySecret()
148 if err != nil {
149 t.Fatalf("Failed to get relay identity second time: %v", err)
150 }
151
152 if string(skb) != string(skb2) {
153 t.Error("GetOrCreateRelayIdentitySecret returned different keys on second call")
154 }
155
156 t.Logf("Relay identity key: %x", skb[:8])
157 }
158
159 // TestDatabaseWipe tests the wipe functionality
160 func TestDatabaseWipe(t *testing.T) {
161 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
162 defer cancel()
163
164 db, err := New(ctx, cancel, "/tmp/test", "info")
165 if err != nil {
166 t.Fatalf("Failed to create database: %v", err)
167 }
168 defer db.Close()
169
170 <-db.Ready()
171
172 // Store some data
173 err = db.SetMarker("wipe_test_key", []byte("wipe_test_value"))
174 if err != nil {
175 t.Fatalf("Failed to set marker: %v", err)
176 }
177
178 // Wipe the database
179 err = db.Wipe()
180 if err != nil {
181 t.Fatalf("Failed to wipe database: %v", err)
182 }
183
184 // Verify data is gone
185 if db.HasMarker("wipe_test_key") {
186 t.Error("Marker still exists after wipe")
187 }
188
189 t.Log("Database wipe successful")
190 }
191
192 // createTestEvent creates a test event with fake ID and signature (for storage testing only)
193 func createTestEvent(kind uint16, content string, pubkey []byte) *event.E {
194 ev := event.New()
195 ev.Kind = kind
196 ev.Content = []byte(content)
197 ev.CreatedAt = time.Now().Unix()
198 ev.Tags = tag.NewS()
199
200 // Use provided pubkey or generate a fake one
201 if len(pubkey) == 32 {
202 ev.Pubkey = pubkey
203 } else {
204 ev.Pubkey = make([]byte, 32)
205 for i := range ev.Pubkey {
206 ev.Pubkey[i] = byte(i)
207 }
208 }
209
210 // Generate a fake ID (normally would be SHA256 of serialized event)
211 ev.ID = make([]byte, 32)
212 for i := range ev.ID {
213 ev.ID[i] = byte(i + 100)
214 }
215
216 // Generate a fake signature (won't verify, but storage doesn't need to verify)
217 ev.Sig = make([]byte, 64)
218 for i := range ev.Sig {
219 ev.Sig[i] = byte(i + 200)
220 }
221
222 return ev
223 }
224
225 // TestSaveAndFetchEvent tests saving and fetching events
226 func TestSaveAndFetchEvent(t *testing.T) {
227 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
228 defer cancel()
229
230 // Wipe the database to start fresh
231 db, err := New(ctx, cancel, "/tmp/test", "debug")
232 if err != nil {
233 t.Fatalf("Failed to create database: %v", err)
234 }
235 defer db.Close()
236
237 <-db.Ready()
238
239 // Wipe to ensure clean state
240 if err := db.Wipe(); err != nil {
241 t.Fatalf("Failed to wipe database: %v", err)
242 }
243
244 // Create a test event
245 ev := createTestEvent(1, "Hello, Nostr!", nil)
246 t.Logf("Created event with ID: %x", ev.ID[:8])
247
248 // Save the event
249 replaced, err := db.SaveEvent(ctx, ev)
250 if err != nil {
251 t.Fatalf("Failed to save event: %v", err)
252 }
253 if replaced {
254 t.Error("Expected replaced to be false for new event")
255 }
256
257 t.Log("Event saved successfully")
258
259 // Look up the serial by ID
260 ser, err := db.GetSerialById(ev.ID)
261 if err != nil {
262 t.Fatalf("Failed to get serial by ID: %v", err)
263 }
264 if ser == nil {
265 t.Fatal("Got nil serial")
266 }
267 t.Logf("Event serial: %d", ser.Get())
268
269 // Fetch the event by serial
270 fetchedEv, err := db.FetchEventBySerial(ser)
271 if err != nil {
272 t.Fatalf("Failed to fetch event by serial: %v", err)
273 }
274 if fetchedEv == nil {
275 t.Fatal("Fetched event is nil")
276 }
277
278 // Verify the event content matches
279 if !bytes.Equal(fetchedEv.ID, ev.ID) {
280 t.Errorf("Event ID mismatch: got %x, want %x", fetchedEv.ID[:8], ev.ID[:8])
281 }
282 if !bytes.Equal(fetchedEv.Content, ev.Content) {
283 t.Errorf("Event content mismatch: got %q, want %q", fetchedEv.Content, ev.Content)
284 }
285 if fetchedEv.Kind != ev.Kind {
286 t.Errorf("Event kind mismatch: got %d, want %d", fetchedEv.Kind, ev.Kind)
287 }
288
289 t.Log("Event fetched and verified successfully")
290 }
291
292 // TestSaveEventDuplicate tests that saving a duplicate event returns an error
293 func TestSaveEventDuplicate(t *testing.T) {
294 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
295 defer cancel()
296
297 db, err := New(ctx, cancel, "/tmp/test", "info")
298 if err != nil {
299 t.Fatalf("Failed to create database: %v", err)
300 }
301 defer db.Close()
302
303 <-db.Ready()
304
305 // Wipe to ensure clean state
306 if err := db.Wipe(); err != nil {
307 t.Fatalf("Failed to wipe database: %v", err)
308 }
309
310 // Create and save a test event
311 ev := createTestEvent(1, "Duplicate test", nil)
312
313 _, err = db.SaveEvent(ctx, ev)
314 if err != nil {
315 t.Fatalf("Failed to save first event: %v", err)
316 }
317
318 // Try to save the same event again
319 _, err = db.SaveEvent(ctx, ev)
320 if err == nil {
321 t.Error("Expected error when saving duplicate event, got nil")
322 } else {
323 t.Logf("Got expected error for duplicate: %v", err)
324 }
325 }
326
327 // TestSaveEventWithTags tests saving an event with tags
328 func TestSaveEventWithTags(t *testing.T) {
329 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
330 defer cancel()
331
332 db, err := New(ctx, cancel, "/tmp/test", "info")
333 if err != nil {
334 t.Fatalf("Failed to create database: %v", err)
335 }
336 defer db.Close()
337
338 <-db.Ready()
339
340 // Wipe to ensure clean state
341 if err := db.Wipe(); err != nil {
342 t.Fatalf("Failed to wipe database: %v", err)
343 }
344
345 // Create an event with tags
346 ev := createTestEvent(1, "Event with tags", nil)
347
348 // Make the ID unique
349 ev.ID[0] = 0x42
350
351 // Add some tags
352 ev.Tags = tag.NewS(
353 tag.NewFromAny("t", "nostr"),
354 tag.NewFromAny("t", "test"),
355 )
356
357 // Save the event
358 _, err = db.SaveEvent(ctx, ev)
359 if err != nil {
360 t.Fatalf("Failed to save event with tags: %v", err)
361 }
362
363 // Fetch it back
364 ser, err := db.GetSerialById(ev.ID)
365 if err != nil {
366 t.Fatalf("Failed to get serial: %v", err)
367 }
368
369 fetchedEv, err := db.FetchEventBySerial(ser)
370 if err != nil {
371 t.Fatalf("Failed to fetch event: %v", err)
372 }
373
374 // Verify tags
375 if fetchedEv.Tags == nil || fetchedEv.Tags.Len() != 2 {
376 t.Errorf("Expected 2 tags, got %v", fetchedEv.Tags)
377 }
378
379 t.Log("Event with tags saved and fetched successfully")
380 }
381
382 // TestFetchEventsBySerials tests batch fetching of events
383 func TestFetchEventsBySerials(t *testing.T) {
384 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
385 defer cancel()
386
387 db, err := New(ctx, cancel, "/tmp/test", "info")
388 if err != nil {
389 t.Fatalf("Failed to create database: %v", err)
390 }
391 defer db.Close()
392
393 <-db.Ready()
394
395 // Wipe to ensure clean state
396 if err := db.Wipe(); err != nil {
397 t.Fatalf("Failed to wipe database: %v", err)
398 }
399
400 // Create and save multiple events
401 var serials []*types.Uint40
402 for i := 0; i < 3; i++ {
403 ev := createTestEvent(1, "Batch test event", nil)
404 ev.ID[0] = byte(0x50 + i) // Make IDs unique
405
406 _, err := db.SaveEvent(ctx, ev)
407 if err != nil {
408 t.Fatalf("Failed to save event %d: %v", i, err)
409 }
410
411 ser, err := db.GetSerialById(ev.ID)
412 if err != nil {
413 t.Fatalf("Failed to get serial for event %d: %v", i, err)
414 }
415 serials = append(serials, ser)
416 }
417
418 // Batch fetch
419 events, err := db.FetchEventsBySerials(serials)
420 if err != nil {
421 t.Fatalf("Failed to batch fetch events: %v", err)
422 }
423
424 if len(events) != 3 {
425 t.Errorf("Expected 3 events, got %d", len(events))
426 }
427
428 t.Logf("Successfully batch fetched %d events", len(events))
429 }
430
431 // TestGetFullIdPubkeyBySerial tests getting event metadata by serial
432 func TestGetFullIdPubkeyBySerial(t *testing.T) {
433 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
434 defer cancel()
435
436 db, err := New(ctx, cancel, "/tmp/test", "debug")
437 if err != nil {
438 t.Fatalf("Failed to create database: %v", err)
439 }
440 defer db.Close()
441
442 <-db.Ready()
443
444 // Wipe to ensure clean state
445 if err := db.Wipe(); err != nil {
446 t.Fatalf("Failed to wipe database: %v", err)
447 }
448
449 // Create and save an event
450 ev := createTestEvent(1, "Metadata test", nil)
451 ev.ID[0] = 0x99 // Make ID unique
452
453 _, err = db.SaveEvent(ctx, ev)
454 if err != nil {
455 t.Fatalf("Failed to save event: %v", err)
456 }
457
458 ser, err := db.GetSerialById(ev.ID)
459 if err != nil {
460 t.Fatalf("Failed to get serial: %v", err)
461 }
462
463 // Get full ID/pubkey/timestamp metadata
464 fidpk, err := db.GetFullIdPubkeyBySerial(ser)
465 if err != nil {
466 t.Fatalf("Failed to get full id pubkey: %v", err)
467 }
468 if fidpk == nil {
469 t.Fatal("Got nil fidpk")
470 }
471
472 // Verify the ID matches
473 if !bytes.Equal(fidpk.Id, ev.ID) {
474 t.Errorf("ID mismatch: got %x, want %x", fidpk.Id[:8], ev.ID[:8])
475 }
476
477 t.Logf("Got metadata: ID=%x, Pub=%x, Ts=%d, Ser=%d",
478 fidpk.Id[:8], fidpk.Pub[:4], fidpk.Ts, fidpk.Ser)
479 }
480
481 // TestQueryEventsByKind tests querying events by kind
482 func TestQueryEventsByKind(t *testing.T) {
483 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
484 defer cancel()
485
486 db, err := New(ctx, cancel, "/tmp/test", "info")
487 if err != nil {
488 t.Fatalf("Failed to create database: %v", err)
489 }
490 defer db.Close()
491
492 <-db.Ready()
493
494 // Wipe to ensure clean state
495 if err := db.Wipe(); err != nil {
496 t.Fatalf("Failed to wipe database: %v", err)
497 }
498
499 // Create events of different kinds
500 ev1 := createTestEvent(1, "Kind 1 event", nil)
501 ev1.ID[0] = 0xA1
502 ev2 := createTestEvent(1, "Another kind 1", nil)
503 ev2.ID[0] = 0xA2
504 ev3 := createTestEvent(7, "Kind 7 event", nil)
505 ev3.ID[0] = 0xA3
506
507 // Save events
508 for _, ev := range []*event.E{ev1, ev2, ev3} {
509 if _, err := db.SaveEvent(ctx, ev); err != nil {
510 t.Fatalf("Failed to save event: %v", err)
511 }
512 }
513
514 // Query for kind 1
515 f := &filter.F{
516 Kinds: kind.NewS(kind.New(1)),
517 }
518
519 evs, err := db.QueryEvents(ctx, f)
520 if err != nil {
521 t.Fatalf("Failed to query events: %v", err)
522 }
523
524 if len(evs) != 2 {
525 t.Errorf("Expected 2 events of kind 1, got %d", len(evs))
526 }
527
528 t.Logf("Query by kind 1 returned %d events", len(evs))
529 }
530
531 // TestQueryEventsByAuthor tests querying events by author pubkey
532 func TestQueryEventsByAuthor(t *testing.T) {
533 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
534 defer cancel()
535
536 db, err := New(ctx, cancel, "/tmp/test", "info")
537 if err != nil {
538 t.Fatalf("Failed to create database: %v", err)
539 }
540 defer db.Close()
541
542 <-db.Ready()
543
544 // Wipe to ensure clean state
545 if err := db.Wipe(); err != nil {
546 t.Fatalf("Failed to wipe database: %v", err)
547 }
548
549 // Create events from different authors
550 author1 := make([]byte, 32)
551 for i := range author1 {
552 author1[i] = 0x11
553 }
554 author2 := make([]byte, 32)
555 for i := range author2 {
556 author2[i] = 0x22
557 }
558
559 ev1 := createTestEvent(1, "From author 1", author1)
560 ev1.ID[0] = 0xB1
561 ev2 := createTestEvent(1, "Also from author 1", author1)
562 ev2.ID[0] = 0xB2
563 ev3 := createTestEvent(1, "From author 2", author2)
564 ev3.ID[0] = 0xB3
565
566 // Save events
567 for _, ev := range []*event.E{ev1, ev2, ev3} {
568 if _, err := db.SaveEvent(ctx, ev); err != nil {
569 t.Fatalf("Failed to save event: %v", err)
570 }
571 }
572
573 // Query for author1
574 f := &filter.F{
575 Authors: tag.NewFromBytesSlice(author1),
576 }
577
578 evs, err := db.QueryEvents(ctx, f)
579 if err != nil {
580 t.Fatalf("Failed to query events: %v", err)
581 }
582
583 if len(evs) != 2 {
584 t.Errorf("Expected 2 events from author1, got %d", len(evs))
585 }
586
587 t.Logf("Query by author returned %d events", len(evs))
588 }
589
590 // TestCountEvents tests the event counting functionality
591 func TestCountEvents(t *testing.T) {
592 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
593 defer cancel()
594
595 db, err := New(ctx, cancel, "/tmp/test", "info")
596 if err != nil {
597 t.Fatalf("Failed to create database: %v", err)
598 }
599 defer db.Close()
600
601 <-db.Ready()
602
603 // Wipe to ensure clean state
604 if err := db.Wipe(); err != nil {
605 t.Fatalf("Failed to wipe database: %v", err)
606 }
607
608 // Create multiple events
609 for i := 0; i < 5; i++ {
610 ev := createTestEvent(1, "Count test event", nil)
611 ev.ID[0] = byte(0xC0 + i)
612 if _, err := db.SaveEvent(ctx, ev); err != nil {
613 t.Fatalf("Failed to save event: %v", err)
614 }
615 }
616
617 // Count all kind 1 events
618 f := &filter.F{
619 Kinds: kind.NewS(kind.New(1)),
620 }
621
622 count, approx, err := db.CountEvents(ctx, f)
623 if err != nil {
624 t.Fatalf("Failed to count events: %v", err)
625 }
626
627 if count != 5 {
628 t.Errorf("Expected count of 5, got %d", count)
629 }
630 if approx {
631 t.Log("Count is approximate")
632 }
633
634 t.Logf("CountEvents returned %d", count)
635 }
636
637 // TestQueryEventsWithLimit tests applying a limit to query results
638 func TestQueryEventsWithLimit(t *testing.T) {
639 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
640 defer cancel()
641
642 db, err := New(ctx, cancel, "/tmp/test", "info")
643 if err != nil {
644 t.Fatalf("Failed to create database: %v", err)
645 }
646 defer db.Close()
647
648 <-db.Ready()
649
650 // Wipe to ensure clean state
651 if err := db.Wipe(); err != nil {
652 t.Fatalf("Failed to wipe database: %v", err)
653 }
654
655 // Create 10 events
656 for i := 0; i < 10; i++ {
657 ev := createTestEvent(1, "Limit test event", nil)
658 ev.ID[0] = byte(0xD0 + i)
659 if _, err := db.SaveEvent(ctx, ev); err != nil {
660 t.Fatalf("Failed to save event: %v", err)
661 }
662 }
663
664 // Query with limit of 3
665 limit := uint(3)
666 f := &filter.F{
667 Kinds: kind.NewS(kind.New(1)),
668 Limit: &limit,
669 }
670
671 evs, err := db.QueryEvents(ctx, f)
672 if err != nil {
673 t.Fatalf("Failed to query events: %v", err)
674 }
675
676 if len(evs) != 3 {
677 t.Errorf("Expected 3 events with limit, got %d", len(evs))
678 }
679
680 t.Logf("Query with limit returned %d events", len(evs))
681 }
682
683 // TestQueryEventsByTag tests querying events by tag
684 func TestQueryEventsByTag(t *testing.T) {
685 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
686 defer cancel()
687
688 db, err := New(ctx, cancel, "/tmp/test", "info")
689 if err != nil {
690 t.Fatalf("Failed to create database: %v", err)
691 }
692 defer db.Close()
693
694 <-db.Ready()
695
696 // Wipe to ensure clean state
697 if err := db.Wipe(); err != nil {
698 t.Fatalf("Failed to wipe database: %v", err)
699 }
700
701 // Create events with different tags
702 ev1 := createTestEvent(1, "Has bitcoin tag", nil)
703 ev1.ID[0] = 0xE1
704 ev1.Tags = tag.NewS(
705 tag.NewFromAny("t", "bitcoin"),
706 )
707
708 ev2 := createTestEvent(1, "Has nostr tag", nil)
709 ev2.ID[0] = 0xE2
710 ev2.Tags = tag.NewS(
711 tag.NewFromAny("t", "nostr"),
712 )
713
714 ev3 := createTestEvent(1, "Has bitcoin and nostr tags", nil)
715 ev3.ID[0] = 0xE3
716 ev3.Tags = tag.NewS(
717 tag.NewFromAny("t", "bitcoin"),
718 tag.NewFromAny("t", "nostr"),
719 )
720
721 // Save events
722 for _, ev := range []*event.E{ev1, ev2, ev3} {
723 if _, err := db.SaveEvent(ctx, ev); err != nil {
724 t.Fatalf("Failed to save event: %v", err)
725 }
726 }
727
728 // Query for bitcoin tag
729 f := &filter.F{
730 Tags: tag.NewS(
731 tag.NewFromAny("#t", "bitcoin"),
732 ),
733 }
734
735 evs, err := db.QueryEvents(ctx, f)
736 if err != nil {
737 t.Fatalf("Failed to query events: %v", err)
738 }
739
740 if len(evs) != 2 {
741 t.Errorf("Expected 2 events with bitcoin tag, got %d", len(evs))
742 }
743
744 t.Logf("Query by tag returned %d events", len(evs))
745 }
746
747 // TestQueryForSerials tests the QueryForSerials method
748 func TestQueryForSerials(t *testing.T) {
749 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
750 defer cancel()
751
752 db, err := New(ctx, cancel, "/tmp/test", "info")
753 if err != nil {
754 t.Fatalf("Failed to create database: %v", err)
755 }
756 defer db.Close()
757
758 <-db.Ready()
759
760 // Wipe to ensure clean state
761 if err := db.Wipe(); err != nil {
762 t.Fatalf("Failed to wipe database: %v", err)
763 }
764
765 // Create and save events
766 for i := 0; i < 3; i++ {
767 ev := createTestEvent(1, "Serial test", nil)
768 ev.ID[0] = byte(0xF0 + i)
769 if _, err := db.SaveEvent(ctx, ev); err != nil {
770 t.Fatalf("Failed to save event: %v", err)
771 }
772 }
773
774 // Query for serials
775 f := &filter.F{
776 Kinds: kind.NewS(kind.New(1)),
777 }
778
779 sers, err := db.QueryForSerials(ctx, f)
780 if err != nil {
781 t.Fatalf("Failed to query for serials: %v", err)
782 }
783
784 if len(sers) != 3 {
785 t.Errorf("Expected 3 serials, got %d", len(sers))
786 }
787
788 t.Logf("QueryForSerials returned %d serials", len(sers))
789 }
790
791 // TestDeleteEvent tests deleting an event by ID
792 func TestDeleteEvent(t *testing.T) {
793 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
794 defer cancel()
795
796 db, err := New(ctx, cancel, "/tmp/test", "info")
797 if err != nil {
798 t.Fatalf("Failed to create database: %v", err)
799 }
800 defer db.Close()
801
802 <-db.Ready()
803
804 // Wipe to ensure clean state
805 if err := db.Wipe(); err != nil {
806 t.Fatalf("Failed to wipe database: %v", err)
807 }
808
809 // Create and save an event
810 ev := createTestEvent(1, "Event to delete", nil)
811 ev.ID[0] = 0xDE
812
813 if _, err := db.SaveEvent(ctx, ev); err != nil {
814 t.Fatalf("Failed to save event: %v", err)
815 }
816
817 // Verify it exists
818 ser, err := db.GetSerialById(ev.ID)
819 if err != nil {
820 t.Fatalf("Failed to get serial: %v", err)
821 }
822 if ser == nil {
823 t.Fatal("Serial should not be nil after save")
824 }
825
826 // Delete the event
827 if err := db.DeleteEvent(ctx, ev.ID); err != nil {
828 t.Fatalf("Failed to delete event: %v", err)
829 }
830
831 // Verify it no longer exists
832 ser, err = db.GetSerialById(ev.ID)
833 if err == nil && ser != nil {
834 t.Error("Event should not exist after deletion")
835 }
836
837 t.Log("Event deleted successfully")
838 }
839
840 // TestDeleteEventBySerial tests deleting an event by serial number
841 func TestDeleteEventBySerial(t *testing.T) {
842 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
843 defer cancel()
844
845 db, err := New(ctx, cancel, "/tmp/test", "info")
846 if err != nil {
847 t.Fatalf("Failed to create database: %v", err)
848 }
849 defer db.Close()
850
851 <-db.Ready()
852
853 // Wipe to ensure clean state
854 if err := db.Wipe(); err != nil {
855 t.Fatalf("Failed to wipe database: %v", err)
856 }
857
858 // Create and save an event
859 ev := createTestEvent(1, "Event to delete by serial", nil)
860 ev.ID[0] = 0xDF
861
862 if _, err := db.SaveEvent(ctx, ev); err != nil {
863 t.Fatalf("Failed to save event: %v", err)
864 }
865
866 // Get the serial
867 ser, err := db.GetSerialById(ev.ID)
868 if err != nil {
869 t.Fatalf("Failed to get serial: %v", err)
870 }
871
872 // Fetch the event
873 fetchedEv, err := db.FetchEventBySerial(ser)
874 if err != nil {
875 t.Fatalf("Failed to fetch event: %v", err)
876 }
877
878 // Delete by serial
879 if err := db.DeleteEventBySerial(ctx, ser, fetchedEv); err != nil {
880 t.Fatalf("Failed to delete event by serial: %v", err)
881 }
882
883 // Verify event is gone
884 fetchedEv, err = db.FetchEventBySerial(ser)
885 if err == nil && fetchedEv != nil {
886 t.Error("Event should not exist after deletion by serial")
887 }
888
889 t.Log("Event deleted by serial successfully")
890 }
891
892 // TestCheckForDeleted tests that deleted events are properly detected
893 func TestCheckForDeleted(t *testing.T) {
894 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
895 defer cancel()
896
897 db, err := New(ctx, cancel, "/tmp/test", "info")
898 if err != nil {
899 t.Fatalf("Failed to create database: %v", err)
900 }
901 defer db.Close()
902
903 <-db.Ready()
904
905 // Wipe to ensure clean state
906 if err := db.Wipe(); err != nil {
907 t.Fatalf("Failed to wipe database: %v", err)
908 }
909
910 // Create a regular event
911 ev := createTestEvent(1, "Event that will be deleted", nil)
912 ev.ID[0] = 0xDD
913 ev.CreatedAt = time.Now().Unix() - 100 // 100 seconds ago
914
915 if _, err := db.SaveEvent(ctx, ev); err != nil {
916 t.Fatalf("Failed to save event: %v", err)
917 }
918
919 // Create a kind 5 deletion event referencing it
920 // Use binary format for the e-tag value since GetIndexesForEvent hashes the raw value,
921 // and NormalizeTagValueForHash converts hex to binary before hashing in filters
922 deleteEv := createTestEvent(5, "", ev.Pubkey)
923 deleteEv.ID[0] = 0xD5
924 deleteEv.CreatedAt = time.Now().Unix() // Now
925
926 // Store the e-tag with binary value in the format matching JSON unmarshal
927 // The nostr library stores e/p tag values as 33 bytes (32 bytes + null terminator)
928 eTagValue := make([]byte, 33)
929 copy(eTagValue[:32], ev.ID)
930 eTagValue[32] = 0 // null terminator to match nostr library's binary format
931 deleteEv.Tags = tag.NewS(
932 tag.NewFromAny("e", string(eTagValue)),
933 )
934
935 if _, err := db.SaveEvent(ctx, deleteEv); err != nil {
936 t.Fatalf("Failed to save delete event: %v", err)
937 }
938
939 // Check if the original event is marked as deleted
940 err = db.CheckForDeleted(ev, nil)
941 if err == nil {
942 t.Error("Expected error indicating event was deleted")
943 } else {
944 t.Logf("CheckForDeleted correctly detected deletion: %v", err)
945 }
946 }
947
948 // ============================================================================
949 // NIP-43 Membership Tests
950 // ============================================================================
951
952 // TestNIP43AddAndRemoveMember tests adding and removing NIP-43 members
953 func TestNIP43AddAndRemoveMember(t *testing.T) {
954 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
955 defer cancel()
956
957 db, err := New(ctx, cancel, "/tmp/test", "info")
958 if err != nil {
959 t.Fatalf("Failed to create database: %v", err)
960 }
961 defer db.Close()
962
963 <-db.Ready()
964
965 // Wipe to ensure clean state
966 if err := db.Wipe(); err != nil {
967 t.Fatalf("Failed to wipe database: %v", err)
968 }
969
970 // Create a test pubkey
971 pubkey := make([]byte, 32)
972 for i := range pubkey {
973 pubkey[i] = byte(i + 1)
974 }
975
976 // Initially should not be a member
977 isMember, err := db.IsNIP43Member(pubkey)
978 if err != nil {
979 t.Fatalf("Failed to check member status: %v", err)
980 }
981 if isMember {
982 t.Error("Expected non-member initially")
983 }
984
985 // Add member with invite code
986 inviteCode := "TEST-INVITE-123"
987 if err := db.AddNIP43Member(pubkey, inviteCode); err != nil {
988 t.Fatalf("Failed to add NIP-43 member: %v", err)
989 }
990
991 // Should now be a member
992 isMember, err = db.IsNIP43Member(pubkey)
993 if err != nil {
994 t.Fatalf("Failed to check member status: %v", err)
995 }
996 if !isMember {
997 t.Error("Expected to be a member after adding")
998 }
999
1000 // Get membership details
1001 membership, err := db.GetNIP43Membership(pubkey)
1002 if err != nil {
1003 t.Fatalf("Failed to get membership: %v", err)
1004 }
1005 if membership.InviteCode != inviteCode {
1006 t.Errorf("Invite code mismatch: got %s, want %s", membership.InviteCode, inviteCode)
1007 }
1008 if !bytes.Equal(membership.Pubkey, pubkey) {
1009 t.Error("Pubkey mismatch in membership")
1010 }
1011
1012 // Remove member
1013 if err := db.RemoveNIP43Member(pubkey); err != nil {
1014 t.Fatalf("Failed to remove member: %v", err)
1015 }
1016
1017 // Should no longer be a member
1018 isMember, err = db.IsNIP43Member(pubkey)
1019 if err != nil {
1020 t.Fatalf("Failed to check member status: %v", err)
1021 }
1022 if isMember {
1023 t.Error("Expected non-member after removal")
1024 }
1025
1026 t.Log("NIP-43 add/remove member test passed")
1027 }
1028
1029 // TestNIP43GetAllMembers tests retrieving all members
1030 func TestNIP43GetAllMembers(t *testing.T) {
1031 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1032 defer cancel()
1033
1034 db, err := New(ctx, cancel, "/tmp/test", "info")
1035 if err != nil {
1036 t.Fatalf("Failed to create database: %v", err)
1037 }
1038 defer db.Close()
1039
1040 <-db.Ready()
1041
1042 // Wipe to ensure clean state
1043 if err := db.Wipe(); err != nil {
1044 t.Fatalf("Failed to wipe database: %v", err)
1045 }
1046
1047 // Add multiple members
1048 pubkeys := make([][]byte, 3)
1049 for i := 0; i < 3; i++ {
1050 pubkeys[i] = make([]byte, 32)
1051 for j := range pubkeys[i] {
1052 pubkeys[i][j] = byte((i+1)*10 + j)
1053 }
1054 if err := db.AddNIP43Member(pubkeys[i], "invite"+string(rune('A'+i))); err != nil {
1055 t.Fatalf("Failed to add member %d: %v", i, err)
1056 }
1057 }
1058
1059 // Get all members
1060 members, err := db.GetAllNIP43Members()
1061 if err != nil {
1062 t.Fatalf("Failed to get all members: %v", err)
1063 }
1064
1065 if len(members) != 3 {
1066 t.Errorf("Expected 3 members, got %d", len(members))
1067 }
1068
1069 t.Logf("Retrieved %d NIP-43 members", len(members))
1070 }
1071
1072 // TestNIP43InviteCode tests invite code functionality
1073 func TestNIP43InviteCode(t *testing.T) {
1074 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1075 defer cancel()
1076
1077 db, err := New(ctx, cancel, "/tmp/test", "info")
1078 if err != nil {
1079 t.Fatalf("Failed to create database: %v", err)
1080 }
1081 defer db.Close()
1082
1083 <-db.Ready()
1084
1085 // Wipe to ensure clean state
1086 if err := db.Wipe(); err != nil {
1087 t.Fatalf("Failed to wipe database: %v", err)
1088 }
1089
1090 // Store a valid invite code (expires in 24 hours)
1091 validCode := "VALID-CODE-ABC"
1092 expiresAt := time.Now().Add(24 * time.Hour)
1093 if err := db.StoreInviteCode(validCode, expiresAt); err != nil {
1094 t.Fatalf("Failed to store invite code: %v", err)
1095 }
1096
1097 // Validate the code
1098 valid, err := db.ValidateInviteCode(validCode)
1099 if err != nil {
1100 t.Fatalf("Failed to validate invite code: %v", err)
1101 }
1102 if !valid {
1103 t.Error("Expected valid invite code to be valid")
1104 }
1105
1106 // Check non-existent code
1107 valid, err = db.ValidateInviteCode("NONEXISTENT")
1108 if err != nil {
1109 t.Fatalf("Unexpected error: %v", err)
1110 }
1111 if valid {
1112 t.Error("Expected non-existent code to be invalid")
1113 }
1114
1115 // Delete the code
1116 if err := db.DeleteInviteCode(validCode); err != nil {
1117 t.Fatalf("Failed to delete invite code: %v", err)
1118 }
1119
1120 // Should now be invalid
1121 valid, err = db.ValidateInviteCode(validCode)
1122 if err != nil {
1123 t.Fatalf("Unexpected error: %v", err)
1124 }
1125 if valid {
1126 t.Error("Expected deleted code to be invalid")
1127 }
1128
1129 t.Log("NIP-43 invite code test passed")
1130 }
1131
1132 // TestNIP43ExpiredInviteCode tests that expired invite codes are invalid
1133 func TestNIP43ExpiredInviteCode(t *testing.T) {
1134 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1135 defer cancel()
1136
1137 db, err := New(ctx, cancel, "/tmp/test", "info")
1138 if err != nil {
1139 t.Fatalf("Failed to create database: %v", err)
1140 }
1141 defer db.Close()
1142
1143 <-db.Ready()
1144
1145 // Wipe to ensure clean state
1146 if err := db.Wipe(); err != nil {
1147 t.Fatalf("Failed to wipe database: %v", err)
1148 }
1149
1150 // Store an expired invite code (expired 1 hour ago)
1151 expiredCode := "EXPIRED-CODE-XYZ"
1152 expiresAt := time.Now().Add(-1 * time.Hour)
1153 if err := db.StoreInviteCode(expiredCode, expiresAt); err != nil {
1154 t.Fatalf("Failed to store invite code: %v", err)
1155 }
1156
1157 // Validate the expired code
1158 valid, err := db.ValidateInviteCode(expiredCode)
1159 if err != nil {
1160 t.Fatalf("Unexpected error: %v", err)
1161 }
1162 if valid {
1163 t.Error("Expected expired invite code to be invalid")
1164 }
1165
1166 t.Log("Expired invite code correctly detected as invalid")
1167 }
1168
1169 // TestNIP43InvalidPubkeyLength tests that invalid pubkey lengths are rejected
1170 func TestNIP43InvalidPubkeyLength(t *testing.T) {
1171 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1172 defer cancel()
1173
1174 db, err := New(ctx, cancel, "/tmp/test", "info")
1175 if err != nil {
1176 t.Fatalf("Failed to create database: %v", err)
1177 }
1178 defer db.Close()
1179
1180 <-db.Ready()
1181
1182 // Try to add a member with invalid pubkey length
1183 shortPubkey := make([]byte, 16) // Should be 32
1184 err = db.AddNIP43Member(shortPubkey, "test")
1185 if err == nil {
1186 t.Error("Expected error for invalid pubkey length")
1187 }
1188
1189 t.Logf("Correctly rejected invalid pubkey length: %v", err)
1190 }
1191
1192 // ============================================================================
1193 // Subscription Management Tests
1194 // ============================================================================
1195
1196 // TestSubscriptionExtend tests extending subscriptions
1197 func TestSubscriptionExtend(t *testing.T) {
1198 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1199 defer cancel()
1200
1201 db, err := New(ctx, cancel, "/tmp/test", "info")
1202 if err != nil {
1203 t.Fatalf("Failed to create database: %v", err)
1204 }
1205 defer db.Close()
1206
1207 <-db.Ready()
1208
1209 // Wipe to ensure clean state
1210 if err := db.Wipe(); err != nil {
1211 t.Fatalf("Failed to wipe database: %v", err)
1212 }
1213
1214 pubkey := make([]byte, 32)
1215 for i := range pubkey {
1216 pubkey[i] = byte(i + 50)
1217 }
1218
1219 // Initially no subscription
1220 sub, err := db.GetSubscription(pubkey)
1221 if err != nil {
1222 t.Fatalf("Failed to get subscription: %v", err)
1223 }
1224 if sub != nil {
1225 t.Error("Expected nil subscription initially")
1226 }
1227
1228 // Extend subscription by 30 days
1229 if err := db.ExtendSubscription(pubkey, 30); err != nil {
1230 t.Fatalf("Failed to extend subscription: %v", err)
1231 }
1232
1233 // Should now have a subscription
1234 sub, err = db.GetSubscription(pubkey)
1235 if err != nil {
1236 t.Fatalf("Failed to get subscription: %v", err)
1237 }
1238 if sub == nil {
1239 t.Fatal("Expected subscription after extension")
1240 }
1241
1242 // Verify paid until is in the future
1243 if sub.PaidUntil.Before(time.Now()) {
1244 t.Error("PaidUntil should be in the future")
1245 }
1246
1247 // Extend again
1248 if err := db.ExtendSubscription(pubkey, 15); err != nil {
1249 t.Fatalf("Failed to extend subscription again: %v", err)
1250 }
1251
1252 sub2, err := db.GetSubscription(pubkey)
1253 if err != nil {
1254 t.Fatalf("Failed to get subscription: %v", err)
1255 }
1256
1257 // Second extension should add to first
1258 if !sub2.PaidUntil.After(sub.PaidUntil) {
1259 t.Error("Expected PaidUntil to increase after second extension")
1260 }
1261
1262 t.Log("Subscription extension test passed")
1263 }
1264
1265 // TestSubscriptionActive tests checking if subscription is active
1266 func TestSubscriptionActive(t *testing.T) {
1267 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1268 defer cancel()
1269
1270 db, err := New(ctx, cancel, "/tmp/test", "info")
1271 if err != nil {
1272 t.Fatalf("Failed to create database: %v", err)
1273 }
1274 defer db.Close()
1275
1276 <-db.Ready()
1277
1278 // Wipe to ensure clean state
1279 if err := db.Wipe(); err != nil {
1280 t.Fatalf("Failed to wipe database: %v", err)
1281 }
1282
1283 pubkey := make([]byte, 32)
1284 for i := range pubkey {
1285 pubkey[i] = byte(i + 60)
1286 }
1287
1288 // First check creates a trial subscription
1289 active, err := db.IsSubscriptionActive(pubkey)
1290 if err != nil {
1291 t.Fatalf("Failed to check subscription: %v", err)
1292 }
1293 if !active {
1294 t.Error("Expected new user to have active trial subscription")
1295 }
1296
1297 // Second check should still be active
1298 active, err = db.IsSubscriptionActive(pubkey)
1299 if err != nil {
1300 t.Fatalf("Failed to check subscription: %v", err)
1301 }
1302 if !active {
1303 t.Error("Expected subscription to remain active")
1304 }
1305
1306 t.Log("Subscription active check passed")
1307 }
1308
1309 // TestBlossomSubscription tests blossom storage subscription
1310 func TestBlossomSubscription(t *testing.T) {
1311 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1312 defer cancel()
1313
1314 db, err := New(ctx, cancel, "/tmp/test", "info")
1315 if err != nil {
1316 t.Fatalf("Failed to create database: %v", err)
1317 }
1318 defer db.Close()
1319
1320 <-db.Ready()
1321
1322 // Wipe to ensure clean state
1323 if err := db.Wipe(); err != nil {
1324 t.Fatalf("Failed to wipe database: %v", err)
1325 }
1326
1327 pubkey := make([]byte, 32)
1328 for i := range pubkey {
1329 pubkey[i] = byte(i + 70)
1330 }
1331
1332 // Initially no quota
1333 quota, err := db.GetBlossomStorageQuota(pubkey)
1334 if err != nil {
1335 t.Fatalf("Failed to get quota: %v", err)
1336 }
1337 if quota != 0 {
1338 t.Error("Expected zero quota initially")
1339 }
1340
1341 // Add blossom subscription
1342 if err := db.ExtendBlossomSubscription(pubkey, "premium", 1024, 30); err != nil {
1343 t.Fatalf("Failed to extend blossom subscription: %v", err)
1344 }
1345
1346 // Check quota
1347 quota, err = db.GetBlossomStorageQuota(pubkey)
1348 if err != nil {
1349 t.Fatalf("Failed to get quota: %v", err)
1350 }
1351 if quota != 1024 {
1352 t.Errorf("Expected quota of 1024, got %d", quota)
1353 }
1354
1355 // Check subscription details
1356 sub, err := db.GetSubscription(pubkey)
1357 if err != nil {
1358 t.Fatalf("Failed to get subscription: %v", err)
1359 }
1360 if sub.BlossomLevel != "premium" {
1361 t.Errorf("Expected premium level, got %s", sub.BlossomLevel)
1362 }
1363
1364 t.Log("Blossom subscription test passed")
1365 }
1366
1367 // TestPaymentHistory tests recording and retrieving payment history
1368 func TestPaymentHistory(t *testing.T) {
1369 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1370 defer cancel()
1371
1372 db, err := New(ctx, cancel, "/tmp/test", "info")
1373 if err != nil {
1374 t.Fatalf("Failed to create database: %v", err)
1375 }
1376 defer db.Close()
1377
1378 <-db.Ready()
1379
1380 // Wipe to ensure clean state
1381 if err := db.Wipe(); err != nil {
1382 t.Fatalf("Failed to wipe database: %v", err)
1383 }
1384
1385 pubkey := make([]byte, 32)
1386 for i := range pubkey {
1387 pubkey[i] = byte(i + 80)
1388 }
1389
1390 // Initially no payment history
1391 payments, err := db.GetPaymentHistory(pubkey)
1392 if err != nil {
1393 t.Fatalf("Failed to get payment history: %v", err)
1394 }
1395 if len(payments) != 0 {
1396 t.Error("Expected empty payment history initially")
1397 }
1398
1399 // Record a payment
1400 if err := db.RecordPayment(pubkey, 10000, "lnbc100...", "preimage123"); err != nil {
1401 t.Fatalf("Failed to record payment: %v", err)
1402 }
1403
1404 // Small delay to ensure different timestamps
1405 time.Sleep(10 * time.Millisecond)
1406
1407 // Record another payment
1408 if err := db.RecordPayment(pubkey, 20000, "lnbc200...", "preimage456"); err != nil {
1409 t.Fatalf("Failed to record payment: %v", err)
1410 }
1411
1412 // Check payment history
1413 payments, err = db.GetPaymentHistory(pubkey)
1414 if err != nil {
1415 t.Fatalf("Failed to get payment history: %v", err)
1416 }
1417 if len(payments) != 2 {
1418 t.Errorf("Expected 2 payments, got %d", len(payments))
1419 }
1420
1421 t.Logf("Retrieved %d payments from history", len(payments))
1422 }
1423
1424 // TestIsFirstTimeUser tests first-time user detection
1425 func TestIsFirstTimeUser(t *testing.T) {
1426 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1427 defer cancel()
1428
1429 db, err := New(ctx, cancel, "/tmp/test", "info")
1430 if err != nil {
1431 t.Fatalf("Failed to create database: %v", err)
1432 }
1433 defer db.Close()
1434
1435 <-db.Ready()
1436
1437 // Wipe to ensure clean state
1438 if err := db.Wipe(); err != nil {
1439 t.Fatalf("Failed to wipe database: %v", err)
1440 }
1441
1442 pubkey := make([]byte, 32)
1443 for i := range pubkey {
1444 pubkey[i] = byte(i + 90)
1445 }
1446
1447 // First check should be true
1448 isFirst, err := db.IsFirstTimeUser(pubkey)
1449 if err != nil {
1450 t.Fatalf("Failed to check first time user: %v", err)
1451 }
1452 if !isFirst {
1453 t.Error("Expected true for first check")
1454 }
1455
1456 // Second check should be false
1457 isFirst, err = db.IsFirstTimeUser(pubkey)
1458 if err != nil {
1459 t.Fatalf("Failed to check first time user: %v", err)
1460 }
1461 if isFirst {
1462 t.Error("Expected false for second check")
1463 }
1464
1465 t.Log("First time user detection test passed")
1466 }
1467
1468 // TestSubscriptionInvalidDays tests that invalid day counts are rejected
1469 func TestSubscriptionInvalidDays(t *testing.T) {
1470 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1471 defer cancel()
1472
1473 db, err := New(ctx, cancel, "/tmp/test", "info")
1474 if err != nil {
1475 t.Fatalf("Failed to create database: %v", err)
1476 }
1477 defer db.Close()
1478
1479 <-db.Ready()
1480
1481 pubkey := make([]byte, 32)
1482
1483 // Try to extend with 0 days
1484 err = db.ExtendSubscription(pubkey, 0)
1485 if err == nil {
1486 t.Error("Expected error for 0 days")
1487 }
1488
1489 // Try to extend with negative days
1490 err = db.ExtendSubscription(pubkey, -5)
1491 if err == nil {
1492 t.Error("Expected error for negative days")
1493 }
1494
1495 t.Log("Invalid days correctly rejected")
1496 }
1497
1498 // ============================================================================
1499 // Import/Export Tests
1500 // ============================================================================
1501
1502 // TestImportExport tests basic import/export functionality
1503 func TestImportExport(t *testing.T) {
1504 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1505 defer cancel()
1506
1507 db, err := New(ctx, cancel, "/tmp/test", "info")
1508 if err != nil {
1509 t.Fatalf("Failed to create database: %v", err)
1510 }
1511 defer db.Close()
1512
1513 <-db.Ready()
1514
1515 // Wipe to ensure clean state
1516 if err := db.Wipe(); err != nil {
1517 t.Fatalf("Failed to wipe database: %v", err)
1518 }
1519
1520 // Create and save some events
1521 for i := 0; i < 3; i++ {
1522 ev := createTestEvent(1, "Export test event", nil)
1523 ev.ID[0] = byte(0xAA + i)
1524 if _, err := db.SaveEvent(ctx, ev); err != nil {
1525 t.Fatalf("Failed to save event: %v", err)
1526 }
1527 }
1528
1529 // Export to buffer
1530 var exportBuf bytes.Buffer
1531 db.Export(ctx, &exportBuf)
1532
1533 exportData := exportBuf.String()
1534 if len(exportData) == 0 {
1535 t.Error("Expected non-empty export data")
1536 }
1537
1538 // Count lines (should be 3 JSONL lines)
1539 lines := bytes.Count(exportBuf.Bytes(), []byte("\n"))
1540 if lines != 3 {
1541 t.Errorf("Expected 3 export lines, got %d", lines)
1542 }
1543
1544 t.Logf("Exported %d events to %d bytes", lines, len(exportData))
1545 }
1546
1547 // TestImportFromReader tests importing events from JSONL reader
1548 func TestImportFromReader(t *testing.T) {
1549 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1550 defer cancel()
1551
1552 db, err := New(ctx, cancel, "/tmp/test", "info")
1553 if err != nil {
1554 t.Fatalf("Failed to create database: %v", err)
1555 }
1556 defer db.Close()
1557
1558 <-db.Ready()
1559
1560 // Wipe to ensure clean state
1561 if err := db.Wipe(); err != nil {
1562 t.Fatalf("Failed to wipe database: %v", err)
1563 }
1564
1565 // Create JSONL import data
1566 // Note: These are simplified test events - real events would have valid signatures
1567 jsonl := `{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pubkey":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","created_at":1700000000,"kind":1,"tags":[],"content":"Test event 1","sig":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"}
1568 {"id":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd","pubkey":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","created_at":1700000001,"kind":1,"tags":[],"content":"Test event 2","sig":"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}
1569 `
1570
1571 reader := bytes.NewReader([]byte(jsonl))
1572 err = db.ImportEventsFromReader(ctx, reader)
1573 if err != nil {
1574 t.Fatalf("Failed to import events: %v", err)
1575 }
1576
1577 t.Log("Import from reader test completed")
1578 }
1579
1580 // TestImportExportRoundTrip tests full round-trip import/export
1581 func TestImportExportRoundTrip(t *testing.T) {
1582 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1583 defer cancel()
1584
1585 // Create first database
1586 db1, err := New(ctx, cancel, "/tmp/test", "info")
1587 if err != nil {
1588 t.Fatalf("Failed to create database 1: %v", err)
1589 }
1590 defer db1.Close()
1591
1592 <-db1.Ready()
1593
1594 // Wipe to ensure clean state
1595 if err := db1.Wipe(); err != nil {
1596 t.Fatalf("Failed to wipe database: %v", err)
1597 }
1598
1599 // Create events with specific content for verification
1600 // Using kinds 1, 7, 10, 20, 30 to avoid kind 3 which requires p tags
1601 kinds := []uint16{1, 7, 10, 20, 30}
1602 originalEvents := make([]*event.E, 5)
1603 for i := 0; i < 5; i++ {
1604 ev := createTestEvent(kinds[i], "Round trip content "+string(rune('A'+i)), nil)
1605 ev.ID[0] = byte(0xBB + i)
1606 originalEvents[i] = ev
1607 if _, err := db1.SaveEvent(ctx, ev); err != nil {
1608 t.Fatalf("Failed to save event: %v", err)
1609 }
1610 }
1611
1612 // Export from db1
1613 var exportBuf bytes.Buffer
1614 db1.Export(ctx, &exportBuf)
1615
1616 // Wipe db1 (simulating a fresh database)
1617 if err := db1.Wipe(); err != nil {
1618 t.Fatalf("Failed to wipe database: %v", err)
1619 }
1620
1621 // Import back
1622 db1.Import(&exportBuf)
1623
1624 // Query all events
1625 f := &filter.F{}
1626 evs, err := db1.QueryEvents(ctx, f)
1627 if err != nil {
1628 t.Fatalf("Failed to query events: %v", err)
1629 }
1630
1631 if len(evs) < 5 {
1632 t.Errorf("Expected at least 5 events after round trip, got %d", len(evs))
1633 }
1634
1635 t.Logf("Round trip test: %d events survived", len(evs))
1636 }
1637
1638 // TestGetSerialsByPubkey tests retrieving serials by pubkey
1639 func TestGetSerialsByPubkey(t *testing.T) {
1640 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1641 defer cancel()
1642
1643 db, err := New(ctx, cancel, "/tmp/test", "info")
1644 if err != nil {
1645 t.Fatalf("Failed to create database: %v", err)
1646 }
1647 defer db.Close()
1648
1649 <-db.Ready()
1650
1651 // Wipe to ensure clean state
1652 if err := db.Wipe(); err != nil {
1653 t.Fatalf("Failed to wipe database: %v", err)
1654 }
1655
1656 // Create a specific author
1657 author := make([]byte, 32)
1658 for i := range author {
1659 author[i] = 0xCC
1660 }
1661
1662 // Create events from this author
1663 for i := 0; i < 3; i++ {
1664 ev := createTestEvent(1, "Author test", author)
1665 ev.ID[0] = byte(0xCC + i)
1666 if _, err := db.SaveEvent(ctx, ev); err != nil {
1667 t.Fatalf("Failed to save event: %v", err)
1668 }
1669 }
1670
1671 // Get serials by pubkey
1672 serials, err := db.GetSerialsByPubkey(author)
1673 if err != nil {
1674 t.Fatalf("Failed to get serials by pubkey: %v", err)
1675 }
1676
1677 if len(serials) != 3 {
1678 t.Errorf("Expected 3 serials, got %d", len(serials))
1679 }
1680
1681 t.Logf("GetSerialsByPubkey returned %d serials", len(serials))
1682 }
1683
1684 // TestExportByPubkey tests exporting events filtered by pubkey
1685 func TestExportByPubkey(t *testing.T) {
1686 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
1687 defer cancel()
1688
1689 db, err := New(ctx, cancel, "/tmp/test", "info")
1690 if err != nil {
1691 t.Fatalf("Failed to create database: %v", err)
1692 }
1693 defer db.Close()
1694
1695 <-db.Ready()
1696
1697 // Wipe to ensure clean state
1698 if err := db.Wipe(); err != nil {
1699 t.Fatalf("Failed to wipe database: %v", err)
1700 }
1701
1702 // Create two different authors
1703 author1 := make([]byte, 32)
1704 for i := range author1 {
1705 author1[i] = 0xDD
1706 }
1707 author2 := make([]byte, 32)
1708 for i := range author2 {
1709 author2[i] = 0xEE
1710 }
1711
1712 // Create events from both authors
1713 for i := 0; i < 2; i++ {
1714 ev := createTestEvent(1, "Author 1 content", author1)
1715 ev.ID[0] = byte(0xD0 + i)
1716 if _, err := db.SaveEvent(ctx, ev); err != nil {
1717 t.Fatalf("Failed to save event: %v", err)
1718 }
1719 }
1720 for i := 0; i < 3; i++ {
1721 ev := createTestEvent(1, "Author 2 content", author2)
1722 ev.ID[0] = byte(0xE0 + i)
1723 if _, err := db.SaveEvent(ctx, ev); err != nil {
1724 t.Fatalf("Failed to save event: %v", err)
1725 }
1726 }
1727
1728 // Export only author1's events
1729 var exportBuf bytes.Buffer
1730 db.Export(ctx, &exportBuf, author1)
1731
1732 lines := bytes.Count(exportBuf.Bytes(), []byte("\n"))
1733 if lines != 2 {
1734 t.Errorf("Expected 2 export lines for author1, got %d", lines)
1735 }
1736
1737 t.Logf("Exported %d events for specific pubkey", lines)
1738 }
1739
1740