189 lines
7.6 KiB
Go
189 lines
7.6 KiB
Go
//go:build unit
|
|||
|
|
|
||
|
|
package admin
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
||
|
|
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Saving settings is a whole-document PUT. A client that sends only the field it
|
||
|
|
// cares about must not reset everything else: a payload as small as
|
||
|
|
// `{"risk_control_enabled":true}` used to clear site_name, after which
|
||
|
|
// getStringOrDefault rendered the empty value as the built-in default and the
|
||
|
|
// login page silently changed name.
|
||
|
|
|
||
|
|
func TestUpdateSettingsPartialPayloadKeepsUnsentKeys(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeySiteName: "Example Gateway",
|
||
|
|
service.SettingKeySiteSubtitle: "Example Gateway Platform",
|
||
|
|
service.SettingKeySMTPHost: "smtp.example.com",
|
||
|
|
service.SettingKeySMTPFrom: "noreply@example.com",
|
||
|
|
service.SettingKeyTurnstileEnabled: "true",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"risk_control_enabled": true}, nil)
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
|
||
|
|
require.Equal(t, "true", repo.values[service.SettingKeyRiskControlEnabled],
|
||
|
|
"the field the caller actually sent must be written")
|
||
|
|
|
||
|
|
require.Equal(t, "Example Gateway", repo.values[service.SettingKeySiteName])
|
||
|
|
require.Equal(t, "Example Gateway Platform", repo.values[service.SettingKeySiteSubtitle])
|
||
|
|
require.Equal(t, "smtp.example.com", repo.values[service.SettingKeySMTPHost])
|
||
|
|
require.Equal(t, "noreply@example.com", repo.values[service.SettingKeySMTPFrom])
|
||
|
|
require.Equal(t, "true", repo.values[service.SettingKeyTurnstileEnabled])
|
||
|
|
}
|
||
|
|
|
||
|
|
// A full payload keeps whole-document semantics: fields explicitly set to their
|
||
|
|
// zero value are still cleared.
|
||
|
|
func TestUpdateSettingsFullPayloadStillClearsSentEmptyFields(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeySiteName: "Example Gateway",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"site_name": ""}, nil)
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
|
||
|
|
require.Equal(t, "", repo.values[service.SettingKeySiteName],
|
||
|
|
"an explicitly sent empty value is a deliberate clear, not an omission")
|
||
|
|
}
|
||
|
|
|
||
|
|
// smtp_from_email is the one request field whose JSON name differs from its
|
||
|
|
// setting key; the alias keeps it from being treated as always-omitted.
|
||
|
|
func TestUpdateSettingsSMTPFromAliasIsWritable(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeySMTPFrom: "old@example.com",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"smtp_from_email": "new@example.com"}, nil)
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
|
||
|
|
require.Equal(t, "new@example.com", repo.values[service.SettingKeySMTPFrom])
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsGrokDefaultBaseURLModeIsWritable(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyGrokDefaultBaseURLMode: service.GrokDefaultBaseURLModeCLI,
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{
|
||
|
|
"grok_default_base_url_mode": service.GrokDefaultBaseURLModeEUWest1,
|
||
|
|
}, nil)
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
require.Equal(t, service.GrokDefaultBaseURLModeEUWest1, repo.values[service.SettingKeyGrokDefaultBaseURLMode])
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsRejectsTwoCaptchaProviders(t *testing.T) {
|
||
|
|
h, _ := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyTurnstileEnabled: "true",
|
||
|
|
service.SettingKeyTurnstileSiteKey: "site-key",
|
||
|
|
service.SettingKeyTurnstileSecretKey: "turnstile-secret",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{
|
||
|
|
"turnstile_enabled": true,
|
||
|
|
"turnstile_site_key": "site-key",
|
||
|
|
"turnstile_secret_key": "turnstile-secret",
|
||
|
|
"tencent_captcha_enabled": true,
|
||
|
|
"tencent_captcha_app_id": "123456789",
|
||
|
|
"tencent_captcha_app_secret_key": "app-secret",
|
||
|
|
"tencent_captcha_cloud_secret_id": "cloud-secret-id",
|
||
|
|
"tencent_captcha_cloud_secret_key": "cloud-secret-key",
|
||
|
|
}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||
|
|
require.Contains(t, rec.Body.String(), "cannot be enabled at the same time")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsRequiresFourTencentCaptchaCredentialsWhenEnabled(t *testing.T) {
|
||
|
|
h, _ := newStepUpSwitchTestHandler(t, map[string]string{})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{
|
||
|
|
"tencent_captcha_enabled": true,
|
||
|
|
"tencent_captcha_app_id": "123456789",
|
||
|
|
}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||
|
|
require.Contains(t, rec.Body.String(), "AppSecretKey")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsRetainsStoredTencentCaptchaCredentialsWhenInputsEmpty(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyTencentCaptchaAppSecretKey: "stored-app-secret",
|
||
|
|
service.SettingKeyTencentCaptchaCloudSecretID: "stored-cloud-secret-id",
|
||
|
|
service.SettingKeyTencentCaptchaCloudSecretKey: "stored-cloud-secret-key",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{
|
||
|
|
"tencent_captcha_enabled": true,
|
||
|
|
"tencent_captcha_app_id": "123456789",
|
||
|
|
"tencent_captcha_app_secret_key": "",
|
||
|
|
"tencent_captcha_cloud_secret_id": "",
|
||
|
|
"tencent_captcha_cloud_secret_key": "",
|
||
|
|
}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
require.Equal(t, "stored-app-secret", repo.values[service.SettingKeyTencentCaptchaAppSecretKey])
|
||
|
|
require.Equal(t, "stored-cloud-secret-id", repo.values[service.SettingKeyTencentCaptchaCloudSecretID])
|
||
|
|
require.Equal(t, "stored-cloud-secret-key", repo.values[service.SettingKeyTencentCaptchaCloudSecretKey])
|
||
|
|
}
|
||
|
|
|
||
|
|
// 天御站点决定前端加载哪个 SDK 与服务端打哪个接入点,两端必须一致。
|
||
|
|
// 部分载荷把它重置回中国站,会让已配国际站的部署在下一次任意保存后整体失效。
|
||
|
|
func TestUpdateSettingsPartialPayloadKeepsTencentCaptchaRegion(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyTencentCaptchaRegion: service.TencentCaptchaRegionINTL,
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"risk_control_enabled": true}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
require.Equal(t, service.TencentCaptchaRegionINTL,
|
||
|
|
repo.values[service.SettingKeyTencentCaptchaRegion])
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsNormalizesUnknownTencentCaptchaRegion(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyTencentCaptchaRegion: service.TencentCaptchaRegionINTL,
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"tencent_captcha_region": "sgp"}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
require.Equal(t, service.TencentCaptchaRegionCN,
|
||
|
|
repo.values[service.SettingKeyTencentCaptchaRegion],
|
||
|
|
"未知站点必须落回中国站,不能写入无法识别的值")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsWritesTencentCaptchaRegionWhenSent(t *testing.T) {
|
||
|
|
h, repo := newStepUpSwitchTestHandler(t, map[string]string{})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{"tencent_captcha_region": "intl"}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
||
|
|
require.Equal(t, service.TencentCaptchaRegionINTL,
|
||
|
|
repo.values[service.SettingKeyTencentCaptchaRegion])
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestUpdateSettingsValidatesTencentCaptchaAppIDWhenEnabledFlagIsOmitted(t *testing.T) {
|
||
|
|
h, _ := newStepUpSwitchTestHandler(t, map[string]string{
|
||
|
|
service.SettingKeyTencentCaptchaEnabled: "true",
|
||
|
|
service.SettingKeyTencentCaptchaAppID: "123456789",
|
||
|
|
service.SettingKeyTencentCaptchaAppSecretKey: "stored-app-secret",
|
||
|
|
service.SettingKeyTencentCaptchaCloudSecretID: "stored-cloud-secret-id",
|
||
|
|
service.SettingKeyTencentCaptchaCloudSecretKey: "stored-cloud-secret-key",
|
||
|
|
})
|
||
|
|
|
||
|
|
rec := doUpdateSettings(t, h, map[string]any{
|
||
|
|
"tencent_captcha_app_id": "not-a-number",
|
||
|
|
}, nil)
|
||
|
|
|
||
|
|
require.Equal(t, http.StatusBadRequest, rec.Code)
|
||
|
|
require.Contains(t, rec.Body.String(), "positive integer")
|
||
|
|
}
|