delete_test.go raw

   1  //go:build integration
   2  // +build integration
   3  
   4  package neo4j
   5  
   6  import (
   7  	"context"
   8  	"testing"
   9  
  10  	"next.orly.dev/pkg/nostr/encoders/event"
  11  	"next.orly.dev/pkg/nostr/encoders/filter"
  12  	"next.orly.dev/pkg/nostr/encoders/hex"
  13  	"next.orly.dev/pkg/nostr/encoders/kind"
  14  	"next.orly.dev/pkg/nostr/encoders/tag"
  15  	"next.orly.dev/pkg/nostr/encoders/timestamp"
  16  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  17  )
  18  
  19  // All tests in this file use the shared testDB instance from testmain_test.go
  20  // to avoid Neo4j authentication rate limiting from too many connections.
  21  
  22  func TestDeleteEvent(t *testing.T) {
  23  	if testDB == nil {
  24  		t.Skip("Neo4j not available")
  25  	}
  26  
  27  	cleanTestDatabase()
  28  
  29  	ctx := context.Background()
  30  
  31  	signer, err := p8k.New()
  32  	if err != nil {
  33  		t.Fatalf("Failed to create signer: %v", err)
  34  	}
  35  	if err := signer.Generate(); err != nil {
  36  		t.Fatalf("Failed to generate keypair: %v", err)
  37  	}
  38  
  39  	// Create and save event
  40  	ev := event.New()
  41  	ev.Pubkey = signer.Pub()
  42  	ev.CreatedAt = timestamp.Now().V
  43  	ev.Kind = 1
  44  	ev.Content = []byte("Event to be deleted")
  45  
  46  	if err := ev.Sign(signer); err != nil {
  47  		t.Fatalf("Failed to sign event: %v", err)
  48  	}
  49  
  50  	if _, err := testDB.SaveEvent(ctx, ev); err != nil {
  51  		t.Fatalf("Failed to save event: %v", err)
  52  	}
  53  
  54  	// Verify event exists
  55  	evs, err := testDB.QueryEvents(ctx, &filter.F{
  56  		Ids: tag.NewFromBytesSlice(ev.ID),
  57  	})
  58  	if err != nil {
  59  		t.Fatalf("Failed to query event: %v", err)
  60  	}
  61  	if len(evs) != 1 {
  62  		t.Fatalf("Expected 1 event before deletion, got %d", len(evs))
  63  	}
  64  
  65  	// Delete the event
  66  	if err := testDB.DeleteEvent(ctx, ev.ID[:]); err != nil {
  67  		t.Fatalf("Failed to delete event: %v", err)
  68  	}
  69  
  70  	// Verify event is deleted
  71  	evs, err = testDB.QueryEvents(ctx, &filter.F{
  72  		Ids: tag.NewFromBytesSlice(ev.ID),
  73  	})
  74  	if err != nil {
  75  		t.Fatalf("Failed to query after deletion: %v", err)
  76  	}
  77  	if len(evs) != 0 {
  78  		t.Fatalf("Expected 0 events after deletion, got %d", len(evs))
  79  	}
  80  
  81  	t.Logf("✓ DeleteEvent successfully removed event")
  82  }
  83  
  84  func TestDeleteEventBySerial(t *testing.T) {
  85  	if testDB == nil {
  86  		t.Skip("Neo4j not available")
  87  	}
  88  
  89  	cleanTestDatabase()
  90  
  91  	ctx := context.Background()
  92  
  93  	signer, err := p8k.New()
  94  	if err != nil {
  95  		t.Fatalf("Failed to create signer: %v", err)
  96  	}
  97  	if err := signer.Generate(); err != nil {
  98  		t.Fatalf("Failed to generate keypair: %v", err)
  99  	}
 100  
 101  	// Create and save event
 102  	ev := event.New()
 103  	ev.Pubkey = signer.Pub()
 104  	ev.CreatedAt = timestamp.Now().V
 105  	ev.Kind = 1
 106  	ev.Content = []byte("Event to be deleted by serial")
 107  
 108  	if err := ev.Sign(signer); err != nil {
 109  		t.Fatalf("Failed to sign event: %v", err)
 110  	}
 111  
 112  	if _, err := testDB.SaveEvent(ctx, ev); err != nil {
 113  		t.Fatalf("Failed to save event: %v", err)
 114  	}
 115  
 116  	// Get serial
 117  	serial, err := testDB.GetSerialById(ev.ID[:])
 118  	if err != nil {
 119  		t.Fatalf("Failed to get serial: %v", err)
 120  	}
 121  
 122  	// Delete by serial
 123  	if err := testDB.DeleteEventBySerial(ctx, serial, ev); err != nil {
 124  		t.Fatalf("Failed to delete event by serial: %v", err)
 125  	}
 126  
 127  	// Verify event is deleted
 128  	evs, err := testDB.QueryEvents(ctx, &filter.F{
 129  		Ids: tag.NewFromBytesSlice(ev.ID),
 130  	})
 131  	if err != nil {
 132  		t.Fatalf("Failed to query after deletion: %v", err)
 133  	}
 134  	if len(evs) != 0 {
 135  		t.Fatalf("Expected 0 events after deletion, got %d", len(evs))
 136  	}
 137  
 138  	t.Logf("✓ DeleteEventBySerial successfully removed event")
 139  }
 140  
 141  func TestProcessDelete_AuthorCanDeleteOwnEvent(t *testing.T) {
 142  	if testDB == nil {
 143  		t.Skip("Neo4j not available")
 144  	}
 145  
 146  	cleanTestDatabase()
 147  
 148  	ctx := context.Background()
 149  
 150  	signer, err := p8k.New()
 151  	if err != nil {
 152  		t.Fatalf("Failed to create signer: %v", err)
 153  	}
 154  	if err := signer.Generate(); err != nil {
 155  		t.Fatalf("Failed to generate keypair: %v", err)
 156  	}
 157  
 158  	// Create and save original event
 159  	originalEvent := event.New()
 160  	originalEvent.Pubkey = signer.Pub()
 161  	originalEvent.CreatedAt = timestamp.Now().V
 162  	originalEvent.Kind = 1
 163  	originalEvent.Content = []byte("This event will be deleted via kind 5")
 164  
 165  	if err := originalEvent.Sign(signer); err != nil {
 166  		t.Fatalf("Failed to sign event: %v", err)
 167  	}
 168  
 169  	if _, err := testDB.SaveEvent(ctx, originalEvent); err != nil {
 170  		t.Fatalf("Failed to save event: %v", err)
 171  	}
 172  
 173  	// Create kind 5 deletion event
 174  	deleteEvent := event.New()
 175  	deleteEvent.Pubkey = signer.Pub() // Same author
 176  	deleteEvent.CreatedAt = timestamp.Now().V + 1
 177  	deleteEvent.Kind = kind.Deletion.K
 178  	deleteEvent.Content = []byte("Deleting my event")
 179  	deleteEvent.Tags = tag.NewS(
 180  		tag.NewFromAny("e", hex.Enc(originalEvent.ID[:])),
 181  	)
 182  
 183  	if err := deleteEvent.Sign(signer); err != nil {
 184  		t.Fatalf("Failed to sign delete event: %v", err)
 185  	}
 186  
 187  	// Process deletion (no admins)
 188  	if err := testDB.ProcessDelete(deleteEvent, nil); err != nil {
 189  		t.Fatalf("Failed to process delete: %v", err)
 190  	}
 191  
 192  	// Verify original event is deleted
 193  	evs, err := testDB.QueryEvents(ctx, &filter.F{
 194  		Ids: tag.NewFromBytesSlice(originalEvent.ID),
 195  	})
 196  	if err != nil {
 197  		t.Fatalf("Failed to query after deletion: %v", err)
 198  	}
 199  	if len(evs) != 0 {
 200  		t.Fatalf("Expected 0 events after deletion, got %d", len(evs))
 201  	}
 202  
 203  	t.Logf("✓ ProcessDelete allowed author to delete own event")
 204  }
 205  
 206  func TestProcessDelete_OtherUserCannotDelete(t *testing.T) {
 207  	if testDB == nil {
 208  		t.Skip("Neo4j not available")
 209  	}
 210  
 211  	cleanTestDatabase()
 212  
 213  	ctx := context.Background()
 214  
 215  	alice, _ := p8k.New()
 216  	alice.Generate()
 217  
 218  	bob, _ := p8k.New()
 219  	bob.Generate()
 220  
 221  	// Alice creates an event
 222  	aliceEvent := event.New()
 223  	aliceEvent.Pubkey = alice.Pub()
 224  	aliceEvent.CreatedAt = timestamp.Now().V
 225  	aliceEvent.Kind = 1
 226  	aliceEvent.Content = []byte("Alice's event")
 227  
 228  	if err := aliceEvent.Sign(alice); err != nil {
 229  		t.Fatalf("Failed to sign event: %v", err)
 230  	}
 231  
 232  	if _, err := testDB.SaveEvent(ctx, aliceEvent); err != nil {
 233  		t.Fatalf("Failed to save event: %v", err)
 234  	}
 235  
 236  	// Bob tries to delete Alice's event
 237  	deleteEvent := event.New()
 238  	deleteEvent.Pubkey = bob.Pub() // Different author
 239  	deleteEvent.CreatedAt = timestamp.Now().V + 1
 240  	deleteEvent.Kind = kind.Deletion.K
 241  	deleteEvent.Tags = tag.NewS(
 242  		tag.NewFromAny("e", hex.Enc(aliceEvent.ID[:])),
 243  	)
 244  
 245  	if err := deleteEvent.Sign(bob); err != nil {
 246  		t.Fatalf("Failed to sign delete event: %v", err)
 247  	}
 248  
 249  	// Process deletion (Bob is not an admin)
 250  	_ = testDB.ProcessDelete(deleteEvent, nil)
 251  
 252  	// Verify Alice's event still exists
 253  	evs, err := testDB.QueryEvents(ctx, &filter.F{
 254  		Ids: tag.NewFromBytesSlice(aliceEvent.ID),
 255  	})
 256  	if err != nil {
 257  		t.Fatalf("Failed to query: %v", err)
 258  	}
 259  	if len(evs) != 1 {
 260  		t.Fatalf("Expected Alice's event to still exist, got %d events", len(evs))
 261  	}
 262  
 263  	t.Logf("✓ ProcessDelete correctly prevented unauthorized deletion")
 264  }
 265  
 266  func TestProcessDelete_AdminCanDeleteAnyEvent(t *testing.T) {
 267  	if testDB == nil {
 268  		t.Skip("Neo4j not available")
 269  	}
 270  
 271  	cleanTestDatabase()
 272  
 273  	ctx := context.Background()
 274  
 275  	alice, _ := p8k.New()
 276  	alice.Generate()
 277  
 278  	admin, _ := p8k.New()
 279  	admin.Generate()
 280  
 281  	// Alice creates an event
 282  	aliceEvent := event.New()
 283  	aliceEvent.Pubkey = alice.Pub()
 284  	aliceEvent.CreatedAt = timestamp.Now().V
 285  	aliceEvent.Kind = 1
 286  	aliceEvent.Content = []byte("Alice's event to be deleted by admin")
 287  
 288  	if err := aliceEvent.Sign(alice); err != nil {
 289  		t.Fatalf("Failed to sign event: %v", err)
 290  	}
 291  
 292  	if _, err := testDB.SaveEvent(ctx, aliceEvent); err != nil {
 293  		t.Fatalf("Failed to save event: %v", err)
 294  	}
 295  
 296  	// Admin creates deletion event
 297  	deleteEvent := event.New()
 298  	deleteEvent.Pubkey = admin.Pub()
 299  	deleteEvent.CreatedAt = timestamp.Now().V + 1
 300  	deleteEvent.Kind = kind.Deletion.K
 301  	deleteEvent.Tags = tag.NewS(
 302  		tag.NewFromAny("e", hex.Enc(aliceEvent.ID[:])),
 303  	)
 304  
 305  	if err := deleteEvent.Sign(admin); err != nil {
 306  		t.Fatalf("Failed to sign delete event: %v", err)
 307  	}
 308  
 309  	// Process deletion with admin pubkey
 310  	adminPubkeys := [][]byte{admin.Pub()}
 311  	if err := testDB.ProcessDelete(deleteEvent, adminPubkeys); err != nil {
 312  		t.Fatalf("Failed to process delete: %v", err)
 313  	}
 314  
 315  	// Verify Alice's event is deleted
 316  	evs, err := testDB.QueryEvents(ctx, &filter.F{
 317  		Ids: tag.NewFromBytesSlice(aliceEvent.ID),
 318  	})
 319  	if err != nil {
 320  		t.Fatalf("Failed to query: %v", err)
 321  	}
 322  	if len(evs) != 0 {
 323  		t.Fatalf("Expected Alice's event to be deleted, got %d events", len(evs))
 324  	}
 325  
 326  	t.Logf("✓ ProcessDelete allowed admin to delete event")
 327  }
 328  
 329  func TestCheckForDeleted(t *testing.T) {
 330  	if testDB == nil {
 331  		t.Skip("Neo4j not available")
 332  	}
 333  
 334  	cleanTestDatabase()
 335  
 336  	ctx := context.Background()
 337  
 338  	signer, err := p8k.New()
 339  	if err != nil {
 340  		t.Fatalf("Failed to create signer: %v", err)
 341  	}
 342  	if err := signer.Generate(); err != nil {
 343  		t.Fatalf("Failed to generate keypair: %v", err)
 344  	}
 345  
 346  	// Create target event
 347  	targetEvent := event.New()
 348  	targetEvent.Pubkey = signer.Pub()
 349  	targetEvent.CreatedAt = timestamp.Now().V
 350  	targetEvent.Kind = 1
 351  	targetEvent.Content = []byte("Target event")
 352  
 353  	if err := targetEvent.Sign(signer); err != nil {
 354  		t.Fatalf("Failed to sign target event: %v", err)
 355  	}
 356  
 357  	if _, err := testDB.SaveEvent(ctx, targetEvent); err != nil {
 358  		t.Fatalf("Failed to save target event: %v", err)
 359  	}
 360  
 361  	// Check that event is not deleted (no deletion event exists)
 362  	err = testDB.CheckForDeleted(targetEvent, nil)
 363  	if err != nil {
 364  		t.Fatalf("Expected no error for non-deleted event, got: %v", err)
 365  	}
 366  
 367  	// Create deletion event that references target
 368  	deleteEvent := event.New()
 369  	deleteEvent.Pubkey = signer.Pub()
 370  	deleteEvent.CreatedAt = timestamp.Now().V + 1
 371  	deleteEvent.Kind = kind.Deletion.K
 372  	deleteEvent.Tags = tag.NewS(
 373  		tag.NewFromAny("e", hex.Enc(targetEvent.ID[:])),
 374  	)
 375  
 376  	if err := deleteEvent.Sign(signer); err != nil {
 377  		t.Fatalf("Failed to sign delete event: %v", err)
 378  	}
 379  
 380  	if _, err := testDB.SaveEvent(ctx, deleteEvent); err != nil {
 381  		t.Fatalf("Failed to save delete event: %v", err)
 382  	}
 383  
 384  	// Now check should return error (event has been deleted)
 385  	err = testDB.CheckForDeleted(targetEvent, nil)
 386  	if err == nil {
 387  		t.Fatal("Expected error for deleted event")
 388  	}
 389  
 390  	t.Logf("✓ CheckForDeleted correctly detected deletion event")
 391  }
 392  
 393  func TestReplaceableEventDeletion(t *testing.T) {
 394  	if testDB == nil {
 395  		t.Skip("Neo4j not available")
 396  	}
 397  
 398  	cleanTestDatabase()
 399  
 400  	ctx := context.Background()
 401  
 402  	signer, err := p8k.New()
 403  	if err != nil {
 404  		t.Fatalf("Failed to create signer: %v", err)
 405  	}
 406  	if err := signer.Generate(); err != nil {
 407  		t.Fatalf("Failed to generate keypair: %v", err)
 408  	}
 409  
 410  	// Create replaceable event (kind 0 - profile)
 411  	profileEvent := event.New()
 412  	profileEvent.Pubkey = signer.Pub()
 413  	profileEvent.CreatedAt = timestamp.Now().V
 414  	profileEvent.Kind = 0
 415  	profileEvent.Content = []byte(`{"name":"Test User"}`)
 416  
 417  	if err := profileEvent.Sign(signer); err != nil {
 418  		t.Fatalf("Failed to sign event: %v", err)
 419  	}
 420  
 421  	if _, err := testDB.SaveEvent(ctx, profileEvent); err != nil {
 422  		t.Fatalf("Failed to save event: %v", err)
 423  	}
 424  
 425  	// Verify event exists
 426  	evs, err := testDB.QueryEvents(ctx, &filter.F{
 427  		Kinds:   kind.NewS(kind.New(0)),
 428  		Authors: tag.NewFromBytesSlice(signer.Pub()),
 429  	})
 430  	if err != nil {
 431  		t.Fatalf("Failed to query: %v", err)
 432  	}
 433  	if len(evs) != 1 {
 434  		t.Fatalf("Expected 1 profile event, got %d", len(evs))
 435  	}
 436  
 437  	// Create a newer replaceable event (replaces the old one)
 438  	newerProfileEvent := event.New()
 439  	newerProfileEvent.Pubkey = signer.Pub()
 440  	newerProfileEvent.CreatedAt = timestamp.Now().V + 100
 441  	newerProfileEvent.Kind = 0
 442  	newerProfileEvent.Content = []byte(`{"name":"Updated User"}`)
 443  
 444  	if err := newerProfileEvent.Sign(signer); err != nil {
 445  		t.Fatalf("Failed to sign newer event: %v", err)
 446  	}
 447  
 448  	if _, err := testDB.SaveEvent(ctx, newerProfileEvent); err != nil {
 449  		t.Fatalf("Failed to save newer event: %v", err)
 450  	}
 451  
 452  	// Query should return only the newer event
 453  	evs, err = testDB.QueryEvents(ctx, &filter.F{
 454  		Kinds:   kind.NewS(kind.New(0)),
 455  		Authors: tag.NewFromBytesSlice(signer.Pub()),
 456  	})
 457  	if err != nil {
 458  		t.Fatalf("Failed to query: %v", err)
 459  	}
 460  	if len(evs) != 1 {
 461  		t.Fatalf("Expected 1 profile event after replacement, got %d", len(evs))
 462  	}
 463  
 464  	if hex.Enc(evs[0].ID[:]) != hex.Enc(newerProfileEvent.ID[:]) {
 465  		t.Fatal("Expected newer profile event to be returned")
 466  	}
 467  
 468  	t.Logf("✓ Replaceable event correctly replaced by newer version")
 469  }
 470