query-for-serials_test.go raw
1 package database
2
3 import (
4 "bufio"
5 "bytes"
6 "context"
7 "os"
8 "sort"
9 "testing"
10
11 "next.orly.dev/pkg/nostr/encoders/event"
12 "next.orly.dev/pkg/nostr/encoders/event/examples"
13 "next.orly.dev/pkg/nostr/encoders/filter"
14 "next.orly.dev/pkg/nostr/encoders/kind"
15 "next.orly.dev/pkg/nostr/encoders/tag"
16 "next.orly.dev/pkg/nostr/encoders/timestamp"
17 "next.orly.dev/pkg/lol/chk"
18 "next.orly.dev/pkg/database/indexes/types"
19 "next.orly.dev/pkg/utils"
20 )
21
22 func TestQueryForSerials(t *testing.T) {
23 // Create a temporary directory for the database
24 tempDir, err := os.MkdirTemp("", "test-db-*")
25 if err != nil {
26 t.Fatalf("Failed to create temporary directory: %v", err)
27 }
28 defer os.RemoveAll(tempDir) // Clean up after the test
29
30 // Create a context and cancel function for the database
31 ctx, cancel := context.WithCancel(context.Background())
32 defer cancel()
33
34 // Initialize the database
35 db, err := New(ctx, cancel, tempDir, "info")
36 if err != nil {
37 t.Fatalf("Failed to create database: %v", err)
38 }
39 defer db.Close()
40
41 // Create a scanner to read events from examples.Cache
42 scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
43 scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
44
45 // Count the number of events processed
46 eventCount := 0
47
48 var events []*event.E
49 var eventSerials = make(map[string]*types.Uint40) // Map event ID (hex) to serial
50
51 // First, collect all events from examples.Cache
52 for scanner.Scan() {
53 chk.E(scanner.Err())
54 b := scanner.Bytes()
55 ev := event.New()
56
57 // Unmarshal the event
58 if _, err = ev.Unmarshal(b); chk.E(err) {
59 ev.Free()
60 t.Fatal(err)
61 }
62
63 events = append(events, ev)
64 }
65
66 // Check for scanner errors
67 if err = scanner.Err(); err != nil {
68 t.Fatalf("Scanner error: %v", err)
69 }
70
71 // Sort events by CreatedAt to ensure addressable events are processed in chronological order
72 sort.Slice(events, func(i, j int) bool {
73 return events[i].CreatedAt < events[j].CreatedAt
74 })
75
76 // Count the number of events processed
77 eventCount = 0
78 skippedCount := 0
79
80 // Now process each event in chronological order
81 for _, ev := range events {
82 // Save the event to the database
83 if _, err = db.SaveEvent(ctx, ev); err != nil {
84 // Skip events that fail validation (e.g., kind 3 without p tags)
85 skippedCount++
86 continue
87 }
88
89 // Get the serial for this event
90 serial, err := db.GetSerialById(ev.ID)
91 if err != nil {
92 t.Fatalf(
93 "Failed to get serial for event #%d: %v", eventCount+1, err,
94 )
95 }
96
97 if serial != nil {
98 eventSerials[string(ev.ID)] = serial
99 }
100
101 eventCount++
102 }
103
104 t.Logf("Successfully saved %d events to the database (skipped %d invalid events)", eventCount, skippedCount)
105
106 // Test QueryForSerials with an ID filter
107 testEvent := events[3] // Using the same event as in other tests
108
109 serials, err := db.QueryForSerials(
110 ctx, &filter.F{
111 Ids: tag.NewFromBytesSlice(testEvent.ID),
112 },
113 )
114 if err != nil {
115 t.Fatalf("Failed to query serials by ID: %v", err)
116 }
117
118 // Verify we got exactly one serial
119 if len(serials) != 1 {
120 t.Fatalf("Expected 1 serial, got %d", len(serials))
121 }
122
123 // Verify the serial corresponds to the correct event
124 // Fetch the event using the serial
125 ev, err := db.FetchEventBySerial(serials[0])
126 if err != nil {
127 t.Fatalf("Failed to fetch event for serial: %v", err)
128 }
129
130 if !utils.FastEqual(ev.ID, testEvent.ID) {
131 t.Fatalf(
132 "Event ID doesn't match. Got %x, expected %x",
133 ev.ID, testEvent.ID,
134 )
135 }
136
137 // Test querying by kind
138 testKind := kind.New(1) // Kind 1 is typically text notes
139 kindFilter := kind.NewS(testKind)
140
141 serials, err = db.QueryForSerials(
142 ctx, &filter.F{
143 Kinds: kindFilter,
144 },
145 )
146 if err != nil {
147 t.Fatalf("Failed to query serials by kind: %v", err)
148 }
149
150 // Verify we got results
151 if len(serials) == 0 {
152 t.Fatal("Expected serials for events with kind 1, but got none")
153 }
154
155 // Verify the serials correspond to events with the correct kind
156 for i, serial := range serials {
157 // Fetch the event using the serial
158 ev, err := db.FetchEventBySerial(serial)
159 if err != nil {
160 t.Fatalf("Failed to fetch event for serial %d: %v", i, err)
161 }
162
163 if ev.Kind != testKind.K {
164 t.Fatalf(
165 "Event %d has incorrect kind. Got %d, expected %d",
166 i, ev.Kind, testKind.K,
167 )
168 }
169 }
170
171 // Test querying by author
172 authorFilter := tag.NewFromBytesSlice(events[1].Pubkey)
173
174 serials, err = db.QueryForSerials(
175 ctx, &filter.F{
176 Authors: authorFilter,
177 },
178 )
179 if err != nil {
180 t.Fatalf("Failed to query serials by author: %v", err)
181 }
182
183 // Verify we got results
184 if len(serials) == 0 {
185 t.Fatal("Expected serials for events from author, but got none")
186 }
187
188 // Verify the serials correspond to events with the correct author
189 for i, serial := range serials {
190 // Fetch the event using the serial
191 ev, err := db.FetchEventBySerial(serial)
192 if err != nil {
193 t.Fatalf("Failed to fetch event for serial %d: %v", i, err)
194 }
195
196 if !utils.FastEqual(ev.Pubkey, events[1].Pubkey) {
197 t.Fatalf(
198 "Event %d has incorrect author. Got %x, expected %x",
199 i, ev.Pubkey, events[1].Pubkey,
200 )
201 }
202 }
203
204 // Test querying by time range
205 // Use the timestamp from the middle event as a reference
206 middleIndex := len(events) / 2
207 middleEvent := events[middleIndex]
208
209 // Create a timestamp range that includes events before and after the middle event
210 sinceTime := new(timestamp.T)
211 sinceTime.V = middleEvent.CreatedAt - 3600 // 1 hour before middle event
212
213 untilTime := new(timestamp.T)
214 untilTime.V = middleEvent.CreatedAt + 3600 // 1 hour after middle event
215
216 serials, err = db.QueryForSerials(
217 ctx, &filter.F{
218 Since: sinceTime,
219 Until: untilTime,
220 },
221 )
222 if err != nil {
223 t.Fatalf("Failed to query serials by time range: %v", err)
224 }
225
226 // Verify we got results
227 if len(serials) == 0 {
228 t.Fatal("Expected serials for events in time range, but got none")
229 }
230
231 // Verify the serials correspond to events within the time range
232 for i, serial := range serials {
233 // Fetch the event using the serial
234 ev, err := db.FetchEventBySerial(serial)
235 if err != nil {
236 t.Fatalf("Failed to fetch event for serial %d: %v", i, err)
237 }
238
239 if ev.CreatedAt < sinceTime.V || ev.CreatedAt > untilTime.V {
240 t.Fatalf(
241 "Event %d is outside the time range. Got %d, expected between %d and %d",
242 i, ev.CreatedAt, sinceTime.V, untilTime.V,
243 )
244 }
245 }
246 }
247