Files
junhong_cmp_fiber/pkg/errors/codes_test.go
huang 6821e5abcf
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m36s
refactor: 统一错误消息数据源,优化错误码与映射表管理
主要改动:
- 改造 errors.New() 和 Wrap() 函数签名为可变参数,优先使用 errorMessages 映射表
- 添加 allErrorCodes 注册表和 init() 启动时校验,确保错误码与映射表一致
- 添加 TestAllCodesHaveMessages 和 TestNoOrphanMessages 测试防止映射表腐化
- 清理 109 处与映射表一致的冗余硬编码(service 层)
- 保留业务特定消息覆盖能力

新增 API 用法:
- errors.New(errors.CodeUnauthorized) // 使用映射表默认消息
- errors.New(errors.CodeNotFound, "提现申请不存在") // 覆盖为自定义消息
2026-01-22 18:27:42 +08:00

220 lines
6.1 KiB
Go

package errors
import (
"testing"
"github.com/gofiber/fiber/v2"
)
// TestGetHTTPStatus 测试错误码到 HTTP 状态码的映射
func TestGetHTTPStatus(t *testing.T) {
tests := []struct {
name string
code int
expected int
}{
// 成功
{"成功", CodeSuccess, fiber.StatusOK},
// 客户端错误 (1xxx -> 4xx)
{"参数验证失败", CodeInvalidParam, fiber.StatusBadRequest},
{"缺失认证令牌", CodeMissingToken, fiber.StatusUnauthorized},
{"无效令牌", CodeInvalidToken, fiber.StatusUnauthorized},
{"未授权访问", CodeUnauthorized, fiber.StatusUnauthorized},
{"禁止访问", CodeForbidden, fiber.StatusForbidden},
{"资源未找到", CodeNotFound, fiber.StatusNotFound},
{"资源冲突", CodeConflict, fiber.StatusConflict},
{"请求过多", CodeTooManyRequests, fiber.StatusTooManyRequests},
{"请求体过大", CodeRequestTooLarge, fiber.StatusBadRequest},
// 服务端错误 (2xxx -> 5xx)
{"内部服务器错误", CodeInternalError, fiber.StatusInternalServerError},
{"数据库错误", CodeDatabaseError, fiber.StatusInternalServerError},
{"缓存服务错误", CodeRedisError, fiber.StatusInternalServerError},
{"服务不可用", CodeServiceUnavailable, fiber.StatusServiceUnavailable},
{"请求超时", CodeTimeout, fiber.StatusGatewayTimeout},
{"任务队列错误", CodeTaskQueueError, fiber.StatusInternalServerError},
// 未知错误码
{"未知错误码", 9999, fiber.StatusInternalServerError},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := GetHTTPStatus(tt.code)
if result != tt.expected {
t.Errorf("GetHTTPStatus(%d) = %d, expected %d", tt.code, result, tt.expected)
}
})
}
}
// TestGetMessage 测试错误码到错误消息的映射
func TestGetMessage(t *testing.T) {
tests := []struct {
name string
code int
expected string
}{
// 成功
{"成功", CodeSuccess, "成功"},
// 客户端错误
{"参数验证失败", CodeInvalidParam, "参数验证失败"},
{"缺失认证令牌", CodeMissingToken, "缺失认证令牌"},
{"无效令牌", CodeInvalidToken, "无效或过期的令牌"},
{"未授权访问", CodeUnauthorized, "未授权访问"},
{"禁止访问", CodeForbidden, "禁止访问"},
{"资源未找到", CodeNotFound, "资源未找到"},
{"资源冲突", CodeConflict, "资源冲突"},
{"请求过多", CodeTooManyRequests, "请求过多,请稍后重试"},
{"请求体过大", CodeRequestTooLarge, "请求体过大"},
// 服务端错误
{"内部服务器错误", CodeInternalError, "内部服务器错误"},
{"数据库错误", CodeDatabaseError, "数据库错误"},
{"缓存服务错误", CodeRedisError, "缓存服务错误"},
{"服务不可用", CodeServiceUnavailable, "服务暂时不可用"},
{"请求超时", CodeTimeout, "请求超时"},
{"任务队列错误", CodeTaskQueueError, "任务队列错误"},
// 未知错误码
{"未知错误码", 9999, "请求处理失败"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := GetMessage(tt.code, "zh-CN")
if result != tt.expected {
t.Errorf("GetMessage(%d, \"zh-CN\") = %q, expected %q", tt.code, result, tt.expected)
}
})
}
}
// TestGetLogLevel 测试错误码到日志级别的映射
func TestGetLogLevel(t *testing.T) {
tests := []struct {
name string
code int
expected string
}{
// 成功 (不记录日志)
{"成功", CodeSuccess, "info"},
// 客户端错误 (Warn 级别)
{"参数验证失败", CodeInvalidParam, "warn"},
{"缺失认证令牌", CodeMissingToken, "warn"},
{"无效令牌", CodeInvalidToken, "warn"},
{"未授权访问", CodeUnauthorized, "warn"},
{"禁止访问", CodeForbidden, "warn"},
{"资源未找到", CodeNotFound, "warn"},
{"资源冲突", CodeConflict, "warn"},
{"请求过多", CodeTooManyRequests, "warn"},
{"请求体过大", CodeRequestTooLarge, "warn"},
// 服务端错误 (Error 级别)
{"内部服务器错误", CodeInternalError, "error"},
{"数据库错误", CodeDatabaseError, "error"},
{"缓存服务错误", CodeRedisError, "error"},
{"服务不可用", CodeServiceUnavailable, "error"},
{"请求超时", CodeTimeout, "error"},
{"任务队列错误", CodeTaskQueueError, "error"},
// 未知错误码 (Error 级别)
{"未知错误码", 9999, "error"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := GetLogLevel(tt.code)
if result != tt.expected {
t.Errorf("GetLogLevel(%d) = %q, expected %q", tt.code, result, tt.expected)
}
})
}
}
func TestAllCodesHaveMessages(t *testing.T) {
var missing []int
for _, code := range allErrorCodes {
if _, ok := errorMessages[code]; !ok {
missing = append(missing, code)
}
}
if len(missing) > 0 {
t.Errorf("以下错误码缺少映射消息: %v", missing)
}
}
func TestNoOrphanMessages(t *testing.T) {
codeSet := make(map[int]bool)
for _, code := range allErrorCodes {
codeSet[code] = true
}
var orphan []int
for code := range errorMessages {
if !codeSet[code] {
orphan = append(orphan, code)
}
}
if len(orphan) > 0 {
t.Errorf("以下错误码在 errorMessages 中存在但未在 allErrorCodes 中注册: %v", orphan)
}
}
// BenchmarkGetHTTPStatus 基准测试 HTTP 状态码映射性能
func BenchmarkGetHTTPStatus(b *testing.B) {
codes := []int{
CodeSuccess,
CodeInvalidParam,
CodeMissingToken,
CodeInternalError,
CodeDatabaseError,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, code := range codes {
GetHTTPStatus(code)
}
}
}
// BenchmarkGetMessage 基准测试错误消息获取性能
func BenchmarkGetMessage(b *testing.B) {
codes := []int{
CodeSuccess,
CodeInvalidParam,
CodeMissingToken,
CodeInternalError,
CodeDatabaseError,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, code := range codes {
GetMessage(code, "zh-CN")
}
}
}
// BenchmarkGetLogLevel 基准测试日志级别映射性能
func BenchmarkGetLogLevel(b *testing.B) {
codes := []int{
CodeSuccess,
CodeInvalidParam,
CodeMissingToken,
CodeInternalError,
CodeDatabaseError,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, code := range codes {
GetLogLevel(code)
}
}
}