package errors import ( "testing" "github.com/gofiber/fiber/v2" "github.com/valyala/fasthttp" ) // TestFromFiberContext 测试从 Fiber Context 提取错误上下文 func TestFromFiberContext(t *testing.T) { app := fiber.New() tests := []struct { name string setupRequest func(*fasthttp.RequestCtx) expectedMethod string expectedPath string hasRequestID bool }{ { name: "GET 请求", setupRequest: func(ctx *fasthttp.RequestCtx) { ctx.Request.Header.SetMethod("GET") ctx.Request.SetRequestURI("/api/v1/users") ctx.Request.Header.Set("X-Request-ID", "test-request-id-123") }, expectedMethod: "GET", expectedPath: "/api/v1/users", hasRequestID: true, }, { name: "POST 请求带查询参数", setupRequest: func(ctx *fasthttp.RequestCtx) { ctx.Request.Header.SetMethod("POST") ctx.Request.SetRequestURI("/api/v1/orders?status=pending") ctx.Request.Header.Set("X-Request-ID", "post-request-456") }, expectedMethod: "POST", expectedPath: "/api/v1/orders", hasRequestID: true, }, { name: "无 Request ID", setupRequest: func(ctx *fasthttp.RequestCtx) { ctx.Request.Header.SetMethod("DELETE") ctx.Request.SetRequestURI("/api/v1/tasks/123") }, expectedMethod: "DELETE", expectedPath: "/api/v1/tasks/123", hasRequestID: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 创建 fasthttp 请求上下文 fctx := &fasthttp.RequestCtx{} tt.setupRequest(fctx) // 创建 Fiber 上下文 c := app.AcquireCtx(fctx) defer app.ReleaseCtx(c) // 提取错误上下文 errCtx := FromFiberContext(c) // 验证方法 if errCtx.Method != tt.expectedMethod { t.Errorf("Method = %q, expected %q", errCtx.Method, tt.expectedMethod) } // 验证路径 if errCtx.Path != tt.expectedPath { t.Errorf("Path = %q, expected %q", errCtx.Path, tt.expectedPath) } // 验证 Request ID if tt.hasRequestID && errCtx.RequestID == "" { t.Error("Expected Request ID, but got empty string") } if !tt.hasRequestID && errCtx.RequestID != "" { t.Errorf("Expected no Request ID, but got %q", errCtx.RequestID) } // 验证 IP 地址不为空 if errCtx.IP == "" { t.Error("Expected IP address, but got empty string") } }) } } // TestErrorContextToLogFields 测试错误上下文转换为日志字段 func TestErrorContextToLogFields(t *testing.T) { tests := []struct { name string ctx *ErrorContext expectedFields int // 期望的字段数量 hasQuery bool hasUserAgent bool hasUserID bool }{ { name: "完整的错误上下文", ctx: &ErrorContext{ RequestID: "test-123", Method: "POST", Path: "/api/v1/users", IP: "192.168.1.100", Query: "status=active", UserAgent: "Mozilla/5.0", UserID: "user-456", }, expectedFields: 7, // request_id, method, path, ip, query, user_agent, user_id hasQuery: true, hasUserAgent: true, hasUserID: true, }, { name: "无查询参数", ctx: &ErrorContext{ RequestID: "test-456", Method: "GET", Path: "/api/v1/orders", IP: "10.0.0.1", Query: "", }, expectedFields: 4, // request_id, method, path, ip hasQuery: false, hasUserAgent: false, hasUserID: false, }, { name: "空 Request ID", ctx: &ErrorContext{ RequestID: "", Method: "DELETE", Path: "/api/v1/tasks/123", IP: "127.0.0.1", Query: "", }, expectedFields: 4, // request_id (空字符串), method, path, ip hasQuery: false, hasUserAgent: false, hasUserID: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fields := tt.ctx.ToLogFields() // 验证字段数量 if len(fields) != tt.expectedFields { t.Errorf("Field count = %d, expected %d", len(fields), tt.expectedFields) } // 验证必需字段存在 if len(fields) < 4 { t.Error("Expected at least 4 required fields (request_id, method, path, ip)") } }) } } // TestFromFiberContextWithUserAgent 测试带 User-Agent 的错误上下文提取 func TestFromFiberContextWithUserAgent(t *testing.T) { app := fiber.New() tests := []struct { name string method string path string userAgent string expectedUserAgent bool }{ { name: "有 User-Agent", method: "GET", path: "/api/v1/users", userAgent: "Mozilla/5.0", expectedUserAgent: true, }, { name: "无 User-Agent", method: "GET", path: "/api/v1/users/123", userAgent: "", expectedUserAgent: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 创建 fasthttp 请求上下文 fctx := &fasthttp.RequestCtx{} fctx.Request.Header.SetMethod(tt.method) fctx.Request.SetRequestURI(tt.path) if tt.userAgent != "" { fctx.Request.Header.Set("User-Agent", tt.userAgent) } // 创建 Fiber 上下文 c := app.AcquireCtx(fctx) defer app.ReleaseCtx(c) // 提取错误上下文 errCtx := FromFiberContext(c) // 验证 User-Agent if tt.expectedUserAgent && errCtx.UserAgent == "" { t.Error("Expected User-Agent, but got empty") } if !tt.expectedUserAgent && errCtx.UserAgent != "" { t.Errorf("Expected no User-Agent, but got %q", errCtx.UserAgent) } }) } } // BenchmarkFromFiberContext 基准测试错误上下文提取性能 func BenchmarkFromFiberContext(b *testing.B) { app := fiber.New() // 创建测试请求 fctx := &fasthttp.RequestCtx{} fctx.Request.Header.SetMethod("POST") fctx.Request.SetRequestURI("/api/v1/users?status=active&limit=10") fctx.Request.Header.Set("X-Request-ID", "benchmark-request-id") fctx.Request.SetBodyString(`{"username":"test","email":"test@example.com"}`) c := app.AcquireCtx(fctx) defer app.ReleaseCtx(c) b.ResetTimer() for i := 0; i < b.N; i++ { _ = FromFiberContext(c) } } // BenchmarkErrorContextToLogFields 基准测试日志字段转换性能 func BenchmarkErrorContextToLogFields(b *testing.B) { ctx := &ErrorContext{ RequestID: "benchmark-123", Method: "POST", Path: "/api/v1/users", IP: "192.168.1.100", Query: "status=active&limit=10", UserAgent: "Mozilla/5.0", UserID: "user-456", } b.ResetTimer() for i := 0; i < b.N; i++ { _ = ctx.ToLogFields() } }