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