Files
next.orly.dev/pkg/neo4j/migrations_test.go
mleku 1e9c447fe6 Refactor Neo4j tests and improve tag handling in Cypher
Replaces outdated Neo4j test setup with a robust TestMain, shared test database, and utility functions for test data and migrations. Improves Cypher generation for processing e-tags, p-tags, and other tags to ensure compliance with Neo4j syntax. Added integration test script and updated benchmark reports for Badger backend.
2025-12-04 20:09:24 +00:00

303 lines
8.7 KiB
Go

package neo4j
import (
"context"
"testing"
)
// TestMigrationV2_CleanupBinaryEncodedValues tests that migration v2 properly
// cleans up binary-encoded pubkeys and event IDs
func TestMigrationV2_CleanupBinaryEncodedValues(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Create some valid NostrUser nodes (should NOT be deleted)
validPubkeys := []string{
"0000000000000000000000000000000000000000000000000000000000000001",
"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}
for _, pk := range validPubkeys {
setupInvalidNostrUser(t, pk) // Using setupInvalidNostrUser to create directly
}
// Create some invalid NostrUser nodes (should be deleted)
invalidPubkeys := []string{
"binary\x00garbage\x00data", // Binary garbage
"ABCDEF", // Too short
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", // Non-hex chars
string(append(make([]byte, 32), 0)), // 33-byte binary format
}
for _, pk := range invalidPubkeys {
setupInvalidNostrUser(t, pk)
}
// Verify invalid nodes exist before migration
invalidCountBefore := countInvalidNostrUsers(t)
if invalidCountBefore != 4 {
t.Errorf("Expected 4 invalid NostrUsers before migration, got %d", invalidCountBefore)
}
totalBefore := countNodes(t, "NostrUser")
if totalBefore != 6 {
t.Errorf("Expected 6 total NostrUsers before migration, got %d", totalBefore)
}
// Run the migration
err := migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("Migration failed: %v", err)
}
// Verify invalid nodes were deleted
invalidCountAfter := countInvalidNostrUsers(t)
if invalidCountAfter != 0 {
t.Errorf("Expected 0 invalid NostrUsers after migration, got %d", invalidCountAfter)
}
// Verify valid nodes were NOT deleted
totalAfter := countNodes(t, "NostrUser")
if totalAfter != 2 {
t.Errorf("Expected 2 valid NostrUsers after migration, got %d", totalAfter)
}
}
// TestMigrationV2_CleanupInvalidEvents tests that migration v2 properly
// cleans up Event nodes with invalid pubkeys or IDs
func TestMigrationV2_CleanupInvalidEvents(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Create valid events
validEventID := "1111111111111111111111111111111111111111111111111111111111111111"
validPubkey := "0000000000000000000000000000000000000000000000000000000000000001"
setupTestEvent(t, validEventID, validPubkey, 1, "[]")
// Create invalid events directly
setupInvalidEvent(t, "invalid_id", validPubkey) // Invalid ID
setupInvalidEvent(t, validEventID+"2", "invalid_pubkey") // Invalid pubkey (different ID to avoid duplicate)
setupInvalidEvent(t, "TOOSHORT", "binary\x00garbage") // Both invalid
// Count events before migration
eventsBefore := countNodes(t, "Event")
if eventsBefore != 4 {
t.Errorf("Expected 4 Events before migration, got %d", eventsBefore)
}
// Run the migration
err := migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("Migration failed: %v", err)
}
// Verify only valid event remains
eventsAfter := countNodes(t, "Event")
if eventsAfter != 1 {
t.Errorf("Expected 1 valid Event after migration, got %d", eventsAfter)
}
}
// TestMigrationV2_CleanupInvalidTags tests that migration v2 properly
// cleans up Tag nodes (e/p type) with invalid values
func TestMigrationV2_CleanupInvalidTags(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Create valid tags
validHex := "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
setupInvalidTag(t, "e", validHex) // Valid e-tag
setupInvalidTag(t, "p", validHex) // Valid p-tag
setupInvalidTag(t, "t", "topic") // Non e/p tag (should not be affected)
// Create invalid e/p tags
setupInvalidTag(t, "e", "binary\x00garbage") // Invalid e-tag
setupInvalidTag(t, "p", "TOOSHORT") // Invalid p-tag (too short)
setupInvalidTag(t, "e", string(append(make([]byte, 32), 0))) // Binary encoded
// Count tags before migration
tagsBefore := countNodes(t, "Tag")
if tagsBefore != 6 {
t.Errorf("Expected 6 Tags before migration, got %d", tagsBefore)
}
invalidBefore := countInvalidTags(t)
if invalidBefore != 3 {
t.Errorf("Expected 3 invalid e/p Tags before migration, got %d", invalidBefore)
}
// Run the migration
err := migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("Migration failed: %v", err)
}
// Verify invalid tags were deleted
invalidAfter := countInvalidTags(t)
if invalidAfter != 0 {
t.Errorf("Expected 0 invalid e/p Tags after migration, got %d", invalidAfter)
}
// Verify valid tags remain (2 e/p valid + 1 t-tag)
tagsAfter := countNodes(t, "Tag")
if tagsAfter != 3 {
t.Errorf("Expected 3 Tags after migration, got %d", tagsAfter)
}
}
// TestMigrationV2_Idempotent tests that migration v2 can be run multiple times safely
func TestMigrationV2_Idempotent(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Create only valid data
validPubkey := "0000000000000000000000000000000000000000000000000000000000000001"
validEventID := "1111111111111111111111111111111111111111111111111111111111111111"
setupTestEvent(t, validEventID, validPubkey, 1, "[]")
countBefore := countNodes(t, "Event")
// Run migration first time
err := migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("First migration run failed: %v", err)
}
countAfterFirst := countNodes(t, "Event")
if countAfterFirst != countBefore {
t.Errorf("First migration changed valid event count: before=%d, after=%d", countBefore, countAfterFirst)
}
// Run migration second time
err = migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("Second migration run failed: %v", err)
}
countAfterSecond := countNodes(t, "Event")
if countAfterSecond != countBefore {
t.Errorf("Second migration changed valid event count: before=%d, after=%d", countBefore, countAfterSecond)
}
}
// TestMigrationV2_NoDataDoesNotFail tests that migration v2 succeeds with empty database
func TestMigrationV2_NoDataDoesNotFail(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up completely
cleanTestDatabase()
ctx := context.Background()
// Run migration on empty database - should not fail
err := migrateBinaryToHex(ctx, testDB)
if err != nil {
t.Fatalf("Migration on empty database failed: %v", err)
}
}
// TestMigrationMarking tests that migrations are properly tracked
func TestMigrationMarking(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Verify migration v2 has not been applied
if testDB.migrationApplied(ctx, "v2") {
t.Error("Migration v2 should not be applied before test")
}
// Mark migration as complete
err := testDB.markMigrationComplete(ctx, "v2", "Test migration")
if err != nil {
t.Fatalf("Failed to mark migration complete: %v", err)
}
// Verify migration is now marked as applied
if !testDB.migrationApplied(ctx, "v2") {
t.Error("Migration v2 should be applied after marking")
}
// Clean up
cleanTestDatabase()
}
// TestMigrationV1_AuthorToNostrUserMerge tests the author migration
func TestMigrationV1_AuthorToNostrUserMerge(t *testing.T) {
if testDB == nil {
t.Skip("Neo4j not available")
}
// Clean up before test
cleanTestDatabase()
ctx := context.Background()
// Create some Author nodes (legacy format)
authorPubkeys := []string{
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000002",
}
for _, pk := range authorPubkeys {
cypher := `CREATE (a:Author {pubkey: $pubkey})`
_, err := testDB.ExecuteWrite(ctx, cypher, map[string]any{"pubkey": pk})
if err != nil {
t.Fatalf("Failed to create Author node: %v", err)
}
}
// Verify Author nodes exist
authorCount := countNodes(t, "Author")
if authorCount != 2 {
t.Errorf("Expected 2 Author nodes, got %d", authorCount)
}
// Run migration
err := migrateAuthorToNostrUser(ctx, testDB)
if err != nil {
t.Fatalf("Migration failed: %v", err)
}
// Verify NostrUser nodes were created
nostrUserCount := countNodes(t, "NostrUser")
if nostrUserCount != 2 {
t.Errorf("Expected 2 NostrUser nodes after migration, got %d", nostrUserCount)
}
// Verify Author nodes were deleted (they should have no relationships after migration)
authorCountAfter := countNodes(t, "Author")
if authorCountAfter != 0 {
t.Errorf("Expected 0 Author nodes after migration, got %d", authorCountAfter)
}
}