migrations_test.go raw

   1  package neo4j
   2  
   3  import (
   4  	"context"
   5  	"testing"
   6  )
   7  
   8  // TestMigrationV2_CleanupBinaryEncodedValues tests that migration v2 properly
   9  // cleans up binary-encoded pubkeys and event IDs
  10  func TestMigrationV2_CleanupBinaryEncodedValues(t *testing.T) {
  11  	if testDB == nil {
  12  		t.Skip("Neo4j not available")
  13  	}
  14  
  15  	// Clean up before test
  16  	cleanTestDatabase()
  17  
  18  	ctx := context.Background()
  19  
  20  	// Create some valid NostrUser nodes (should NOT be deleted)
  21  	validPubkeys := []string{
  22  		"0000000000000000000000000000000000000000000000000000000000000001",
  23  		"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
  24  	}
  25  	for _, pk := range validPubkeys {
  26  		setupInvalidNostrUser(t, pk) // Using setupInvalidNostrUser to create directly
  27  	}
  28  
  29  	// Create some invalid NostrUser nodes (should be deleted)
  30  	invalidPubkeys := []string{
  31  		"binary\x00garbage\x00data",                         // Binary garbage
  32  		"ABCDEF",                                             // Too short
  33  		"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", // Non-hex chars
  34  		string(append(make([]byte, 32), 0)),                 // 33-byte binary format
  35  	}
  36  	for _, pk := range invalidPubkeys {
  37  		setupInvalidNostrUser(t, pk)
  38  	}
  39  
  40  	// Verify invalid nodes exist before migration
  41  	invalidCountBefore := countInvalidNostrUsers(t)
  42  	if invalidCountBefore != 4 {
  43  		t.Errorf("Expected 4 invalid NostrUsers before migration, got %d", invalidCountBefore)
  44  	}
  45  
  46  	totalBefore := countNodes(t, "NostrUser")
  47  	if totalBefore != 6 {
  48  		t.Errorf("Expected 6 total NostrUsers before migration, got %d", totalBefore)
  49  	}
  50  
  51  	// Run the migration
  52  	err := migrateBinaryToHex(ctx, testDB)
  53  	if err != nil {
  54  		t.Fatalf("Migration failed: %v", err)
  55  	}
  56  
  57  	// Verify invalid nodes were deleted
  58  	invalidCountAfter := countInvalidNostrUsers(t)
  59  	if invalidCountAfter != 0 {
  60  		t.Errorf("Expected 0 invalid NostrUsers after migration, got %d", invalidCountAfter)
  61  	}
  62  
  63  	// Verify valid nodes were NOT deleted
  64  	totalAfter := countNodes(t, "NostrUser")
  65  	if totalAfter != 2 {
  66  		t.Errorf("Expected 2 valid NostrUsers after migration, got %d", totalAfter)
  67  	}
  68  }
  69  
  70  // TestMigrationV2_CleanupInvalidEvents tests that migration v2 properly
  71  // cleans up Event nodes with invalid pubkeys or IDs
  72  func TestMigrationV2_CleanupInvalidEvents(t *testing.T) {
  73  	if testDB == nil {
  74  		t.Skip("Neo4j not available")
  75  	}
  76  
  77  	// Clean up before test
  78  	cleanTestDatabase()
  79  
  80  	ctx := context.Background()
  81  
  82  	// Create valid events
  83  	validEventID := "1111111111111111111111111111111111111111111111111111111111111111"
  84  	validPubkey := "0000000000000000000000000000000000000000000000000000000000000001"
  85  	setupTestEvent(t, validEventID, validPubkey, 1, "[]")
  86  
  87  	// Create invalid events directly
  88  	setupInvalidEvent(t, "invalid_id", validPubkey)             // Invalid ID
  89  	setupInvalidEvent(t, validEventID+"2", "invalid_pubkey")    // Invalid pubkey (different ID to avoid duplicate)
  90  	setupInvalidEvent(t, "TOOSHORT", "binary\x00garbage")       // Both invalid
  91  
  92  	// Count events before migration
  93  	eventsBefore := countNodes(t, "Event")
  94  	if eventsBefore != 4 {
  95  		t.Errorf("Expected 4 Events before migration, got %d", eventsBefore)
  96  	}
  97  
  98  	// Run the migration
  99  	err := migrateBinaryToHex(ctx, testDB)
 100  	if err != nil {
 101  		t.Fatalf("Migration failed: %v", err)
 102  	}
 103  
 104  	// Verify only valid event remains
 105  	eventsAfter := countNodes(t, "Event")
 106  	if eventsAfter != 1 {
 107  		t.Errorf("Expected 1 valid Event after migration, got %d", eventsAfter)
 108  	}
 109  }
 110  
 111  // TestMigrationV2_CleanupInvalidTags tests that migration v2 properly
 112  // cleans up Tag nodes (e/p type) with invalid values
 113  func TestMigrationV2_CleanupInvalidTags(t *testing.T) {
 114  	if testDB == nil {
 115  		t.Skip("Neo4j not available")
 116  	}
 117  
 118  	// Clean up before test
 119  	cleanTestDatabase()
 120  
 121  	ctx := context.Background()
 122  
 123  	// Create valid tags
 124  	validHex := "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
 125  	setupInvalidTag(t, "e", validHex) // Valid e-tag
 126  	setupInvalidTag(t, "p", validHex) // Valid p-tag
 127  	setupInvalidTag(t, "t", "topic")  // Non e/p tag (should not be affected)
 128  
 129  	// Create invalid e/p tags
 130  	setupInvalidTag(t, "e", "binary\x00garbage")   // Invalid e-tag
 131  	setupInvalidTag(t, "p", "TOOSHORT")            // Invalid p-tag (too short)
 132  	setupInvalidTag(t, "e", string(append(make([]byte, 32), 0))) // Binary encoded
 133  
 134  	// Count tags before migration
 135  	tagsBefore := countNodes(t, "Tag")
 136  	if tagsBefore != 6 {
 137  		t.Errorf("Expected 6 Tags before migration, got %d", tagsBefore)
 138  	}
 139  
 140  	invalidBefore := countInvalidTags(t)
 141  	if invalidBefore != 3 {
 142  		t.Errorf("Expected 3 invalid e/p Tags before migration, got %d", invalidBefore)
 143  	}
 144  
 145  	// Run the migration
 146  	err := migrateBinaryToHex(ctx, testDB)
 147  	if err != nil {
 148  		t.Fatalf("Migration failed: %v", err)
 149  	}
 150  
 151  	// Verify invalid tags were deleted
 152  	invalidAfter := countInvalidTags(t)
 153  	if invalidAfter != 0 {
 154  		t.Errorf("Expected 0 invalid e/p Tags after migration, got %d", invalidAfter)
 155  	}
 156  
 157  	// Verify valid tags remain (2 e/p valid + 1 t-tag)
 158  	tagsAfter := countNodes(t, "Tag")
 159  	if tagsAfter != 3 {
 160  		t.Errorf("Expected 3 Tags after migration, got %d", tagsAfter)
 161  	}
 162  }
 163  
 164  // TestMigrationV2_Idempotent tests that migration v2 can be run multiple times safely
 165  func TestMigrationV2_Idempotent(t *testing.T) {
 166  	if testDB == nil {
 167  		t.Skip("Neo4j not available")
 168  	}
 169  
 170  	// Clean up before test
 171  	cleanTestDatabase()
 172  
 173  	ctx := context.Background()
 174  
 175  	// Create only valid data
 176  	validPubkey := "0000000000000000000000000000000000000000000000000000000000000001"
 177  	validEventID := "1111111111111111111111111111111111111111111111111111111111111111"
 178  	setupTestEvent(t, validEventID, validPubkey, 1, "[]")
 179  
 180  	countBefore := countNodes(t, "Event")
 181  
 182  	// Run migration first time
 183  	err := migrateBinaryToHex(ctx, testDB)
 184  	if err != nil {
 185  		t.Fatalf("First migration run failed: %v", err)
 186  	}
 187  
 188  	countAfterFirst := countNodes(t, "Event")
 189  	if countAfterFirst != countBefore {
 190  		t.Errorf("First migration changed valid event count: before=%d, after=%d", countBefore, countAfterFirst)
 191  	}
 192  
 193  	// Run migration second time
 194  	err = migrateBinaryToHex(ctx, testDB)
 195  	if err != nil {
 196  		t.Fatalf("Second migration run failed: %v", err)
 197  	}
 198  
 199  	countAfterSecond := countNodes(t, "Event")
 200  	if countAfterSecond != countBefore {
 201  		t.Errorf("Second migration changed valid event count: before=%d, after=%d", countBefore, countAfterSecond)
 202  	}
 203  }
 204  
 205  // TestMigrationV2_NoDataDoesNotFail tests that migration v2 succeeds with empty database
 206  func TestMigrationV2_NoDataDoesNotFail(t *testing.T) {
 207  	if testDB == nil {
 208  		t.Skip("Neo4j not available")
 209  	}
 210  
 211  	// Clean up completely
 212  	cleanTestDatabase()
 213  
 214  	ctx := context.Background()
 215  
 216  	// Run migration on empty database - should not fail
 217  	err := migrateBinaryToHex(ctx, testDB)
 218  	if err != nil {
 219  		t.Fatalf("Migration on empty database failed: %v", err)
 220  	}
 221  }
 222  
 223  // TestMigrationMarking tests that migrations are properly tracked
 224  func TestMigrationMarking(t *testing.T) {
 225  	if testDB == nil {
 226  		t.Skip("Neo4j not available")
 227  	}
 228  
 229  	// Clean up before test
 230  	cleanTestDatabase()
 231  
 232  	ctx := context.Background()
 233  
 234  	// Verify migration v2 has not been applied
 235  	if testDB.migrationApplied(ctx, "v2") {
 236  		t.Error("Migration v2 should not be applied before test")
 237  	}
 238  
 239  	// Mark migration as complete
 240  	err := testDB.markMigrationComplete(ctx, "v2", "Test migration")
 241  	if err != nil {
 242  		t.Fatalf("Failed to mark migration complete: %v", err)
 243  	}
 244  
 245  	// Verify migration is now marked as applied
 246  	if !testDB.migrationApplied(ctx, "v2") {
 247  		t.Error("Migration v2 should be applied after marking")
 248  	}
 249  
 250  	// Clean up
 251  	cleanTestDatabase()
 252  }
 253  
 254  // TestMigrationV1_AuthorToNostrUserMerge tests the author migration
 255  func TestMigrationV1_AuthorToNostrUserMerge(t *testing.T) {
 256  	if testDB == nil {
 257  		t.Skip("Neo4j not available")
 258  	}
 259  
 260  	// Clean up before test
 261  	cleanTestDatabase()
 262  
 263  	ctx := context.Background()
 264  
 265  	// Create some Author nodes (legacy format)
 266  	authorPubkeys := []string{
 267  		"0000000000000000000000000000000000000000000000000000000000000001",
 268  		"0000000000000000000000000000000000000000000000000000000000000002",
 269  	}
 270  
 271  	for _, pk := range authorPubkeys {
 272  		cypher := `CREATE (a:Author {pubkey: $pubkey})`
 273  		_, err := testDB.ExecuteWrite(ctx, cypher, map[string]any{"pubkey": pk})
 274  		if err != nil {
 275  			t.Fatalf("Failed to create Author node: %v", err)
 276  		}
 277  	}
 278  
 279  	// Verify Author nodes exist
 280  	authorCount := countNodes(t, "Author")
 281  	if authorCount != 2 {
 282  		t.Errorf("Expected 2 Author nodes, got %d", authorCount)
 283  	}
 284  
 285  	// Run migration
 286  	err := migrateAuthorToNostrUser(ctx, testDB)
 287  	if err != nil {
 288  		t.Fatalf("Migration failed: %v", err)
 289  	}
 290  
 291  	// Verify NostrUser nodes were created
 292  	nostrUserCount := countNodes(t, "NostrUser")
 293  	if nostrUserCount != 2 {
 294  		t.Errorf("Expected 2 NostrUser nodes after migration, got %d", nostrUserCount)
 295  	}
 296  
 297  	// Verify Author nodes were deleted (they should have no relationships after migration)
 298  	authorCountAfter := countNodes(t, "Author")
 299  	if authorCountAfter != 0 {
 300  		t.Errorf("Expected 0 Author nodes after migration, got %d", authorCountAfter)
 301  	}
 302  }
 303