client_delete_tombstone_test.go 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. package service
  2. import (
  3. "testing"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. // Delete tombstones up front and keeps the record when an inbound fails. A
  8. // surviving tombstone lets the next node merge finish the refused deletion.
  9. func TestFailedDeleteWithdrawsTombstone(t *testing.T) {
  10. setupBulkDB(t)
  11. svc := &ClientService{}
  12. inboundSvc := &InboundService{}
  13. db := database.GetDB()
  14. const email = "retry@x"
  15. broken := mkInbound(t, 30401, model.VLESS, `{"clients": [ THIS IS NOT JSON`)
  16. rec := &model.ClientRecord{Email: email, Enable: true, UUID: "33333333-3333-3333-3333-333333333333"}
  17. if err := db.Create(rec).Error; err != nil {
  18. t.Fatalf("create client record: %v", err)
  19. }
  20. if err := db.Create(&model.ClientInbound{ClientId: rec.Id, InboundId: broken.Id}).Error; err != nil {
  21. t.Fatalf("attach client: %v", err)
  22. }
  23. t.Cleanup(func() { withdrawClientTombstones(email) })
  24. if _, err := svc.Delete(inboundSvc, rec.Id, false); err == nil {
  25. t.Fatal("setup: delete was expected to fail on the unparseable inbound settings")
  26. }
  27. var surviving int64
  28. if err := db.Model(&model.ClientRecord{}).Where("email = ?", email).Count(&surviving).Error; err != nil {
  29. t.Fatalf("count clients: %v", err)
  30. }
  31. if surviving != 1 {
  32. t.Fatalf("failed delete must keep the record for a retry, got %d rows", surviving)
  33. }
  34. if isClientEmailTombstoned(email) {
  35. t.Fatal("delete kept the record but left a live tombstone: the next node sync would finish the deletion it refused")
  36. }
  37. }