group_repo_duplicate_integration_test.go 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. //go:build integration
  2. package repository
  3. import (
  4. "context"
  5. "fmt"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/Wei-Shaw/sub2api/internal/service"
  10. "github.com/stretchr/testify/require"
  11. )
  12. func TestCreateGroupFromSourceRollsBackWhenOutboxInsertFails(t *testing.T) {
  13. ctx := context.Background()
  14. client := testEntClient(t)
  15. repo := newGroupRepositoryWithSQL(client, integrationDB)
  16. suffix := time.Now().UnixNano()
  17. operationID := strings.Repeat("b", 64)
  18. source, err := client.Group.Create().
  19. SetName(fmt.Sprintf("duplicate-rollback-source-%d", suffix)).
  20. SetPlatform(service.PlatformAnthropic).
  21. Save(ctx)
  22. require.NoError(t, err)
  23. account, err := client.Account.Create().
  24. SetName(fmt.Sprintf("duplicate-rollback-account-%d", suffix)).
  25. SetPlatform(service.PlatformAnthropic).
  26. SetType(service.AccountTypeOAuth).
  27. Save(ctx)
  28. require.NoError(t, err)
  29. _, err = client.AccountGroup.Create().
  30. SetAccountID(account.ID).
  31. SetGroupID(source.ID).
  32. SetPriority(29).
  33. Save(ctx)
  34. require.NoError(t, err)
  35. functionName := fmt.Sprintf("fail_group_duplicate_outbox_%d", suffix)
  36. triggerName := fmt.Sprintf("fail_group_duplicate_outbox_trigger_%d", suffix)
  37. _, err = integrationDB.ExecContext(ctx, fmt.Sprintf(`
  38. CREATE FUNCTION %s() RETURNS trigger LANGUAGE plpgsql AS $$
  39. BEGIN
  40. IF NEW.group_id IS NOT NULL AND EXISTS (
  41. SELECT 1 FROM groups
  42. WHERE id = NEW.group_id AND duplicate_operation_id = '%s'
  43. ) THEN
  44. RAISE EXCEPTION 'forced duplicate outbox failure';
  45. END IF;
  46. RETURN NEW;
  47. END;
  48. $$`, functionName, operationID))
  49. require.NoError(t, err)
  50. _, err = integrationDB.ExecContext(ctx, fmt.Sprintf(
  51. "CREATE TRIGGER %s BEFORE INSERT ON scheduler_outbox FOR EACH ROW EXECUTE FUNCTION %s()",
  52. triggerName,
  53. functionName,
  54. ))
  55. require.NoError(t, err)
  56. duplicateName := fmt.Sprintf("duplicate-rollback-copy-%d", suffix)
  57. t.Cleanup(func() {
  58. _, _ = integrationDB.ExecContext(context.Background(), fmt.Sprintf("DROP TRIGGER IF EXISTS %s ON scheduler_outbox", triggerName))
  59. _, _ = integrationDB.ExecContext(context.Background(), fmt.Sprintf("DROP FUNCTION IF EXISTS %s()", functionName))
  60. _, _ = integrationDB.ExecContext(context.Background(), "DELETE FROM scheduler_outbox WHERE group_id = $1", source.ID)
  61. _, _ = integrationDB.ExecContext(context.Background(), "DELETE FROM account_groups WHERE account_id = $1", account.ID)
  62. _, _ = integrationDB.ExecContext(context.Background(), "DELETE FROM accounts WHERE id = $1", account.ID)
  63. _, _ = integrationDB.ExecContext(context.Background(), "DELETE FROM groups WHERE name IN ($1, $2)", source.Name, duplicateName)
  64. })
  65. duplicate := &service.Group{
  66. Name: duplicateName,
  67. Platform: source.Platform,
  68. RateMultiplier: 1,
  69. Status: "inactive",
  70. SubscriptionType: service.SubscriptionTypeStandard,
  71. DuplicateOperationID: operationID,
  72. }
  73. err = repo.CreateFromSource(ctx, duplicate, source.ID)
  74. require.ErrorContains(t, err, "forced duplicate outbox failure")
  75. var groupCount, bindingCount, outboxCount int
  76. require.NoError(t, integrationDB.QueryRowContext(ctx, "SELECT COUNT(*) FROM groups WHERE name = $1", duplicateName).Scan(&groupCount))
  77. require.NoError(t, integrationDB.QueryRowContext(ctx, "SELECT COUNT(*) FROM account_groups WHERE group_id = $1", duplicate.ID).Scan(&bindingCount))
  78. require.NoError(t, integrationDB.QueryRowContext(ctx, "SELECT COUNT(*) FROM scheduler_outbox WHERE group_id = $1", duplicate.ID).Scan(&outboxCount))
  79. require.Zero(t, groupCount)
  80. require.Zero(t, bindingCount)
  81. require.Zero(t, outboxCount)
  82. }