auth_identity_payment_migrations_regression_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package migrations
  2. import (
  3. "strings"
  4. "testing"
  5. "github.com/stretchr/testify/require"
  6. )
  7. func TestMigration112UsesIdempotentAddColumn(t *testing.T) {
  8. content, err := FS.ReadFile("112_add_payment_order_provider_key_snapshot.sql")
  9. require.NoError(t, err)
  10. sql := string(content)
  11. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS provider_key VARCHAR(30)")
  12. require.NotContains(t, sql, "ADD COLUMN provider_key VARCHAR(30);")
  13. }
  14. func TestMigration118DoesNotForceOverwriteAuthSourceGrantDefaults(t *testing.T) {
  15. content, err := FS.ReadFile("118_wechat_dual_mode_and_auth_source_defaults.sql")
  16. require.NoError(t, err)
  17. sql := string(content)
  18. require.NotContains(t, sql, "UPDATE settings")
  19. require.NotContains(t, sql, "SET value = 'false'")
  20. require.True(t, strings.Contains(sql, "ON CONFLICT (key) DO NOTHING"))
  21. require.Contains(t, sql, "THEN ''")
  22. }
  23. func TestAuthIdentityReportTypeWideningRunsBeforeLongReportWritersAndStillReconcilesAt121(t *testing.T) {
  24. preflightContent, err := FS.ReadFile("108a_widen_auth_identity_migration_report_type.sql")
  25. require.NoError(t, err)
  26. preflightSQL := string(preflightContent)
  27. require.Contains(t, preflightSQL, "ALTER TABLE auth_identity_migration_reports")
  28. require.Contains(t, preflightSQL, "ALTER COLUMN report_type TYPE VARCHAR(80)")
  29. content, err := FS.ReadFile("109_auth_identity_compat_backfill.sql")
  30. require.NoError(t, err)
  31. sql := string(content)
  32. require.NotContains(t, sql, "ALTER TABLE auth_identity_migration_reports")
  33. followupContent, err := FS.ReadFile("121_auth_identity_migration_report_type_widen.sql")
  34. require.NoError(t, err)
  35. followupSQL := string(followupContent)
  36. require.Contains(t, followupSQL, "ALTER TABLE auth_identity_migration_reports")
  37. require.Contains(t, followupSQL, "ALTER COLUMN report_type TYPE VARCHAR(80)")
  38. }
  39. func TestMigration119DefersPaymentIndexRolloutToOnlineFollowup(t *testing.T) {
  40. content, err := FS.ReadFile("119_enforce_payment_orders_out_trade_no_unique.sql")
  41. require.NoError(t, err)
  42. sql := string(content)
  43. require.Contains(t, sql, "120_enforce_payment_orders_out_trade_no_unique_notx.sql")
  44. require.Contains(t, sql, "NULL;")
  45. require.NotContains(t, sql, "CREATE UNIQUE INDEX")
  46. require.NotContains(t, sql, "DROP INDEX")
  47. followupContent, err := FS.ReadFile("120_enforce_payment_orders_out_trade_no_unique_notx.sql")
  48. require.NoError(t, err)
  49. followupSQL := string(followupContent)
  50. require.Contains(t, followupSQL, "explicit duplicate out_trade_no precheck")
  51. require.Contains(t, followupSQL, "stale invalid paymentorder_out_trade_no_unique index")
  52. require.Contains(t, followupSQL, "CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS paymentorder_out_trade_no_unique")
  53. require.NotContains(t, followupSQL, "DROP INDEX CONCURRENTLY IF EXISTS paymentorder_out_trade_no_unique")
  54. require.Contains(t, followupSQL, "DROP INDEX CONCURRENTLY IF EXISTS paymentorder_out_trade_no")
  55. require.Contains(t, followupSQL, "WHERE out_trade_no <> ''")
  56. alignmentContent, err := FS.ReadFile("120a_align_payment_orders_out_trade_no_index_name.sql")
  57. require.NoError(t, err)
  58. alignmentSQL := string(alignmentContent)
  59. require.Contains(t, alignmentSQL, "paymentorder_out_trade_no_unique")
  60. require.Contains(t, alignmentSQL, "RENAME TO paymentorder_out_trade_no")
  61. }
  62. func TestMigration110SeedsAuthSourceSignupGrantsDisabledByDefault(t *testing.T) {
  63. content, err := FS.ReadFile("110_pending_auth_and_provider_default_grants.sql")
  64. require.NoError(t, err)
  65. sql := string(content)
  66. require.Contains(t, sql, "('auth_source_default_email_grant_on_signup', 'false')")
  67. require.Contains(t, sql, "('auth_source_default_linuxdo_grant_on_signup', 'false')")
  68. require.Contains(t, sql, "('auth_source_default_oidc_grant_on_signup', 'false')")
  69. require.Contains(t, sql, "('auth_source_default_wechat_grant_on_signup', 'false')")
  70. require.NotContains(t, sql, "('auth_source_default_email_grant_on_signup', 'true')")
  71. }
  72. func TestMigration122ScrubsPendingOAuthCompletionTokensAtRest(t *testing.T) {
  73. content, err := FS.ReadFile("122_pending_auth_completion_token_cleanup.sql")
  74. require.NoError(t, err)
  75. sql := string(content)
  76. require.Contains(t, sql, "UPDATE pending_auth_sessions")
  77. require.Contains(t, sql, "completion_response")
  78. require.Contains(t, sql, "access_token")
  79. require.Contains(t, sql, "refresh_token")
  80. require.Contains(t, sql, "expires_in")
  81. require.Contains(t, sql, "token_type")
  82. }
  83. func TestMigration123BackfillsLegacyAuthSourceGrantDefaultsSafely(t *testing.T) {
  84. content, err := FS.ReadFile("123_fix_legacy_auth_source_grant_on_signup_defaults.sql")
  85. require.NoError(t, err)
  86. sql := string(content)
  87. require.Contains(t, sql, "110_pending_auth_and_provider_default_grants.sql")
  88. require.Contains(t, sql, "schema_migrations")
  89. require.Contains(t, sql, "updated_at")
  90. require.Contains(t, sql, "'_grant_on_signup'")
  91. require.Contains(t, sql, "value = 'false'")
  92. require.Contains(t, sql, "auth_identity_migration_reports")
  93. }
  94. func TestMigration124BackfillsLegacyOIDCSecurityFlagsSafely(t *testing.T) {
  95. content, err := FS.ReadFile("124_backfill_legacy_oidc_security_flags.sql")
  96. require.NoError(t, err)
  97. sql := string(content)
  98. require.Contains(t, sql, "oidc_connect_use_pkce")
  99. require.Contains(t, sql, "oidc_connect_validate_id_token")
  100. require.Contains(t, sql, "ON CONFLICT (key) DO NOTHING")
  101. require.Contains(t, sql, "oidc_connect_enabled")
  102. require.Contains(t, sql, "'false'")
  103. }
  104. func TestMigration134AddsAffiliateLedgerAuditFieldsWithoutJSONCast(t *testing.T) {
  105. content, err := FS.ReadFile("134_affiliate_ledger_audit_snapshots.sql")
  106. require.NoError(t, err)
  107. sql := string(content)
  108. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS source_order_id BIGINT")
  109. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS balance_after DECIMAL(20,8)")
  110. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS aff_quota_after DECIMAL(20,8)")
  111. require.Contains(t, sql, "substring(")
  112. require.Contains(t, sql, `"rebateAmount"`)
  113. require.Contains(t, sql, "COUNT(*) OVER (PARTITION BY ra.order_id) AS order_match_count")
  114. require.Contains(t, sql, "COUNT(*) OVER (PARTITION BY ual.id) AS ledger_match_count")
  115. require.NotContains(t, sql, "detail::jsonb")
  116. }
  117. func TestMigration135AllowsGitHubAndGoogleAuthProviders(t *testing.T) {
  118. content, err := FS.ReadFile("135_allow_email_oauth_provider_types.sql")
  119. require.NoError(t, err)
  120. sql := string(content)
  121. require.Contains(t, sql, "users_signup_source_check")
  122. require.Contains(t, sql, "auth_identities_provider_type_check")
  123. require.Contains(t, sql, "auth_identity_channels_provider_type_check")
  124. require.Contains(t, sql, "pending_auth_sessions_provider_type_check")
  125. require.Contains(t, sql, "'github'")
  126. require.Contains(t, sql, "'google'")
  127. }
  128. func TestMigration151AddsAccountAutoPauseExpiryPartialIndex(t *testing.T) {
  129. content, err := FS.ReadFile("151_account_autopause_expiry_index_notx.sql")
  130. require.NoError(t, err)
  131. sql := string(content)
  132. require.Contains(t, sql, "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_accounts_autopause_expiry_due")
  133. require.Contains(t, sql, "ON accounts (expires_at)")
  134. require.Contains(t, sql, "WHERE deleted_at IS NULL")
  135. require.Contains(t, sql, "schedulable = TRUE")
  136. require.Contains(t, sql, "auto_pause_on_expired = TRUE")
  137. require.Contains(t, sql, "expires_at IS NOT NULL")
  138. }
  139. func TestMigration158BackfillsGrokMediaGenerationGroups(t *testing.T) {
  140. content, err := FS.ReadFile("158_enable_grok_media_generation_groups.sql")
  141. require.NoError(t, err)
  142. sql := string(content)
  143. require.Contains(t, sql, "UPDATE groups")
  144. require.Contains(t, sql, "SET allow_image_generation = true")
  145. require.Contains(t, sql, "WHERE platform = 'grok'")
  146. require.Contains(t, sql, "AND allow_image_generation = false")
  147. }
  148. func TestMigration154AddsSparkShadowColumnsAndConstraintsWithoutHotIndexes(t *testing.T) {
  149. content, err := FS.ReadFile("154_account_spark_shadow.sql")
  150. require.NoError(t, err)
  151. sql := string(content)
  152. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS parent_account_id BIGINT")
  153. require.Contains(t, sql, "ADD COLUMN IF NOT EXISTS quota_dimension VARCHAR(20) NOT NULL DEFAULT 'global'")
  154. require.Contains(t, sql, "chk_accounts_parent_dimension")
  155. // 约束已放开为「影子 ⇒ 非 global 维度」(spark 不再写死进 parent 约束)
  156. require.Contains(t, sql, "parent_account_id IS NOT NULL AND quota_dimension <> 'global'")
  157. require.NotContains(t, sql, "parent_account_id IS NOT NULL AND quota_dimension = 'spark'")
  158. require.Contains(t, sql, "chk_accounts_parent_not_self")
  159. require.Contains(t, sql, "fk_accounts_parent_account_id")
  160. require.Contains(t, sql, "FOREIGN KEY (parent_account_id) REFERENCES accounts(id)")
  161. require.Contains(t, sql, "ON DELETE RESTRICT")
  162. require.Contains(t, sql, "NOT VALID")
  163. require.NotContains(t, sql, "CREATE INDEX")
  164. require.NotContains(t, sql, "CREATE UNIQUE INDEX")
  165. require.NotContains(t, sql, "CONCURRENTLY")
  166. }
  167. func TestMigration154aAddsSparkShadowIndexesConcurrently(t *testing.T) {
  168. content, err := FS.ReadFile("154a_account_spark_shadow_indexes_notx.sql")
  169. require.NoError(t, err)
  170. sql := string(content)
  171. require.Contains(t, sql, "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_accounts_parent_account_id")
  172. require.Contains(t, sql, "CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS uq_accounts_spark_shadow_per_parent")
  173. require.Contains(t, sql, "ON accounts (parent_account_id)")
  174. require.Contains(t, sql, "WHERE parent_account_id IS NOT NULL")
  175. require.Contains(t, sql, "quota_dimension = 'spark'")
  176. require.Contains(t, sql, "deleted_at IS NULL")
  177. }
  178. func TestMigration173AllowsCyberBlockedUsageRequestType(t *testing.T) {
  179. entries, err := FS.ReadDir(".")
  180. require.NoError(t, err)
  181. previousIndex := -1
  182. currentIndex := -1
  183. for i, entry := range entries {
  184. switch entry.Name() {
  185. case "172_video_per_second_billing_metadata.sql":
  186. previousIndex = i
  187. case "173_allow_cyber_blocked_usage_request_type.sql":
  188. currentIndex = i
  189. }
  190. }
  191. require.NotEqual(t, -1, previousIndex)
  192. require.NotEqual(t, -1, currentIndex)
  193. require.Less(t, previousIndex, currentIndex)
  194. content, err := FS.ReadFile("173_allow_cyber_blocked_usage_request_type.sql")
  195. require.NoError(t, err)
  196. sql := string(content)
  197. require.Contains(t, sql, "DROP CONSTRAINT IF EXISTS usage_logs_request_type_check")
  198. require.Contains(t, sql, "ADD CONSTRAINT usage_logs_request_type_check")
  199. require.Contains(t, sql, "CHECK (request_type IN (0, 1, 2, 3, 4)) NOT VALID")
  200. }