package config import ( "testing" "time" ) // ShouldRetry 是纯数据判据:状态码列表 + 响应体子串。它决定"这次失败值不值得 // 在网关内部等一会儿重试"。厂商知识绝不能进内核,所以判据必须能用一个表格测清楚。 func TestRetryPolicyShouldRetry(t *testing.T) { cases := []struct { name string policy *RetryPolicy status int body string want bool }{ {"nil policy", nil, 500, "anything", false}, {"max_attempts=1 disables", &RetryPolicy{OnStatus: []int{400}, MaxAttempts: 1}, 400, "x", false}, {"status matches, no body filter", &RetryPolicy{OnStatus: []int{429}, MaxAttempts: 3}, 429, "", true}, {"status mismatch", &RetryPolicy{OnStatus: []int{429}, MaxAttempts: 3}, 400, "429", false}, {"status + body match", &RetryPolicy{OnStatus: []int{400}, OnBodyContains: []string{"TM.00001041"}, MaxAttempts: 3}, 400, `{"error_code":"TM.00001041"}`, true}, {"status matches but body does not", &RetryPolicy{OnStatus: []int{400}, OnBodyContains: []string{"TM.00001041"}, MaxAttempts: 3}, 400, `{"error_code":"TM.00001005"}`, false}, {"empty substring never matches", &RetryPolicy{OnStatus: []int{400}, OnBodyContains: []string{""}, MaxAttempts: 3}, 400, "x", false}, {"any of several body signals", &RetryPolicy{OnStatus: []int{400, 429}, OnBodyContains: []string{"TM.00001041", "TPM limit"}, MaxAttempts: 3}, 429, `{"message":"TPM limit exceeded"}`, true}, } for _, tc := range cases { if got := tc.policy.ShouldRetry(tc.status, tc.body); got != tc.want { t.Errorf("%s: ShouldRetry(%d, %q) = %v, want %v", tc.name, tc.status, tc.body, got, tc.want) } } } func TestRetryPolicyDelay(t *testing.T) { cases := []struct { name string policy *RetryPolicy want time.Duration }{ {"nil", nil, 10 * time.Second}, {"explicit", &RetryPolicy{Delay: "250ms"}, 250 * time.Millisecond}, {"garbage falls back", &RetryPolicy{Delay: "not-a-duration"}, 10 * time.Second}, {"negative falls back", &RetryPolicy{Delay: "-5s"}, 10 * time.Second}, } for _, tc := range cases { if got := tc.policy.DelayDuration(); got != tc.want { t.Errorf("%s: DelayDuration() = %v, want %v", tc.name, got, tc.want) } } }