Files
lijiaoqiao/gateway/pkg/error/error_test.go
Your Name d90cc382a4 fix: 验证并修复comprehensive_review_v4问题
已验证的问题状态:
1. P0-07补偿处理器 - 已集成到main.go 
2. P0-09外键校验器 - 已集成到main.go并调用 
3. 幂等协议Idempotency-Key - 已在idempotency.go实现 
4. 幂等唯一索引 - 已在SQL中定义 

Gateway修复:
- 修复cors.go语法错误(重复函数定义)
- 修复middleware_test.go参数不匹配问题
- 修复go.mod降级到go 1.21解决依赖问题
2026-04-08 20:17:07 +08:00

325 lines
8.9 KiB
Go

package error
import (
"errors"
"testing"
)
func TestErrorCodes(t *testing.T) {
// 验证所有错误码常量
tests := []struct {
code ErrorCode
expected string
}{
{AUTH_INVALID_TOKEN, "AUTH_001"},
{AUTH_INSUFFICIENT_PERMISSION, "AUTH_002"},
{AUTH_MFA_REQUIRED, "AUTH_003"},
{BILLING_INSUFFICIENT_BALANCE, "BILLING_001"},
{BILLING_CHARGE_FAILED, "BILLING_002"},
{BILLING_REFUND_FAILED, "BILLING_003"},
{BILLING_DISCREPANCY, "BILLING_004"},
{ROUTER_NO_PROVIDER_AVAILABLE, "ROUTER_001"},
{ROUTER_ALL_PROVIDERS_FAILED, "ROUTER_002"},
{ROUTER_TIMEOUT, "ROUTER_003"},
{PROVIDER_INVALID_KEY, "PROVIDER_001"},
{PROVIDER_RATE_LIMIT, "PROVIDER_002"},
{PROVIDER_QUOTA_EXCEEDED, "PROVIDER_003"},
{PROVIDER_MODEL_NOT_FOUND, "PROVIDER_004"},
{PROVIDER_ERROR, "PROVIDER_005"},
{RATE_LIMIT_EXCEEDED, "RATE_LIMIT_001"},
{RATE_LIMIT_TOKEN_EXCEEDED, "RATE_LIMIT_002"},
{RATE_LIMIT_BURST_EXCEEDED, "RATE_LIMIT_003"},
{COMMON_INVALID_REQUEST, "COMMON_001"},
{COMMON_RESOURCE_NOT_FOUND, "COMMON_002"},
{COMMON_INTERNAL_ERROR, "COMMON_003"},
{COMMON_SERVICE_UNAVAILABLE, "COMMON_004"},
}
for _, tt := range tests {
if string(tt.code) != tt.expected {
t.Errorf("expected %s, got %s", tt.expected, tt.code)
}
}
}
func TestNewGatewayError(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "test message")
if err.Code != COMMON_INVALID_REQUEST {
t.Errorf("expected code COMMON_INVALID_REQUEST, got %s", err.Code)
}
if err.Message != "test message" {
t.Errorf("expected message 'test message', got %s", err.Message)
}
if err.Details == nil {
t.Error("expected Details to be initialized")
}
}
func TestGatewayError_Error(t *testing.T) {
tests := []struct {
name string
err *GatewayError
expected string
}{
{
name: "without internal error",
err: NewGatewayError(COMMON_INVALID_REQUEST, "test"),
expected: "COMMON_001: test",
},
{
name: "with internal error",
err: NewGatewayError(COMMON_INTERNAL_ERROR, "outer").WithInternal(errors.New("inner")),
expected: "COMMON_003: outer (caused by: inner)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.err.Error(); got != tt.expected {
t.Errorf("Error() = %v, want %v", got, tt.expected)
}
})
}
}
func TestGatewayError_Unwrap(t *testing.T) {
internalErr := errors.New("inner error")
err := NewGatewayError(COMMON_INTERNAL_ERROR, "outer").WithInternal(internalErr)
if err.Unwrap() != internalErr {
t.Error("Unwrap() should return the internal error")
}
}
func TestGatewayError_WithRequestID(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "test")
result := err.WithRequestID("req-123")
if err.RequestID != "req-123" {
t.Errorf("expected RequestID req-123, got %s", err.RequestID)
}
if result != err {
t.Error("WithRequestID should return the same error")
}
}
func TestGatewayError_WithDetail(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "test")
result := err.WithDetail("key", "value")
if err.Details["key"] != "value" {
t.Errorf("expected Details[key] = value, got %v", err.Details["key"])
}
if result != err {
t.Error("WithDetail should return the same error")
}
}
func TestGatewayError_WithInternal(t *testing.T) {
internalErr := errors.New("internal error")
err := NewGatewayError(COMMON_INVALID_REQUEST, "test")
result := err.WithInternal(internalErr)
if err.Internal != internalErr {
t.Error("expected Internal to be set")
}
if result != err {
t.Error("WithInternal should return the same error")
}
}
func TestGetErrorInfo(t *testing.T) {
tests := []struct {
code ErrorCode
expectedStatus int
expectedRetry bool
}{
{AUTH_INVALID_TOKEN, 401, false},
{AUTH_INSUFFICIENT_PERMISSION, 403, false},
{AUTH_MFA_REQUIRED, 403, false},
{BILLING_INSUFFICIENT_BALANCE, 402, false},
{BILLING_CHARGE_FAILED, 500, true},
{BILLING_REFUND_FAILED, 500, true},
{BILLING_DISCREPANCY, 500, true},
{ROUTER_NO_PROVIDER_AVAILABLE, 503, true},
{ROUTER_ALL_PROVIDERS_FAILED, 503, true},
{ROUTER_TIMEOUT, 504, true},
{PROVIDER_INVALID_KEY, 401, false},
{PROVIDER_RATE_LIMIT, 429, true},
{PROVIDER_QUOTA_EXCEEDED, 402, false},
{PROVIDER_MODEL_NOT_FOUND, 404, false},
{PROVIDER_ERROR, 502, true},
{RATE_LIMIT_EXCEEDED, 429, false},
{RATE_LIMIT_TOKEN_EXCEEDED, 429, false},
{RATE_LIMIT_BURST_EXCEEDED, 429, false},
{COMMON_INVALID_REQUEST, 400, false},
{COMMON_RESOURCE_NOT_FOUND, 404, false},
{COMMON_INTERNAL_ERROR, 500, true},
{COMMON_SERVICE_UNAVAILABLE, 503, true},
}
for _, tt := range tests {
t.Run(string(tt.code), func(t *testing.T) {
err := NewGatewayError(tt.code, "test")
info := err.GetErrorInfo()
if info.HTTPStatus != tt.expectedStatus {
t.Errorf("code %s: expected status %d, got %d", tt.code, tt.expectedStatus, info.HTTPStatus)
}
if info.Retryable != tt.expectedRetry {
t.Errorf("code %s: expected retryable %v, got %v", tt.code, tt.expectedRetry, info.Retryable)
}
})
}
}
func TestGetErrorInfo_UnknownCode(t *testing.T) {
err := NewGatewayError("UNKNOWN_CODE", "test")
info := err.GetErrorInfo()
// 未知错误码应返回默认值
if info.HTTPStatus != 500 {
t.Errorf("expected status 500, got %d", info.HTTPStatus)
}
if info.Retryable != true {
t.Error("expected retryable true for unknown code")
}
if info.Code != COMMON_INTERNAL_ERROR {
t.Errorf("expected code COMMON_INTERNAL_ERROR, got %s", info.Code)
}
}
func TestErrorInfo_Struct(t *testing.T) {
info := ErrorInfo{
Code: COMMON_INVALID_REQUEST,
Message: "test message",
HTTPStatus: 400,
Retryable: false,
}
if info.Code != COMMON_INVALID_REQUEST {
t.Errorf("expected code COMMON_INVALID_REQUEST, got %s", info.Code)
}
if info.Message != "test message" {
t.Errorf("expected message 'test message', got %s", info.Message)
}
if info.HTTPStatus != 400 {
t.Errorf("expected HTTPStatus 400, got %d", info.HTTPStatus)
}
if info.Retryable != false {
t.Error("expected Retryable false")
}
}
func TestGatewayError_Chaining(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "test").
WithRequestID("req-123").
WithDetail("field", "email").
WithDetail("reason", "invalid format")
if err.RequestID != "req-123" {
t.Errorf("expected RequestID req-123, got %s", err.RequestID)
}
if err.Details["field"] != "email" {
t.Errorf("expected field=email, got %v", err.Details["field"])
}
if err.Details["reason"] != "invalid format" {
t.Errorf("expected reason=invalid format, got %v", err.Details["reason"])
}
}
func TestErrorDefinitions_Completeness(t *testing.T) {
// 确保所有错误码都在ErrorDefinitions中定义
codes := []ErrorCode{
AUTH_INVALID_TOKEN,
AUTH_INSUFFICIENT_PERMISSION,
AUTH_MFA_REQUIRED,
BILLING_INSUFFICIENT_BALANCE,
BILLING_CHARGE_FAILED,
BILLING_REFUND_FAILED,
BILLING_DISCREPANCY,
ROUTER_NO_PROVIDER_AVAILABLE,
ROUTER_ALL_PROVIDERS_FAILED,
ROUTER_TIMEOUT,
PROVIDER_INVALID_KEY,
PROVIDER_RATE_LIMIT,
PROVIDER_QUOTA_EXCEEDED,
PROVIDER_MODEL_NOT_FOUND,
PROVIDER_ERROR,
RATE_LIMIT_EXCEEDED,
RATE_LIMIT_TOKEN_EXCEEDED,
RATE_LIMIT_BURST_EXCEEDED,
COMMON_INVALID_REQUEST,
COMMON_RESOURCE_NOT_FOUND,
COMMON_INTERNAL_ERROR,
COMMON_SERVICE_UNAVAILABLE,
}
for _, code := range codes {
if _, ok := ErrorDefinitions[code]; !ok {
t.Errorf("code %s not found in ErrorDefinitions", code)
}
}
}
func TestErrorDefinitions_Consistency(t *testing.T) {
for code, info := range ErrorDefinitions {
if info.Code != code {
t.Errorf("ErrorDefinitions[%s].Code = %s, expected %s", code, info.Code, code)
}
}
}
func TestGatewayError_ImplementsErrorInterface(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "test")
var e error = err
if e.Error() != "COMMON_001: test" {
t.Error("GatewayError should implement error interface")
}
}
func TestGatewayError_ErrorWithWrappedError(t *testing.T) {
wrapped := errors.New("wrapped error")
err := NewGatewayError(COMMON_INTERNAL_ERROR, "outer error").WithInternal(wrapped)
// Error()应该包含wrapped error的信息
expected := "COMMON_003: outer error (caused by: wrapped error)"
if err.Error() != expected {
t.Errorf("expected %s, got %s", expected, err.Error())
}
}
func TestNewGatewayError_EmptyMessage(t *testing.T) {
err := NewGatewayError(COMMON_INVALID_REQUEST, "")
if err.Message != "" {
t.Errorf("expected empty message, got %s", err.Message)
}
}
func TestGetErrorInfo_ErrorDefinitions(t *testing.T) {
info := ErrorDefinitions[AUTH_INVALID_TOKEN]
if info.Code != AUTH_INVALID_TOKEN {
t.Errorf("expected AUTH_INVALID_TOKEN, got %s", info.Code)
}
if info.Message != "Invalid or expired token" {
t.Errorf("unexpected message: %s", info.Message)
}
if info.HTTPStatus != 401 {
t.Errorf("expected 401, got %d", info.HTTPStatus)
}
if info.Retryable != false {
t.Error("expected non-retryable")
}
}
func TestErrorCode_Type(t *testing.T) {
var code ErrorCode = "TEST_001"
if string(code) != "TEST_001" {
t.Errorf("expected TEST_001, got %s", code)
}
}