//go:build integration // +build integration // NOTE: This file requires updates to match the current nostr library types. // The filter/tag/kind types have changed since this test was written. package neo4j import ( "context" "testing" "git.mleku.dev/mleku/nostr/encoders/filter" "git.mleku.dev/mleku/nostr/encoders/kind" "git.mleku.dev/mleku/nostr/encoders/tag" "git.mleku.dev/mleku/nostr/encoders/timestamp" ) // Valid test pubkeys and event IDs (64-character lowercase hex) const ( validPubkey1 = "0000000000000000000000000000000000000000000000000000000000000001" validPubkey2 = "0000000000000000000000000000000000000000000000000000000000000002" validPubkey3 = "0000000000000000000000000000000000000000000000000000000000000003" validEventID1 = "1111111111111111111111111111111111111111111111111111111111111111" validEventID2 = "2222222222222222222222222222222222222222222222222222222222222222" validEventID3 = "3333333333333333333333333333333333333333333333333333333333333333" ) // TestQueryEventsWithNilFilter tests that QueryEvents handles nil filter fields gracefully // This test covers the nil pointer fix in query-events.go func TestQueryEventsWithNilFilter(t *testing.T) { if testDB == nil { t.Skip("Neo4j not available") } // Clean up before test cleanTestDatabase() // Setup some test events setupTestEvent(t, validEventID1, validPubkey1, 1, "[]") setupTestEvent(t, validEventID2, validPubkey2, 1, "[]") ctx := context.Background() // Test 1: Completely empty filter (all nil fields) t.Run("EmptyFilter", func(t *testing.T) { f := &filter.F{} events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with empty filter should not panic: %v", err) } if len(events) == 0 { t.Error("Expected to find events with empty filter") } }) // Test 2: Filter with nil Ids t.Run("NilIds", func(t *testing.T) { f := &filter.F{ Ids: nil, // Explicitly nil } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with nil Ids should not panic: %v", err) } }) // Test 3: Filter with nil Authors t.Run("NilAuthors", func(t *testing.T) { f := &filter.F{ Authors: nil, // Explicitly nil } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with nil Authors should not panic: %v", err) } }) // Test 4: Filter with nil Kinds t.Run("NilKinds", func(t *testing.T) { f := &filter.F{ Kinds: nil, // Explicitly nil } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with nil Kinds should not panic: %v", err) } }) // Test 5: Filter with empty Ids (using tag with empty slice) t.Run("EmptyIds", func(t *testing.T) { f := &filter.F{ Ids: &tag.T{T: [][]byte{}}, } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with empty Ids should not panic: %v", err) } }) // Test 6: Filter with empty Authors (using tag with empty slice) t.Run("EmptyAuthors", func(t *testing.T) { f := &filter.F{ Authors: &tag.T{T: [][]byte{}}, } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with empty Authors should not panic: %v", err) } }) // Test 7: Filter with empty Kinds slice t.Run("EmptyKinds", func(t *testing.T) { f := &filter.F{ Kinds: kind.NewS(), } _, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents with empty Kinds should not panic: %v", err) } }) } // TestQueryEventsWithValidFilters tests that QueryEvents works correctly with valid filters func TestQueryEventsWithValidFilters(t *testing.T) { if testDB == nil { t.Skip("Neo4j not available") } // Clean up before test cleanTestDatabase() // Setup test events setupTestEvent(t, validEventID1, validPubkey1, 1, "[]") setupTestEvent(t, validEventID2, validPubkey2, 3, "[]") setupTestEvent(t, validEventID3, validPubkey1, 1, "[]") ctx := context.Background() // Test 1: Filter by ID t.Run("FilterByID", func(t *testing.T) { f := &filter.F{ Ids: tag.NewFromBytesSlice([]byte(validEventID1)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 1 { t.Errorf("Expected 1 event, got %d", len(events)) } }) // Test 2: Filter by Author t.Run("FilterByAuthor", func(t *testing.T) { f := &filter.F{ Authors: tag.NewFromBytesSlice([]byte(validPubkey1)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 2 { t.Errorf("Expected 2 events from pubkey1, got %d", len(events)) } }) // Test 3: Filter by Kind t.Run("FilterByKind", func(t *testing.T) { f := &filter.F{ Kinds: kind.NewS(kind.New(1)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 2 { t.Errorf("Expected 2 kind-1 events, got %d", len(events)) } }) // Test 4: Combined filters (kind + author) t.Run("FilterByKindAndAuthor", func(t *testing.T) { f := &filter.F{ Kinds: kind.NewS(kind.New(1)), Authors: tag.NewFromBytesSlice([]byte(validPubkey1)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 2 { t.Errorf("Expected 2 kind-1 events from pubkey1, got %d", len(events)) } }) // Test 5: Filter with limit t.Run("FilterWithLimit", func(t *testing.T) { limit := uint(1) f := &filter.F{ Kinds: kind.NewS(kind.New(1)), Limit: &limit, } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 1 { t.Errorf("Expected 1 event due to limit, got %d", len(events)) } }) } // TestBuildCypherQueryWithNilFields tests the buildCypherQuery function with nil fields func TestBuildCypherQueryWithNilFields(t *testing.T) { if testDB == nil { t.Skip("Neo4j not available") } // Test that buildCypherQuery doesn't panic with nil fields t.Run("AllNilFields", func(t *testing.T) { f := &filter.F{ Ids: nil, Authors: nil, Kinds: nil, Since: nil, Until: nil, Tags: nil, Limit: nil, } cypher, params := testDB.buildCypherQuery(f, false) if cypher == "" { t.Error("Expected non-empty Cypher query") } if params == nil { t.Error("Expected non-nil params map") } }) // Test with empty slices t.Run("EmptySlices", func(t *testing.T) { f := &filter.F{ Ids: &tag.T{T: [][]byte{}}, Authors: &tag.T{T: [][]byte{}}, Kinds: kind.NewS(), } cypher, params := testDB.buildCypherQuery(f, false) if cypher == "" { t.Error("Expected non-empty Cypher query") } if params == nil { t.Error("Expected non-nil params map") } }) // Test with time filters t.Run("TimeFilters", func(t *testing.T) { since := timestamp.Now() until := timestamp.Now() f := &filter.F{ Since: since, Until: until, } cypher, params := testDB.buildCypherQuery(f, false) if _, ok := params["since"]; !ok { t.Error("Expected 'since' param") } if _, ok := params["until"]; !ok { t.Error("Expected 'until' param") } _ = cypher }) } // TestQueryEventsUppercaseHexNormalization tests that uppercase hex in filters is normalized func TestQueryEventsUppercaseHexNormalization(t *testing.T) { if testDB == nil { t.Skip("Neo4j not available") } // Clean up before test cleanTestDatabase() // Setup test event with lowercase pubkey (as Neo4j stores) lowercasePubkey := "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" lowercaseEventID := "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210" setupTestEvent(t, lowercaseEventID, lowercasePubkey, 1, "[]") ctx := context.Background() // Test query with uppercase pubkey - should be normalized and still match t.Run("UppercaseAuthor", func(t *testing.T) { uppercasePubkey := "ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789" f := &filter.F{ Authors: tag.NewFromBytesSlice([]byte(uppercasePubkey)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 1 { t.Errorf("Expected to find 1 event with uppercase pubkey filter, got %d", len(events)) } }) // Test query with uppercase event ID - should be normalized and still match t.Run("UppercaseEventID", func(t *testing.T) { uppercaseEventID := "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210" f := &filter.F{ Ids: tag.NewFromBytesSlice([]byte(uppercaseEventID)), } events, err := testDB.QueryEvents(ctx, f) if err != nil { t.Fatalf("QueryEvents failed: %v", err) } if len(events) != 1 { t.Errorf("Expected to find 1 event with uppercase ID filter, got %d", len(events)) } }) }