query_events_test.go raw
1 //go:build integration
2 // +build integration
3
4 // NOTE: This file requires updates to match the current nostr library types.
5 // The filter/tag/kind types have changed since this test was written.
6
7 package neo4j
8
9 import (
10 "context"
11 "testing"
12
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 )
18
19 // Valid test pubkeys and event IDs (64-character lowercase hex)
20 const (
21 validPubkey1 = "0000000000000000000000000000000000000000000000000000000000000001"
22 validPubkey2 = "0000000000000000000000000000000000000000000000000000000000000002"
23 validPubkey3 = "0000000000000000000000000000000000000000000000000000000000000003"
24 validEventID1 = "1111111111111111111111111111111111111111111111111111111111111111"
25 validEventID2 = "2222222222222222222222222222222222222222222222222222222222222222"
26 validEventID3 = "3333333333333333333333333333333333333333333333333333333333333333"
27 )
28
29 // TestQueryEventsWithNilFilter tests that QueryEvents handles nil filter fields gracefully
30 // This test covers the nil pointer fix in query-events.go
31 func TestQueryEventsWithNilFilter(t *testing.T) {
32 if testDB == nil {
33 t.Skip("Neo4j not available")
34 }
35
36 // Clean up before test
37 cleanTestDatabase()
38
39 // Setup some test events
40 setupTestEvent(t, validEventID1, validPubkey1, 1, "[]")
41 setupTestEvent(t, validEventID2, validPubkey2, 1, "[]")
42
43 ctx := context.Background()
44
45 // Test 1: Completely empty filter (all nil fields)
46 t.Run("EmptyFilter", func(t *testing.T) {
47 f := &filter.F{}
48 events, err := testDB.QueryEvents(ctx, f)
49 if err != nil {
50 t.Fatalf("QueryEvents with empty filter should not panic: %v", err)
51 }
52 if len(events) == 0 {
53 t.Error("Expected to find events with empty filter")
54 }
55 })
56
57 // Test 2: Filter with nil Ids
58 t.Run("NilIds", func(t *testing.T) {
59 f := &filter.F{
60 Ids: nil, // Explicitly nil
61 }
62 _, err := testDB.QueryEvents(ctx, f)
63 if err != nil {
64 t.Fatalf("QueryEvents with nil Ids should not panic: %v", err)
65 }
66 })
67
68 // Test 3: Filter with nil Authors
69 t.Run("NilAuthors", func(t *testing.T) {
70 f := &filter.F{
71 Authors: nil, // Explicitly nil
72 }
73 _, err := testDB.QueryEvents(ctx, f)
74 if err != nil {
75 t.Fatalf("QueryEvents with nil Authors should not panic: %v", err)
76 }
77 })
78
79 // Test 4: Filter with nil Kinds
80 t.Run("NilKinds", func(t *testing.T) {
81 f := &filter.F{
82 Kinds: nil, // Explicitly nil
83 }
84 _, err := testDB.QueryEvents(ctx, f)
85 if err != nil {
86 t.Fatalf("QueryEvents with nil Kinds should not panic: %v", err)
87 }
88 })
89
90 // Test 5: Filter with empty Ids (using tag with empty slice)
91 t.Run("EmptyIds", func(t *testing.T) {
92 f := &filter.F{
93 Ids: &tag.T{T: [][]byte{}},
94 }
95 _, err := testDB.QueryEvents(ctx, f)
96 if err != nil {
97 t.Fatalf("QueryEvents with empty Ids should not panic: %v", err)
98 }
99 })
100
101 // Test 6: Filter with empty Authors (using tag with empty slice)
102 t.Run("EmptyAuthors", func(t *testing.T) {
103 f := &filter.F{
104 Authors: &tag.T{T: [][]byte{}},
105 }
106 _, err := testDB.QueryEvents(ctx, f)
107 if err != nil {
108 t.Fatalf("QueryEvents with empty Authors should not panic: %v", err)
109 }
110 })
111
112 // Test 7: Filter with empty Kinds slice
113 t.Run("EmptyKinds", func(t *testing.T) {
114 f := &filter.F{
115 Kinds: kind.NewS(),
116 }
117 _, err := testDB.QueryEvents(ctx, f)
118 if err != nil {
119 t.Fatalf("QueryEvents with empty Kinds should not panic: %v", err)
120 }
121 })
122 }
123
124 // TestQueryEventsWithValidFilters tests that QueryEvents works correctly with valid filters
125 func TestQueryEventsWithValidFilters(t *testing.T) {
126 if testDB == nil {
127 t.Skip("Neo4j not available")
128 }
129
130 // Clean up before test
131 cleanTestDatabase()
132
133 // Setup test events
134 setupTestEvent(t, validEventID1, validPubkey1, 1, "[]")
135 setupTestEvent(t, validEventID2, validPubkey2, 3, "[]")
136 setupTestEvent(t, validEventID3, validPubkey1, 1, "[]")
137
138 ctx := context.Background()
139
140 // Test 1: Filter by ID
141 t.Run("FilterByID", func(t *testing.T) {
142 f := &filter.F{
143 Ids: tag.NewFromBytesSlice([]byte(validEventID1)),
144 }
145 events, err := testDB.QueryEvents(ctx, f)
146 if err != nil {
147 t.Fatalf("QueryEvents failed: %v", err)
148 }
149 if len(events) != 1 {
150 t.Errorf("Expected 1 event, got %d", len(events))
151 }
152 })
153
154 // Test 2: Filter by Author
155 t.Run("FilterByAuthor", func(t *testing.T) {
156 f := &filter.F{
157 Authors: tag.NewFromBytesSlice([]byte(validPubkey1)),
158 }
159 events, err := testDB.QueryEvents(ctx, f)
160 if err != nil {
161 t.Fatalf("QueryEvents failed: %v", err)
162 }
163 if len(events) != 2 {
164 t.Errorf("Expected 2 events from pubkey1, got %d", len(events))
165 }
166 })
167
168 // Test 3: Filter by Kind
169 t.Run("FilterByKind", func(t *testing.T) {
170 f := &filter.F{
171 Kinds: kind.NewS(kind.New(1)),
172 }
173 events, err := testDB.QueryEvents(ctx, f)
174 if err != nil {
175 t.Fatalf("QueryEvents failed: %v", err)
176 }
177 if len(events) != 2 {
178 t.Errorf("Expected 2 kind-1 events, got %d", len(events))
179 }
180 })
181
182 // Test 4: Combined filters (kind + author)
183 t.Run("FilterByKindAndAuthor", func(t *testing.T) {
184 f := &filter.F{
185 Kinds: kind.NewS(kind.New(1)),
186 Authors: tag.NewFromBytesSlice([]byte(validPubkey1)),
187 }
188 events, err := testDB.QueryEvents(ctx, f)
189 if err != nil {
190 t.Fatalf("QueryEvents failed: %v", err)
191 }
192 if len(events) != 2 {
193 t.Errorf("Expected 2 kind-1 events from pubkey1, got %d", len(events))
194 }
195 })
196
197 // Test 5: Filter with limit
198 t.Run("FilterWithLimit", func(t *testing.T) {
199 limit := uint(1)
200 f := &filter.F{
201 Kinds: kind.NewS(kind.New(1)),
202 Limit: &limit,
203 }
204 events, err := testDB.QueryEvents(ctx, f)
205 if err != nil {
206 t.Fatalf("QueryEvents failed: %v", err)
207 }
208 if len(events) != 1 {
209 t.Errorf("Expected 1 event due to limit, got %d", len(events))
210 }
211 })
212 }
213
214 // TestBuildCypherQueryWithNilFields tests the buildCypherQuery function with nil fields
215 func TestBuildCypherQueryWithNilFields(t *testing.T) {
216 if testDB == nil {
217 t.Skip("Neo4j not available")
218 }
219
220 // Test that buildCypherQuery doesn't panic with nil fields
221 t.Run("AllNilFields", func(t *testing.T) {
222 f := &filter.F{
223 Ids: nil,
224 Authors: nil,
225 Kinds: nil,
226 Since: nil,
227 Until: nil,
228 Tags: nil,
229 Limit: nil,
230 }
231 cypher, params := testDB.buildCypherQuery(f, false)
232 if cypher == "" {
233 t.Error("Expected non-empty Cypher query")
234 }
235 if params == nil {
236 t.Error("Expected non-nil params map")
237 }
238 })
239
240 // Test with empty slices
241 t.Run("EmptySlices", func(t *testing.T) {
242 f := &filter.F{
243 Ids: &tag.T{T: [][]byte{}},
244 Authors: &tag.T{T: [][]byte{}},
245 Kinds: kind.NewS(),
246 }
247 cypher, params := testDB.buildCypherQuery(f, false)
248 if cypher == "" {
249 t.Error("Expected non-empty Cypher query")
250 }
251 if params == nil {
252 t.Error("Expected non-nil params map")
253 }
254 })
255
256 // Test with time filters
257 t.Run("TimeFilters", func(t *testing.T) {
258 since := timestamp.Now()
259 until := timestamp.Now()
260 f := &filter.F{
261 Since: since,
262 Until: until,
263 }
264 cypher, params := testDB.buildCypherQuery(f, false)
265 if _, ok := params["since"]; !ok {
266 t.Error("Expected 'since' param")
267 }
268 if _, ok := params["until"]; !ok {
269 t.Error("Expected 'until' param")
270 }
271 _ = cypher
272 })
273 }
274
275 // TestQueryEventsUppercaseHexNormalization tests that uppercase hex in filters is normalized
276 func TestQueryEventsUppercaseHexNormalization(t *testing.T) {
277 if testDB == nil {
278 t.Skip("Neo4j not available")
279 }
280
281 // Clean up before test
282 cleanTestDatabase()
283
284 // Setup test event with lowercase pubkey (as Neo4j stores)
285 lowercasePubkey := "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
286 lowercaseEventID := "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
287 setupTestEvent(t, lowercaseEventID, lowercasePubkey, 1, "[]")
288
289 ctx := context.Background()
290
291 // Test query with uppercase pubkey - should be normalized and still match
292 t.Run("UppercaseAuthor", func(t *testing.T) {
293 uppercasePubkey := "ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789"
294 f := &filter.F{
295 Authors: tag.NewFromBytesSlice([]byte(uppercasePubkey)),
296 }
297 events, err := testDB.QueryEvents(ctx, f)
298 if err != nil {
299 t.Fatalf("QueryEvents failed: %v", err)
300 }
301 if len(events) != 1 {
302 t.Errorf("Expected to find 1 event with uppercase pubkey filter, got %d", len(events))
303 }
304 })
305
306 // Test query with uppercase event ID - should be normalized and still match
307 t.Run("UppercaseEventID", func(t *testing.T) {
308 uppercaseEventID := "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210"
309 f := &filter.F{
310 Ids: tag.NewFromBytesSlice([]byte(uppercaseEventID)),
311 }
312 events, err := testDB.QueryEvents(ctx, f)
313 if err != nil {
314 t.Fatalf("QueryEvents failed: %v", err)
315 }
316 if len(events) != 1 {
317 t.Errorf("Expected to find 1 event with uppercase ID filter, got %d", len(events))
318 }
319 })
320 }
321