test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
//go:build unit
|
|
|
|
|
|
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_AllValid(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_EmptyName(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("", 1, 9.99, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "plan name")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_WhitespaceName(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired(" ", 1, 9.99, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "plan name")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_ZeroGroupID(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 0, 9.99, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "group")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_NegativeGroupID(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", -1, 9.99, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "group")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_ZeroPrice(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 0, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_NegativePrice(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, -5, 30, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_ZeroValidityDays(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 0, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity days")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_NegativeValidityDays(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, -7, "days", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity days")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_EmptyValidityUnit(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, "", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity unit")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_WhitespaceValidityUnit(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, " ", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity unit")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_NameValidatedFirst(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired("", 0, 0, 0, "", nil)
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "plan name")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_TrimmedValidName(t *testing.T) {
|
2026-04-13 23:35:59 +08:00
|
|
|
err := validatePlanRequired(" Pro ", 1, 9.99, 30, "days", nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_NegativeOriginalPrice(t *testing.T) {
|
|
|
|
|
neg := -10.0
|
|
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, "days", &neg)
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "original price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_ZeroOriginalPrice(t *testing.T) {
|
|
|
|
|
zero := 0.0
|
|
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, "days", &zero)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanRequired_ValidOriginalPrice(t *testing.T) {
|
|
|
|
|
op := 19.99
|
|
|
|
|
err := validatePlanRequired("Pro", 1, 9.99, 30, "days", &op)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- validatePlanPatch tests ---
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_NegativeOriginalPrice(t *testing.T) {
|
|
|
|
|
neg := -5.0
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{OriginalPrice: &neg})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "original price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ZeroOriginalPrice(t *testing.T) {
|
|
|
|
|
zero := 0.0
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{OriginalPrice: &zero})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ValidOriginalPrice(t *testing.T) {
|
|
|
|
|
op := 29.99
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{OriginalPrice: &op})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_NilOriginalPrice(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{OriginalPrice: nil})
|
test: add 66 unit tests for balance/quota notify + plan validation
balance_notify_service_test.go (27 tests):
- resolveBalanceThreshold: fixed/percentage/zero recharged/empty type
- quotaDim.resolvedThreshold: fixed normal/exceed/equal limit, percentage 0/30/100/>100, zero/negative limit
- sanitizeEmailHeader: CRLF/CR/LF/clean/empty/multiple newlines
- buildQuotaDims / buildQuotaDimsFromState: all dimensions, empty extra, state-vs-account precedence
- collectBalanceNotifyRecipients: empty, filter disabled/unverified, case-insensitive dedup, skip empty, trim
balance_notify_check_test.go (16 tests):
- CheckBalanceAfterDeduction guard clauses: nil user/disabled/global-off/threshold=0/user-override/no-crossing
- CheckAccountQuotaAfterIncrement guards: nil account/zero cost/negative cost/global-disabled
- getBalanceNotifyConfig: all fields, disabled, invalid threshold
- isAccountQuotaNotifyEnabled: missing/false/true
- getSiteName: default fallback + configured
balance_notify_email_body_test.go (10 tests):
- Guards against fmt.Sprintf arg-count mismatches in email templates
- Verifies HTML escaping of recharge URL
- Verifies CSS %% escape produces literal % in output
- Verifies unlimited/percentage/over-quota display branches
payment_config_plans_validation_test.go (13 tests):
- validatePlanRequired: all 5 validation branches + whitespace handling
2026-04-13 20:35:38 +08:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
2026-04-14 00:26:20 +08:00
|
|
|
|
|
|
|
|
// --- validatePlanPatch: other fields ---
|
|
|
|
|
|
2026-04-14 07:43:08 +08:00
|
|
|
func ptrStr(s string) *string { return &s }
|
|
|
|
|
func ptrInt(i int) *int { return &i }
|
|
|
|
|
func ptrInt64(i int64) *int64 { return &i }
|
2026-04-14 00:26:20 +08:00
|
|
|
func ptrFloat(f float64) *float64 { return &f }
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_EmptyName(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{Name: ptrStr("")})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "plan name")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ValidName(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{Name: ptrStr("Basic")})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ZeroGroupID(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{GroupID: ptrInt64(0)})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "group")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_NegativePrice(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{Price: ptrFloat(-1)})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ZeroPrice(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{Price: ptrFloat(0)})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "price")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ValidPrice(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{Price: ptrFloat(9.99)})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ZeroValidityDays(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{ValidityDays: ptrInt(0)})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity days")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_EmptyValidityUnit(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{ValidityUnit: ptrStr("")})
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Contains(t, err.Error(), "validity unit")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_ValidValidityUnit(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{ValidityUnit: ptrStr("days")})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidatePlanPatch_AllNil(t *testing.T) {
|
|
|
|
|
err := validatePlanPatch(UpdatePlanRequest{})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|