Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
06136af8
Commit
06136af8
authored
Apr 22, 2026
by
IanShaw027
Browse files
fix(upgrade): preserve legacy auth and payment compatibility
parent
29caf851
Changes
14
Hide whitespace changes
Inline
Side-by-side
backend/internal/handler/admin/setting_handler.go
View file @
06136af8
...
@@ -304,8 +304,8 @@ type UpdateSettingsRequest struct {
...
@@ -304,8 +304,8 @@ type UpdateSettingsRequest struct {
OIDCConnectRedirectURL
string
`json:"oidc_connect_redirect_url"`
OIDCConnectRedirectURL
string
`json:"oidc_connect_redirect_url"`
OIDCConnectFrontendRedirectURL
string
`json:"oidc_connect_frontend_redirect_url"`
OIDCConnectFrontendRedirectURL
string
`json:"oidc_connect_frontend_redirect_url"`
OIDCConnectTokenAuthMethod
string
`json:"oidc_connect_token_auth_method"`
OIDCConnectTokenAuthMethod
string
`json:"oidc_connect_token_auth_method"`
OIDCConnectUsePKCE
bool
`json:"oidc_connect_use_pkce"`
OIDCConnectUsePKCE
*
bool
`json:"oidc_connect_use_pkce"`
OIDCConnectValidateIDToken
bool
`json:"oidc_connect_validate_id_token"`
OIDCConnectValidateIDToken
*
bool
`json:"oidc_connect_validate_id_token"`
OIDCConnectAllowedSigningAlgs
string
`json:"oidc_connect_allowed_signing_algs"`
OIDCConnectAllowedSigningAlgs
string
`json:"oidc_connect_allowed_signing_algs"`
OIDCConnectClockSkewSeconds
int
`json:"oidc_connect_clock_skew_seconds"`
OIDCConnectClockSkewSeconds
int
`json:"oidc_connect_clock_skew_seconds"`
OIDCConnectRequireEmailVerified
bool
`json:"oidc_connect_require_email_verified"`
OIDCConnectRequireEmailVerified
bool
`json:"oidc_connect_require_email_verified"`
...
@@ -682,6 +682,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -682,6 +682,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
}
// Generic OIDC 参数验证
// Generic OIDC 参数验证
oidcUsePKCE
:=
previousSettings
.
OIDCConnectUsePKCE
oidcValidateIDToken
:=
previousSettings
.
OIDCConnectValidateIDToken
if
req
.
OIDCConnectEnabled
{
if
req
.
OIDCConnectEnabled
{
req
.
OIDCConnectProviderName
=
strings
.
TrimSpace
(
req
.
OIDCConnectProviderName
)
req
.
OIDCConnectProviderName
=
strings
.
TrimSpace
(
req
.
OIDCConnectProviderName
)
req
.
OIDCConnectClientID
=
strings
.
TrimSpace
(
req
.
OIDCConnectClientID
)
req
.
OIDCConnectClientID
=
strings
.
TrimSpace
(
req
.
OIDCConnectClientID
)
...
@@ -716,11 +718,11 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -716,11 +718,11 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
req
.
OIDCConnectUserInfoEmailPath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoEmailPath
,
previousSettings
.
OIDCConnectUserInfoEmailPath
))
req
.
OIDCConnectUserInfoEmailPath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoEmailPath
,
previousSettings
.
OIDCConnectUserInfoEmailPath
))
req
.
OIDCConnectUserInfoIDPath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoIDPath
,
previousSettings
.
OIDCConnectUserInfoIDPath
))
req
.
OIDCConnectUserInfoIDPath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoIDPath
,
previousSettings
.
OIDCConnectUserInfoIDPath
))
req
.
OIDCConnectUserInfoUsernamePath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoUsernamePath
,
previousSettings
.
OIDCConnectUserInfoUsernamePath
))
req
.
OIDCConnectUserInfoUsernamePath
=
strings
.
TrimSpace
(
firstNonEmpty
(
req
.
OIDCConnectUserInfoUsernamePath
,
previousSettings
.
OIDCConnectUserInfoUsernamePath
))
if
!
req
.
OIDCConnectUsePKCE
{
if
req
.
OIDCConnectUsePKCE
!=
nil
{
req
.
OIDCConnect
UsePKCE
=
p
re
viousSettings
.
OIDCConnectUsePKCE
oidc
UsePKCE
=
*
re
q
.
OIDCConnectUsePKCE
}
}
if
!
req
.
OIDCConnectValidateIDToken
{
if
req
.
OIDCConnectValidateIDToken
!=
nil
{
req
.
OIDCConnect
ValidateIDToken
=
p
re
viousSettings
.
OIDCConnectValidateIDToken
oidc
ValidateIDToken
=
*
re
q
.
OIDCConnectValidateIDToken
}
}
if
req
.
OIDCConnectClockSkewSeconds
==
0
{
if
req
.
OIDCConnectClockSkewSeconds
==
0
{
req
.
OIDCConnectClockSkewSeconds
=
previousSettings
.
OIDCConnectClockSkewSeconds
req
.
OIDCConnectClockSkewSeconds
=
previousSettings
.
OIDCConnectClockSkewSeconds
...
@@ -795,7 +797,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -795,7 +797,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
response
.
BadRequest
(
c
,
"OIDC clock skew seconds must be between 0 and 600"
)
response
.
BadRequest
(
c
,
"OIDC clock skew seconds must be between 0 and 600"
)
return
return
}
}
if
req
.
OIDCConnect
ValidateIDToken
&&
req
.
OIDCConnectAllowedSigningAlgs
==
""
{
if
oidc
ValidateIDToken
&&
req
.
OIDCConnectAllowedSigningAlgs
==
""
{
response
.
BadRequest
(
c
,
"OIDC Allowed Signing Algs is required when validate_id_token=true"
)
response
.
BadRequest
(
c
,
"OIDC Allowed Signing Algs is required when validate_id_token=true"
)
return
return
}
}
...
@@ -1076,8 +1078,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
...
@@ -1076,8 +1078,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
OIDCConnectRedirectURL
:
req
.
OIDCConnectRedirectURL
,
OIDCConnectRedirectURL
:
req
.
OIDCConnectRedirectURL
,
OIDCConnectFrontendRedirectURL
:
req
.
OIDCConnectFrontendRedirectURL
,
OIDCConnectFrontendRedirectURL
:
req
.
OIDCConnectFrontendRedirectURL
,
OIDCConnectTokenAuthMethod
:
req
.
OIDCConnectTokenAuthMethod
,
OIDCConnectTokenAuthMethod
:
req
.
OIDCConnectTokenAuthMethod
,
OIDCConnectUsePKCE
:
req
.
OIDCConnect
UsePKCE
,
OIDCConnectUsePKCE
:
oidc
UsePKCE
,
OIDCConnectValidateIDToken
:
req
.
OIDCConnect
ValidateIDToken
,
OIDCConnectValidateIDToken
:
oidc
ValidateIDToken
,
OIDCConnectAllowedSigningAlgs
:
req
.
OIDCConnectAllowedSigningAlgs
,
OIDCConnectAllowedSigningAlgs
:
req
.
OIDCConnectAllowedSigningAlgs
,
OIDCConnectClockSkewSeconds
:
req
.
OIDCConnectClockSkewSeconds
,
OIDCConnectClockSkewSeconds
:
req
.
OIDCConnectClockSkewSeconds
,
OIDCConnectRequireEmailVerified
:
req
.
OIDCConnectRequireEmailVerified
,
OIDCConnectRequireEmailVerified
:
req
.
OIDCConnectRequireEmailVerified
,
...
...
backend/internal/handler/admin/setting_handler_auth_source_defaults_test.go
View file @
06136af8
...
@@ -247,6 +247,94 @@ func TestSettingHandler_UpdateSettings_PersistsPaymentVisibleMethodsAndAdvancedS
...
@@ -247,6 +247,94 @@ func TestSettingHandler_UpdateSettings_PersistsPaymentVisibleMethodsAndAdvancedS
require
.
Equal
(
t
,
true
,
data
[
"openai_advanced_scheduler_enabled"
])
require
.
Equal
(
t
,
true
,
data
[
"openai_advanced_scheduler_enabled"
])
}
}
func
TestSettingHandler_UpdateSettings_PreservesLegacyBlankPaymentVisibleMethodSource
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
repo
:=
&
settingHandlerRepoStub
{
values
:
map
[
string
]
string
{
service
.
SettingKeyPromoCodeEnabled
:
"true"
,
service
.
SettingPaymentVisibleMethodAlipayEnabled
:
"true"
,
service
.
SettingPaymentVisibleMethodAlipaySource
:
""
,
service
.
SettingPaymentVisibleMethodWxpayEnabled
:
"false"
,
service
.
SettingPaymentVisibleMethodWxpaySource
:
""
,
},
}
svc
:=
service
.
NewSettingService
(
repo
,
&
config
.
Config
{
Default
:
config
.
DefaultConfig
{
UserConcurrency
:
5
}})
handler
:=
NewSettingHandler
(
svc
,
nil
,
nil
,
nil
,
nil
,
nil
)
body
:=
map
[
string
]
any
{
"promo_code_enabled"
:
false
,
}
rawBody
,
err
:=
json
.
Marshal
(
body
)
require
.
NoError
(
t
,
err
)
rec
:=
httptest
.
NewRecorder
()
c
,
_
:=
gin
.
CreateTestContext
(
rec
)
c
.
Request
=
httptest
.
NewRequest
(
http
.
MethodPut
,
"/api/v1/admin/settings"
,
bytes
.
NewReader
(
rawBody
))
c
.
Request
.
Header
.
Set
(
"Content-Type"
,
"application/json"
)
handler
.
UpdateSettings
(
c
)
require
.
Equal
(
t
,
http
.
StatusOK
,
rec
.
Code
)
require
.
Equal
(
t
,
""
,
repo
.
values
[
service
.
SettingPaymentVisibleMethodAlipaySource
])
require
.
Equal
(
t
,
"true"
,
repo
.
values
[
service
.
SettingPaymentVisibleMethodAlipayEnabled
])
}
func
TestSettingHandler_UpdateSettings_PersistsExplicitFalseOIDCCompatibilityFlags
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
repo
:=
&
settingHandlerRepoStub
{
values
:
map
[
string
]
string
{
service
.
SettingKeyPromoCodeEnabled
:
"true"
,
service
.
SettingKeyOIDCConnectEnabled
:
"true"
,
service
.
SettingKeyOIDCConnectProviderName
:
"OIDC"
,
service
.
SettingKeyOIDCConnectClientID
:
"oidc-client"
,
service
.
SettingKeyOIDCConnectClientSecret
:
"oidc-secret"
,
service
.
SettingKeyOIDCConnectIssuerURL
:
"https://issuer.example.com"
,
service
.
SettingKeyOIDCConnectAuthorizeURL
:
"https://issuer.example.com/auth"
,
service
.
SettingKeyOIDCConnectTokenURL
:
"https://issuer.example.com/token"
,
service
.
SettingKeyOIDCConnectUserInfoURL
:
"https://issuer.example.com/userinfo"
,
service
.
SettingKeyOIDCConnectJWKSURL
:
"https://issuer.example.com/jwks"
,
service
.
SettingKeyOIDCConnectScopes
:
"openid email profile"
,
service
.
SettingKeyOIDCConnectRedirectURL
:
"https://example.com/api/v1/auth/oauth/oidc/callback"
,
service
.
SettingKeyOIDCConnectFrontendRedirectURL
:
"/auth/oidc/callback"
,
service
.
SettingKeyOIDCConnectTokenAuthMethod
:
"client_secret_post"
,
service
.
SettingKeyOIDCConnectUsePKCE
:
"true"
,
service
.
SettingKeyOIDCConnectValidateIDToken
:
"true"
,
service
.
SettingKeyOIDCConnectAllowedSigningAlgs
:
"RS256"
,
service
.
SettingKeyOIDCConnectClockSkewSeconds
:
"120"
,
},
}
svc
:=
service
.
NewSettingService
(
repo
,
&
config
.
Config
{
Default
:
config
.
DefaultConfig
{
UserConcurrency
:
5
}})
handler
:=
NewSettingHandler
(
svc
,
nil
,
nil
,
nil
,
nil
,
nil
)
body
:=
map
[
string
]
any
{
"promo_code_enabled"
:
true
,
"oidc_connect_enabled"
:
true
,
"oidc_connect_use_pkce"
:
false
,
"oidc_connect_validate_id_token"
:
false
,
"oidc_connect_allowed_signing_algs"
:
""
,
}
rawBody
,
err
:=
json
.
Marshal
(
body
)
require
.
NoError
(
t
,
err
)
rec
:=
httptest
.
NewRecorder
()
c
,
_
:=
gin
.
CreateTestContext
(
rec
)
c
.
Request
=
httptest
.
NewRequest
(
http
.
MethodPut
,
"/api/v1/admin/settings"
,
bytes
.
NewReader
(
rawBody
))
c
.
Request
.
Header
.
Set
(
"Content-Type"
,
"application/json"
)
handler
.
UpdateSettings
(
c
)
require
.
Equal
(
t
,
http
.
StatusOK
,
rec
.
Code
)
require
.
Equal
(
t
,
"false"
,
repo
.
values
[
service
.
SettingKeyOIDCConnectUsePKCE
])
require
.
Equal
(
t
,
"false"
,
repo
.
values
[
service
.
SettingKeyOIDCConnectValidateIDToken
])
var
resp
response
.
Response
require
.
NoError
(
t
,
json
.
Unmarshal
(
rec
.
Body
.
Bytes
(),
&
resp
))
data
,
ok
:=
resp
.
Data
.
(
map
[
string
]
any
)
require
.
True
(
t
,
ok
)
require
.
Equal
(
t
,
false
,
data
[
"oidc_connect_use_pkce"
])
require
.
Equal
(
t
,
false
,
data
[
"oidc_connect_validate_id_token"
])
}
func
TestSettingHandler_UpdateSettings_RejectsInvalidPaymentVisibleMethodSource
(
t
*
testing
.
T
)
{
func
TestSettingHandler_UpdateSettings_RejectsInvalidPaymentVisibleMethodSource
(
t
*
testing
.
T
)
{
gin
.
SetMode
(
gin
.
TestMode
)
gin
.
SetMode
(
gin
.
TestMode
)
repo
:=
&
settingHandlerRepoStub
{
repo
:=
&
settingHandlerRepoStub
{
...
...
backend/internal/repository/migrations_runner.go
View file @
06136af8
...
@@ -62,10 +62,13 @@ type migrationChecksumCompatibilityRule struct {
...
@@ -62,10 +62,13 @@ type migrationChecksumCompatibilityRule struct {
// 规则必须同时匹配「迁移名 + 数据库 checksum + 当前文件 checksum」且两者都落在该迁移的已知版本集合内才会放行,
// 规则必须同时匹配「迁移名 + 数据库 checksum + 当前文件 checksum」且两者都落在该迁移的已知版本集合内才会放行,
// 避免放宽全局校验,也允许将误改的历史 migration 回滚为已发布版本而不要求人工修 checksum。
// 避免放宽全局校验,也允许将误改的历史 migration 回滚为已发布版本而不要求人工修 checksum。
var
migrationChecksumCompatibilityRules
=
map
[
string
]
migrationChecksumCompatibilityRule
{
var
migrationChecksumCompatibilityRules
=
map
[
string
]
migrationChecksumCompatibilityRule
{
"054_drop_legacy_cache_columns.sql"
:
newMigrationChecksumCompatibilityRule
(
"82de761156e03876653e7a6a4eee883cd927847036f779b0b9f34c42a8af7a7d"
,
"182c193f3359946cf094090cd9e57d5c3fd9abaffbc1e8fc378646b8a6fa12b4"
),
"054_drop_legacy_cache_columns.sql"
:
newMigrationChecksumCompatibilityRule
(
"82de761156e03876653e7a6a4eee883cd927847036f779b0b9f34c42a8af7a7d"
,
"182c193f3359946cf094090cd9e57d5c3fd9abaffbc1e8fc378646b8a6fa12b4"
),
"061_add_usage_log_request_type.sql"
:
newMigrationChecksumCompatibilityRule
(
"66207e7aa5dd0429c2e2c0fabdaf79783ff157fa0af2e81adff2ee03790ec65c"
,
"08a248652cbab7cfde147fc6ef8cda464f2477674e20b718312faa252e0481c0"
,
"222b4a09c797c22e5922b6b172327c824f5463aaa8760e4f621bc5c22e2be0f3"
),
"061_add_usage_log_request_type.sql"
:
newMigrationChecksumCompatibilityRule
(
"66207e7aa5dd0429c2e2c0fabdaf79783ff157fa0af2e81adff2ee03790ec65c"
,
"08a248652cbab7cfde147fc6ef8cda464f2477674e20b718312faa252e0481c0"
,
"222b4a09c797c22e5922b6b172327c824f5463aaa8760e4f621bc5c22e2be0f3"
),
"109_auth_identity_compat_backfill.sql"
:
newMigrationChecksumCompatibilityRule
(
"2b380305e73ff0c13aa8c811e45897f2b36ca4a438f7b3e8f98e19ecb6bae0b3"
,
"551e498aa5616d2d91096e9d72cf9fb36e418ee22eacc557f8811cadbc9e20ee"
),
"109_auth_identity_compat_backfill.sql"
:
newMigrationChecksumCompatibilityRule
(
"2b380305e73ff0c13aa8c811e45897f2b36ca4a438f7b3e8f98e19ecb6bae0b3"
,
"551e498aa5616d2d91096e9d72cf9fb36e418ee22eacc557f8811cadbc9e20ee"
),
"119_enforce_payment_orders_out_trade_no_unique.sql"
:
newMigrationChecksumCompatibilityRule
(
"0bbe809ae48a9d811dabda1ba1c74955bd71c4a9cc610f9128816818dfa6c11e"
,
"ebd2c67cce0116393fb4f1b5d5116a67c6aceb73820dfb5133d1ff6f36d72d34"
),
"118_wechat_dual_mode_and_auth_source_defaults.sql"
:
newMigrationChecksumCompatibilityRule
(
"b54194d7a3e4fbf710e0a3590d22a2fe7966804c487052a356e0b55f53ef96b0"
,
"e0cdf835d6c688d64100f483d31bc02ac9ebad414bf1837af239a84bf75b8227"
),
"119_enforce_payment_orders_out_trade_no_unique.sql"
:
newMigrationChecksumCompatibilityRule
(
"0bbe809ae48a9d811dabda1ba1c74955bd71c4a9cc610f9128816818dfa6c11e"
,
"ebd2c67cce0116393fb4f1b5d5116a67c6aceb73820dfb5133d1ff6f36d72d34"
),
"120_enforce_payment_orders_out_trade_no_unique_notx.sql"
:
newMigrationChecksumCompatibilityRule
(
"707431450603e70a43ce9fbd61e0c12fa67da4875158ccefabacea069587ab22"
,
"04b082b5a239c525154fe9185d324ee2b05ff90da9297e10dba19f9be79aa59a"
),
"123_fix_legacy_auth_source_grant_on_signup_defaults.sql"
:
newMigrationChecksumCompatibilityRule
(
"2ce43c2cd89e9f9e1febd34a407ed9e84d177386c5544b6f02c1f58a21129f57"
,
"6cd33422f215dcd1f486ab6f35c0ea5805d9ca69bb25906d94bc649156657145"
),
}
}
// ApplyMigrations 将嵌入的 SQL 迁移文件应用到指定的数据库。
// ApplyMigrations 将嵌入的 SQL 迁移文件应用到指定的数据库。
...
...
backend/internal/repository/migrations_runner_extra_test.go
View file @
06136af8
...
@@ -94,6 +94,19 @@ func TestIsMigrationChecksumCompatible_AdditionalCases(t *testing.T) {
...
@@ -94,6 +94,19 @@ func TestIsMigrationChecksumCompatible_AdditionalCases(t *testing.T) {
require
.
True
(
t
,
isMigrationChecksumCompatible
(
name
,
accepted
,
rule
.
fileChecksum
))
require
.
True
(
t
,
isMigrationChecksumCompatible
(
name
,
accepted
,
rule
.
fileChecksum
))
}
}
func
TestMigrationChecksumCompatibilityRules_CoverEditedUpgradeCompatibilityMigrations
(
t
*
testing
.
T
)
{
for
_
,
name
:=
range
[]
string
{
"118_wechat_dual_mode_and_auth_source_defaults.sql"
,
"120_enforce_payment_orders_out_trade_no_unique_notx.sql"
,
"123_fix_legacy_auth_source_grant_on_signup_defaults.sql"
,
}
{
rule
,
ok
:=
migrationChecksumCompatibilityRules
[
name
]
require
.
Truef
(
t
,
ok
,
"missing compatibility rule for %s"
,
name
)
require
.
NotEmpty
(
t
,
rule
.
fileChecksum
)
require
.
NotEmpty
(
t
,
rule
.
acceptedDBChecksum
)
}
}
func
TestEnsureAtlasBaselineAligned
(
t
*
testing
.
T
)
{
func
TestEnsureAtlasBaselineAligned
(
t
*
testing
.
T
)
{
t
.
Run
(
"skip_when_no_legacy_table"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"skip_when_no_legacy_table"
,
func
(
t
*
testing
.
T
)
{
db
,
mock
,
err
:=
sqlmock
.
New
()
db
,
mock
,
err
:=
sqlmock
.
New
()
...
...
backend/internal/service/payment_config_limits.go
View file @
06136af8
...
@@ -45,10 +45,18 @@ func (s *PaymentConfigService) pcApplyEnabledVisibleMethodInstances(ctx context.
...
@@ -45,10 +45,18 @@ func (s *PaymentConfigService) pcApplyEnabledVisibleMethodInstances(ctx context.
for
_
,
method
:=
range
[]
string
{
payment
.
TypeAlipay
,
payment
.
TypeWxpay
}
{
for
_
,
method
:=
range
[]
string
{
payment
.
TypeAlipay
,
payment
.
TypeWxpay
}
{
matching
:=
filterEnabledVisibleMethodInstances
(
instances
,
method
)
matching
:=
filterEnabledVisibleMethodInstances
(
instances
,
method
)
providerKey
,
err
:=
s
.
resolveVisibleMethodProviderKey
(
ctx
,
method
,
matching
)
providerKey
,
err
:=
s
.
resolveVisibleMethodProviderKey
(
ctx
,
method
,
matching
)
if
err
!=
nil
||
providerKey
==
""
{
if
err
!=
nil
{
delete
(
filtered
,
method
)
delete
(
filtered
,
method
)
continue
continue
}
}
if
providerKey
==
""
{
if
len
(
matching
)
==
0
{
delete
(
filtered
,
method
)
continue
}
filtered
[
method
]
=
matching
continue
}
selectedInstances
:=
filterVisibleMethodInstancesByProviderKey
(
instances
,
method
,
providerKey
)
selectedInstances
:=
filterVisibleMethodInstancesByProviderKey
(
instances
,
method
,
providerKey
)
if
len
(
selectedInstances
)
==
0
{
if
len
(
selectedInstances
)
==
0
{
delete
(
filtered
,
method
)
delete
(
filtered
,
method
)
...
...
backend/internal/service/payment_config_limits_test.go
View file @
06136af8
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
dbent
"github.com/Wei-Shaw/sub2api/ent"
dbent
"github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/Wei-Shaw/sub2api/internal/payment"
"github.com/stretchr/testify/require"
)
)
func
TestUnionFloat
(
t
*
testing
.
T
)
{
func
TestUnionFloat
(
t
*
testing
.
T
)
{
...
@@ -402,3 +403,59 @@ func TestGetAvailableMethodLimitsUsesConfiguredVisibleMethodSource(t *testing.T)
...
@@ -402,3 +403,59 @@ func TestGetAvailableMethodLimitsUsesConfiguredVisibleMethodSource(t *testing.T)
})
})
}
}
}
}
func
TestGetAvailableMethodLimitsPreservesLegacyCrossProviderBehaviorWhenVisibleMethodSourceMissing
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
_
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeAlipay
)
.
SetName
(
"Official Alipay"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"alipay"
)
.
SetLimits
(
`{"alipay":{"singleMin":10,"singleMax":100}}`
)
.
SetEnabled
(
true
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeEasyPay
)
.
SetName
(
"EasyPay Mixed"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"alipay,wxpay"
)
.
SetLimits
(
`{"alipay":{"singleMin":20,"singleMax":200},"wxpay":{"singleMin":40,"singleMax":400}}`
)
.
SetEnabled
(
true
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeWxpay
)
.
SetName
(
"Official WeChat"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"wxpay"
)
.
SetLimits
(
`{"wxpay":{"singleMin":30,"singleMax":300}}`
)
.
SetEnabled
(
true
)
.
Save
(
ctx
)
require
.
NoError
(
t
,
err
)
svc
:=
&
PaymentConfigService
{
entClient
:
client
,
settingRepo
:
&
paymentConfigSettingRepoStub
{
values
:
map
[
string
]
string
{}},
}
resp
,
err
:=
svc
.
GetAvailableMethodLimits
(
ctx
)
require
.
NoError
(
t
,
err
)
alipayLimits
,
ok
:=
resp
.
Methods
[
payment
.
TypeAlipay
]
require
.
True
(
t
,
ok
,
"expected alipay limits to remain visible"
)
require
.
Equal
(
t
,
10.0
,
alipayLimits
.
SingleMin
)
require
.
Equal
(
t
,
200.0
,
alipayLimits
.
SingleMax
)
wxpayLimits
,
ok
:=
resp
.
Methods
[
payment
.
TypeWxpay
]
require
.
True
(
t
,
ok
,
"expected wxpay limits to remain visible"
)
require
.
Equal
(
t
,
30.0
,
wxpayLimits
.
SingleMin
)
require
.
Equal
(
t
,
400.0
,
wxpayLimits
.
SingleMax
)
require
.
Equal
(
t
,
10.0
,
resp
.
GlobalMin
)
require
.
Equal
(
t
,
400.0
,
resp
.
GlobalMax
)
}
backend/internal/service/payment_resume_service_test.go
View file @
06136af8
...
@@ -586,7 +586,60 @@ func TestVisibleMethodLoadBalancerUsesConfiguredSourceWhenMultipleProvidersEnabl
...
@@ -586,7 +586,60 @@ func TestVisibleMethodLoadBalancerUsesConfiguredSourceWhenMultipleProvidersEnabl
}
}
}
}
func
TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvidersEnabled
(
t
*
testing
.
T
)
{
func
TestVisibleMethodLoadBalancerPreservesLegacyCrossProviderRoutingWhenSourceMissing
(
t
*
testing
.
T
)
{
t
.
Parallel
()
ctx
:=
context
.
Background
()
client
:=
newPaymentConfigServiceTestClient
(
t
)
_
,
err
:=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeAlipay
)
.
SetName
(
"Official Alipay"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"alipay"
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
1
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create official provider: %v"
,
err
)
}
_
,
err
=
client
.
PaymentProviderInstance
.
Create
()
.
SetProviderKey
(
payment
.
TypeEasyPay
)
.
SetName
(
"EasyPay Alipay"
)
.
SetConfig
(
"{}"
)
.
SetSupportedTypes
(
"alipay"
)
.
SetEnabled
(
true
)
.
SetSortOrder
(
2
)
.
Save
(
ctx
)
if
err
!=
nil
{
t
.
Fatalf
(
"create easypay provider: %v"
,
err
)
}
inner
:=
&
captureLoadBalancer
{}
configService
:=
&
PaymentConfigService
{
entClient
:
client
,
settingRepo
:
&
paymentConfigSettingRepoStub
{
values
:
map
[
string
]
string
{
visibleMethodSourceSettingKey
(
payment
.
TypeAlipay
)
:
""
,
},
},
}
lb
:=
newVisibleMethodLoadBalancer
(
inner
,
configService
)
_
,
err
=
lb
.
SelectInstance
(
ctx
,
""
,
payment
.
TypeAlipay
,
payment
.
StrategyRoundRobin
,
9.9
)
if
err
!=
nil
{
t
.
Fatalf
(
"SelectInstance returned error: %v"
,
err
)
}
if
inner
.
lastProviderKey
!=
""
{
t
.
Fatalf
(
"lastProviderKey = %q, want legacy cross-provider empty key"
,
inner
.
lastProviderKey
)
}
if
inner
.
lastPaymentType
!=
payment
.
TypeAlipay
{
t
.
Fatalf
(
"lastPaymentType = %q, want %q"
,
inner
.
lastPaymentType
,
payment
.
TypeAlipay
)
}
}
func
TestVisibleMethodLoadBalancerRejectsInvalidSourceWhenMultipleProvidersEnabled
(
t
*
testing
.
T
)
{
t
.
Parallel
()
t
.
Parallel
()
tests
:=
[]
struct
{
tests
:=
[]
struct
{
...
@@ -595,12 +648,6 @@ func TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvi
...
@@ -595,12 +648,6 @@ func TestVisibleMethodLoadBalancerRejectsMissingOrInvalidSourceWhenMultipleProvi
sourceValue
string
sourceValue
string
wantMessage
string
wantMessage
string
}{
}{
{
name
:
"missing alipay source"
,
method
:
payment
.
TypeAlipay
,
sourceValue
:
""
,
wantMessage
:
"alipay source is required when the visible method is enabled"
,
},
{
{
name
:
"invalid wxpay source"
,
name
:
"invalid wxpay source"
,
method
:
payment
.
TypeWxpay
,
method
:
payment
.
TypeWxpay
,
...
...
backend/internal/service/payment_visible_method_instances.go
View file @
06136af8
...
@@ -2,6 +2,7 @@ package service
...
@@ -2,6 +2,7 @@ package service
import
(
import
(
"context"
"context"
"errors"
"fmt"
"fmt"
"strings"
"strings"
...
@@ -166,15 +167,21 @@ func (s *PaymentConfigService) resolveVisibleMethodSourceProviderKey(ctx context
...
@@ -166,15 +167,21 @@ func (s *PaymentConfigService) resolveVisibleMethodSourceProviderKey(ctx context
if
s
!=
nil
&&
s
.
settingRepo
!=
nil
&&
sourceKey
!=
""
{
if
s
!=
nil
&&
s
.
settingRepo
!=
nil
&&
sourceKey
!=
""
{
value
,
err
:=
s
.
settingRepo
.
GetValue
(
ctx
,
sourceKey
)
value
,
err
:=
s
.
settingRepo
.
GetValue
(
ctx
,
sourceKey
)
if
err
!=
nil
{
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"get %s: %w"
,
sourceKey
,
err
)
if
!
errors
.
Is
(
err
,
ErrSettingNotFound
)
{
return
""
,
fmt
.
Errorf
(
"get %s: %w"
,
sourceKey
,
err
)
}
}
else
{
rawSource
=
value
}
}
rawSource
=
value
}
}
normalizedSource
,
err
:=
normalizeVisibleMethodSettingSource
(
method
,
rawSource
,
true
)
normalizedSource
,
err
:=
normalizeVisibleMethodSettingSource
(
method
,
rawSource
,
true
)
if
err
!=
nil
{
if
err
!=
nil
{
return
""
,
err
return
""
,
err
}
}
if
normalizedSource
==
""
{
return
""
,
nil
}
providerKey
,
ok
:=
VisibleMethodProviderKeyForSource
(
method
,
normalizedSource
)
providerKey
,
ok
:=
VisibleMethodProviderKeyForSource
(
method
,
normalizedSource
)
if
!
ok
{
if
!
ok
{
return
""
,
infraerrors
.
BadRequest
(
return
""
,
infraerrors
.
BadRequest
(
...
@@ -200,6 +207,9 @@ func (s *PaymentConfigService) resolveVisibleMethodProviderKey(
...
@@ -200,6 +207,9 @@ func (s *PaymentConfigService) resolveVisibleMethodProviderKey(
if
err
!=
nil
{
if
err
!=
nil
{
return
""
,
err
return
""
,
err
}
}
if
providerKey
==
""
{
return
""
,
nil
}
selected
:=
selectVisibleMethodInstanceByProviderKey
(
matching
,
providerKey
)
selected
:=
selectVisibleMethodInstanceByProviderKey
(
matching
,
providerKey
)
if
selected
==
nil
{
if
selected
==
nil
{
return
""
,
infraerrors
.
BadRequest
(
return
""
,
infraerrors
.
BadRequest
(
...
@@ -237,5 +247,11 @@ func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
...
@@ -237,5 +247,11 @@ func (s *PaymentConfigService) resolveEnabledVisibleMethodInstance(
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
if
providerKey
==
""
{
if
len
(
matching
)
==
0
{
return
nil
,
nil
}
return
&
dbent
.
PaymentProviderInstance
{
ProviderKey
:
""
},
nil
}
return
selectVisibleMethodInstanceByProviderKey
(
matching
,
providerKey
),
nil
return
selectVisibleMethodInstanceByProviderKey
(
matching
,
providerKey
),
nil
}
}
backend/internal/service/setting_service.go
View file @
06136af8
...
@@ -282,7 +282,19 @@ func mergeWeChatConnectCapabilitySettings(settings map[string]string, base confi
...
@@ -282,7 +282,19 @@ func mergeWeChatConnectCapabilitySettings(settings map[string]string, base confi
mobileConfigured
:=
hasMobile
&&
strings
.
TrimSpace
(
rawMobile
)
!=
""
mobileConfigured
:=
hasMobile
&&
strings
.
TrimSpace
(
rawMobile
)
!=
""
if
openConfigured
||
mpConfigured
||
mobileConfigured
{
if
openConfigured
||
mpConfigured
||
mobileConfigured
{
return
parseWeChatConnectCapabilitySettings
(
settings
,
enabled
,
mode
)
openEnabled
:=
strings
.
TrimSpace
(
rawOpen
)
==
"true"
mpEnabled
:=
strings
.
TrimSpace
(
rawMP
)
==
"true"
mobileEnabled
:=
strings
.
TrimSpace
(
rawMobile
)
==
"true"
_
,
enabledConfigured
:=
settings
[
SettingKeyWeChatConnectEnabled
]
if
!
enabledConfigured
&&
enabled
&&
!
openEnabled
&&
!
mpEnabled
&&
!
mobileEnabled
&&
(
base
.
OpenEnabled
||
base
.
MPEnabled
||
base
.
MobileEnabled
)
{
return
base
.
OpenEnabled
,
base
.
MPEnabled
,
base
.
MobileEnabled
}
return
openEnabled
,
mpEnabled
,
mobileEnabled
}
}
if
!
enabled
{
if
!
enabled
{
return
false
,
false
,
false
return
false
,
false
,
false
...
@@ -1921,14 +1933,9 @@ func isFalseSettingValue(value string) bool {
...
@@ -1921,14 +1933,9 @@ func isFalseSettingValue(value string) bool {
}
}
func
normalizeVisibleMethodSettingSource
(
method
,
source
string
,
enabled
bool
)
(
string
,
error
)
{
func
normalizeVisibleMethodSettingSource
(
method
,
source
string
,
enabled
bool
)
(
string
,
error
)
{
_
=
enabled
source
=
strings
.
TrimSpace
(
source
)
source
=
strings
.
TrimSpace
(
source
)
if
source
==
""
{
if
source
==
""
{
if
enabled
{
return
""
,
infraerrors
.
BadRequest
(
"INVALID_PAYMENT_VISIBLE_METHOD_SOURCE"
,
fmt
.
Sprintf
(
"%s source is required when the visible method is enabled"
,
method
),
)
}
return
""
,
nil
return
""
,
nil
}
}
...
...
backend/internal/service/setting_service_wechat_config_test.go
View file @
06136af8
...
@@ -109,6 +109,36 @@ func TestSettingService_GetWeChatConnectOAuthConfig_FallsBackToConfigWhenDatabas
...
@@ -109,6 +109,36 @@ func TestSettingService_GetWeChatConnectOAuthConfig_FallsBackToConfigWhenDatabas
require
.
Empty
(
t
,
got
.
RedirectURL
)
require
.
Empty
(
t
,
got
.
RedirectURL
)
}
}
func
TestSettingService_GetWeChatConnectOAuthConfig_IgnoresSyntheticDisabledCapabilitiesFromMigration118
(
t
*
testing
.
T
)
{
repo
:=
&
settingWeChatRepoStub
{
values
:
map
[
string
]
string
{
SettingKeyWeChatConnectOpenEnabled
:
"false"
,
SettingKeyWeChatConnectMPEnabled
:
"false"
,
},
}
svc
:=
NewSettingService
(
repo
,
&
config
.
Config
{
WeChat
:
config
.
WeChatConnectConfig
{
Enabled
:
true
,
OpenEnabled
:
true
,
MPEnabled
:
true
,
Mode
:
"open"
,
OpenAppID
:
"wx-open-config"
,
OpenAppSecret
:
"wx-open-secret"
,
MPAppID
:
"wx-mp-config"
,
MPAppSecret
:
"wx-mp-secret"
,
FrontendRedirectURL
:
"/auth/wechat/config-callback"
,
},
})
got
,
err
:=
svc
.
GetWeChatConnectOAuthConfig
(
context
.
Background
())
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
got
.
Enabled
)
require
.
True
(
t
,
got
.
OpenEnabled
)
require
.
True
(
t
,
got
.
MPEnabled
)
require
.
Equal
(
t
,
"wx-open-config"
,
got
.
AppIDForMode
(
"open"
))
require
.
Equal
(
t
,
"wx-mp-config"
,
got
.
AppIDForMode
(
"mp"
))
}
func
TestSettingService_ParseSettings_FallsBackToConfigForWeChatAdminView
(
t
*
testing
.
T
)
{
func
TestSettingService_ParseSettings_FallsBackToConfigForWeChatAdminView
(
t
*
testing
.
T
)
{
svc
:=
NewSettingService
(
&
settingWeChatRepoStub
{
values
:
map
[
string
]
string
{}},
&
config
.
Config
{
svc
:=
NewSettingService
(
&
settingWeChatRepoStub
{
values
:
map
[
string
]
string
{}},
&
config
.
Config
{
WeChat
:
config
.
WeChatConnectConfig
{
WeChat
:
config
.
WeChatConnectConfig
{
...
...
backend/migrations/118_wechat_dual_mode_and_auth_source_defaults.sql
View file @
06136af8
...
@@ -3,6 +3,7 @@ VALUES
...
@@ -3,6 +3,7 @@ VALUES
(
(
'wechat_connect_open_enabled'
,
'wechat_connect_open_enabled'
,
CASE
CASE
WHEN
NOT
EXISTS
(
SELECT
1
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
)
THEN
''
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'false'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'false'
ELSE
'true'
ELSE
'true'
...
@@ -11,6 +12,7 @@ VALUES
...
@@ -11,6 +12,7 @@ VALUES
(
(
'wechat_connect_mp_enabled'
,
'wechat_connect_mp_enabled'
,
CASE
CASE
WHEN
NOT
EXISTS
(
SELECT
1
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
)
THEN
''
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'true'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'true'
ELSE
'false'
ELSE
'false'
...
...
backend/migrations/120_enforce_payment_orders_out_trade_no_unique_notx.sql
View file @
06136af8
-- Build the payment order uniqueness guarantee online.
-- Build the payment order uniqueness guarantee online.
-- Create the new partial unique index concurrently first so writes keep flowing,
-- Create the new partial unique index concurrently first so writes keep flowing,
-- then remove the legacy index name once the replacement is ready.
-- then remove the legacy index name once the replacement is ready.
DROP
INDEX
CONCURRENTLY
IF
EXISTS
paymentorder_out_trade_no_unique
;
CREATE
UNIQUE
INDEX
CONCURRENTLY
IF
NOT
EXISTS
paymentorder_out_trade_no_unique
CREATE
UNIQUE
INDEX
CONCURRENTLY
IF
NOT
EXISTS
paymentorder_out_trade_no_unique
ON
payment_orders
(
out_trade_no
)
ON
payment_orders
(
out_trade_no
)
WHERE
out_trade_no
<>
''
;
WHERE
out_trade_no
<>
''
;
...
...
backend/migrations/123_fix_legacy_auth_source_grant_on_signup_defaults.sql
View file @
06136af8
WITH
migration_110
AS
(
-- Intentionally left as a no-op.
SELECT
applied_at
-- Legacy installs may have intentionally kept the original signup grant defaults,
FROM
schema_migrations
-- and we cannot distinguish those cases safely from untouched migration 110 rows.
WHERE
filename
=
'110_pending_auth_and_provider_default_grants.sql'
),
legacy_provider_defaults
AS
(
SELECT
provider_type
FROM
(
VALUES
(
'email'
),
(
'linuxdo'
),
(
'oidc'
),
(
'wechat'
)
)
AS
providers
(
provider_type
)
CROSS
JOIN
migration_110
JOIN
settings
balance
ON
balance
.
key
=
'auth_source_default_'
||
providers
.
provider_type
||
'_balance'
JOIN
settings
concurrency
ON
concurrency
.
key
=
'auth_source_default_'
||
providers
.
provider_type
||
'_concurrency'
JOIN
settings
subscriptions
ON
subscriptions
.
key
=
'auth_source_default_'
||
providers
.
provider_type
||
'_subscriptions'
JOIN
settings
grant_on_signup
ON
grant_on_signup
.
key
=
'auth_source_default_'
||
providers
.
provider_type
||
'_grant_on_signup'
JOIN
settings
grant_on_first_bind
ON
grant_on_first_bind
.
key
=
'auth_source_default_'
||
providers
.
provider_type
||
'_grant_on_first_bind'
WHERE
balance
.
value
=
'0'
AND
concurrency
.
value
=
'5'
AND
subscriptions
.
value
=
'[]'
AND
grant_on_signup
.
value
=
'true'
AND
grant_on_first_bind
.
value
=
'false'
AND
balance
.
updated_at
BETWEEN
migration_110
.
applied_at
-
INTERVAL
'1 minute'
AND
migration_110
.
applied_at
+
INTERVAL
'1 minute'
AND
concurrency
.
updated_at
BETWEEN
migration_110
.
applied_at
-
INTERVAL
'1 minute'
AND
migration_110
.
applied_at
+
INTERVAL
'1 minute'
AND
subscriptions
.
updated_at
BETWEEN
migration_110
.
applied_at
-
INTERVAL
'1 minute'
AND
migration_110
.
applied_at
+
INTERVAL
'1 minute'
AND
grant_on_signup
.
updated_at
BETWEEN
migration_110
.
applied_at
-
INTERVAL
'1 minute'
AND
migration_110
.
applied_at
+
INTERVAL
'1 minute'
AND
grant_on_first_bind
.
updated_at
BETWEEN
migration_110
.
applied_at
-
INTERVAL
'1 minute'
AND
migration_110
.
applied_at
+
INTERVAL
'1 minute'
)
UPDATE
settings
SET
value
=
'false'
,
updated_at
=
NOW
()
FROM
legacy_provider_defaults
WHERE
settings
.
key
=
'auth_source_default_'
||
legacy_provider_defaults
.
provider_type
||
'_grant_on_signup'
AND
settings
.
value
=
'true'
;
backend/migrations/auth_identity_payment_migrations_regression_test.go
View file @
06136af8
...
@@ -24,6 +24,7 @@ func TestMigration118DoesNotForceOverwriteAuthSourceGrantDefaults(t *testing.T)
...
@@ -24,6 +24,7 @@ func TestMigration118DoesNotForceOverwriteAuthSourceGrantDefaults(t *testing.T)
require
.
NotContains
(
t
,
sql
,
"UPDATE settings"
)
require
.
NotContains
(
t
,
sql
,
"UPDATE settings"
)
require
.
NotContains
(
t
,
sql
,
"SET value = 'false'"
)
require
.
NotContains
(
t
,
sql
,
"SET value = 'false'"
)
require
.
True
(
t
,
strings
.
Contains
(
sql
,
"ON CONFLICT (key) DO NOTHING"
))
require
.
True
(
t
,
strings
.
Contains
(
sql
,
"ON CONFLICT (key) DO NOTHING"
))
require
.
Contains
(
t
,
sql
,
"THEN ''"
)
}
}
func
TestAuthIdentityReportTypeWideningRunsBeforeLongReportWritersAndStillReconcilesAt121
(
t
*
testing
.
T
)
{
func
TestAuthIdentityReportTypeWideningRunsBeforeLongReportWritersAndStillReconcilesAt121
(
t
*
testing
.
T
)
{
...
@@ -63,6 +64,7 @@ func TestMigration119DefersPaymentIndexRolloutToOnlineFollowup(t *testing.T) {
...
@@ -63,6 +64,7 @@ func TestMigration119DefersPaymentIndexRolloutToOnlineFollowup(t *testing.T) {
followupSQL
:=
string
(
followupContent
)
followupSQL
:=
string
(
followupContent
)
require
.
Contains
(
t
,
followupSQL
,
"CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS paymentorder_out_trade_no_unique"
)
require
.
Contains
(
t
,
followupSQL
,
"CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS paymentorder_out_trade_no_unique"
)
require
.
NotContains
(
t
,
followupSQL
,
"DROP INDEX CONCURRENTLY IF EXISTS paymentorder_out_trade_no_unique"
)
require
.
Contains
(
t
,
followupSQL
,
"DROP INDEX CONCURRENTLY IF EXISTS paymentorder_out_trade_no"
)
require
.
Contains
(
t
,
followupSQL
,
"DROP INDEX CONCURRENTLY IF EXISTS paymentorder_out_trade_no"
)
require
.
Contains
(
t
,
followupSQL
,
"WHERE out_trade_no <> ''"
)
require
.
Contains
(
t
,
followupSQL
,
"WHERE out_trade_no <> ''"
)
...
@@ -92,9 +94,7 @@ func TestMigration123BackfillsLegacyAuthSourceGrantDefaultsSafely(t *testing.T)
...
@@ -92,9 +94,7 @@ func TestMigration123BackfillsLegacyAuthSourceGrantDefaultsSafely(t *testing.T)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
sql
:=
string
(
content
)
sql
:=
string
(
content
)
require
.
Contains
(
t
,
sql
,
"110_pending_auth_and_provider_default_grants.sql"
)
require
.
Contains
(
t
,
sql
,
"Intentionally left as a no-op"
)
require
.
Contains
(
t
,
sql
,
"schema_migrations"
)
require
.
NotContains
(
t
,
sql
,
"UPDATE settings"
)
require
.
Contains
(
t
,
sql
,
"updated_at"
)
require
.
NotContains
(
t
,
sql
,
"value = 'false'"
)
require
.
Contains
(
t
,
sql
,
"'_grant_on_signup'"
)
require
.
Contains
(
t
,
sql
,
"value = 'false'"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment