You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
8.6 KiB
320 lines
8.6 KiB
//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)) |
|
} |
|
}) |
|
}
|
|
|