package response import ( "encoding/json" "io" "net/http/httptest" "testing" "time" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/gofiber/fiber/v2" ) // TestSuccess 测试成功响应(T034) func TestSuccess(t *testing.T) { tests := []struct { name string data any }{ { name: "success with string data", data: "test data", }, { name: "success with map data", data: map[string]any{ "id": 123, "name": "test", }, }, { name: "success with slice data", data: []string{"item1", "item2", "item3"}, }, { name: "success with struct data", data: struct { ID int `json:"id"` Name string `json:"name"` }{ ID: 456, Name: "test struct", }, }, { name: "success with nil data", data: nil, }, { name: "success with empty map", data: map[string]any{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return Success(c, tt.data) }) req := httptest.NewRequest("GET", "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("Failed to execute request: %v", err) } defer func() { _ = resp.Body.Close() }() // 验证 HTTP 状态码 if resp.StatusCode != 200 { t.Errorf("Expected status code 200, got %d", resp.StatusCode) } // 验证响应头 if resp.Header.Get("Content-Type") != "application/json" { t.Errorf("Expected Content-Type application/json, got %s", resp.Header.Get("Content-Type")) } // 解析响应体 body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Failed to read response body: %v", err) } var response Response if err := json.Unmarshal(body, &response); err != nil { t.Fatalf("Failed to unmarshal response: %v", err) } // 验证响应结构 if response.Code != errors.CodeSuccess { t.Errorf("Expected code %d, got %d", errors.CodeSuccess, response.Code) } if response.Message != "success" { t.Errorf("Expected message 'success', got '%s'", response.Message) } // 验证时间戳格式 RFC3339 if _, err := time.Parse(time.RFC3339, response.Timestamp); err != nil { t.Errorf("Timestamp is not in RFC3339 format: %s", response.Timestamp) } // 验证数据字段(如果不是 nil) if tt.data != nil { if response.Data == nil { t.Error("Expected data field to be non-nil") } } }) } } // TestError 测试错误响应(T035) func TestError(t *testing.T) { tests := []struct { name string httpStatus int code int message string }{ { name: "internal server error", httpStatus: 500, code: errors.CodeInternalError, message: "Internal server error occurred", }, { name: "missing token error", httpStatus: 401, code: errors.CodeMissingToken, message: "Authentication token is missing", }, { name: "invalid token error", httpStatus: 401, code: errors.CodeInvalidToken, message: "Token is invalid or expired", }, { name: "rate limit error", httpStatus: 429, code: errors.CodeTooManyRequests, message: "Too many requests, please try again later", }, { name: "service unavailable error", httpStatus: 503, code: errors.CodeAuthServiceUnavailable, message: "Authentication service is currently unavailable", }, { name: "bad request error", httpStatus: 400, code: 2000, message: "Invalid request parameters", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return Error(c, tt.httpStatus, tt.code, tt.message) }) req := httptest.NewRequest("GET", "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("Failed to execute request: %v", err) } defer func() { _ = resp.Body.Close() }() // 验证 HTTP 状态码 if resp.StatusCode != tt.httpStatus { t.Errorf("Expected status code %d, got %d", tt.httpStatus, resp.StatusCode) } // 验证响应头 if resp.Header.Get("Content-Type") != "application/json" { t.Errorf("Expected Content-Type application/json, got %s", resp.Header.Get("Content-Type")) } // 解析响应体 body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Failed to read response body: %v", err) } var response Response if err := json.Unmarshal(body, &response); err != nil { t.Fatalf("Failed to unmarshal response: %v", err) } // 验证响应结构 if response.Code != tt.code { t.Errorf("Expected code %d, got %d", tt.code, response.Code) } if response.Message != tt.message { t.Errorf("Expected message '%s', got '%s'", tt.message, response.Message) } if response.Data != nil { t.Errorf("Expected data to be nil in error response, got %v", response.Data) } // 验证时间戳格式 RFC3339 if _, err := time.Parse(time.RFC3339, response.Timestamp); err != nil { t.Errorf("Timestamp is not in RFC3339 format: %s", response.Timestamp) } }) } } // TestSuccessWithMessage 测试带自定义消息的成功响应(T034) func TestSuccessWithMessage(t *testing.T) { tests := []struct { name string data any message string }{ { name: "custom success message", data: map[string]any{ "user_id": 123, }, message: "User created successfully", }, { name: "empty custom message", data: "test data", message: "", }, { name: "chinese message", data: map[string]string{ "status": "ok", }, message: "操作成功", }, { name: "long message", data: nil, message: "This is a very long success message that describes in detail what happened during the operation", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return SuccessWithMessage(c, tt.data, tt.message) }) req := httptest.NewRequest("GET", "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("Failed to execute request: %v", err) } defer func() { _ = resp.Body.Close() }() // 验证 HTTP 状态码(默认 200) if resp.StatusCode != 200 { t.Errorf("Expected status code 200, got %d", resp.StatusCode) } // 解析响应体 body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Failed to read response body: %v", err) } var response Response if err := json.Unmarshal(body, &response); err != nil { t.Fatalf("Failed to unmarshal response: %v", err) } // 验证响应结构 if response.Code != errors.CodeSuccess { t.Errorf("Expected code %d, got %d", errors.CodeSuccess, response.Code) } if response.Message != tt.message { t.Errorf("Expected message '%s', got '%s'", tt.message, response.Message) } // 验证时间戳格式 RFC3339 if _, err := time.Parse(time.RFC3339, response.Timestamp); err != nil { t.Errorf("Timestamp is not in RFC3339 format: %s", response.Timestamp) } }) } } // TestResponseSerialization 测试响应序列化(T036) func TestResponseSerialization(t *testing.T) { tests := []struct { name string response Response }{ { name: "complete response", response: Response{ Code: 0, Data: map[string]any{"key": "value"}, Message: "success", Timestamp: time.Now().Format(time.RFC3339), }, }, { name: "response with nil data", response: Response{ Code: 1000, Data: nil, Message: "error", Timestamp: time.Now().Format(time.RFC3339), }, }, { name: "response with nested data", response: Response{ Code: 0, Data: map[string]any{ "user": map[string]any{ "id": 123, "name": "test", "tags": []string{"tag1", "tag2"}, }, }, Message: "success", Timestamp: time.Now().Format(time.RFC3339), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // 序列化 data, err := json.Marshal(tt.response) if err != nil { t.Fatalf("Failed to marshal response: %v", err) } // 反序列化 var deserialized Response if err := json.Unmarshal(data, &deserialized); err != nil { t.Fatalf("Failed to unmarshal response: %v", err) } // 验证字段 if deserialized.Code != tt.response.Code { t.Errorf("Code mismatch: expected %d, got %d", tt.response.Code, deserialized.Code) } if deserialized.Message != tt.response.Message { t.Errorf("Message mismatch: expected '%s', got '%s'", tt.response.Message, deserialized.Message) } if deserialized.Timestamp != tt.response.Timestamp { t.Errorf("Timestamp mismatch: expected '%s', got '%s'", tt.response.Timestamp, deserialized.Timestamp) } }) } } // TestResponseStructFields 测试响应结构字段(T036) func TestResponseStructFields(t *testing.T) { response := Response{ Code: 0, Data: "test", Message: "success", Timestamp: time.Now().Format(time.RFC3339), } data, err := json.Marshal(response) if err != nil { t.Fatalf("Failed to marshal response: %v", err) } // 解析为 map 以检查 JSON 键 var jsonMap map[string]any if err := json.Unmarshal(data, &jsonMap); err != nil { t.Fatalf("Failed to unmarshal to map: %v", err) } // 验证所有必需字段都存在 requiredFields := []string{"code", "data", "msg", "timestamp"} for _, field := range requiredFields { if _, exists := jsonMap[field]; !exists { t.Errorf("Required field '%s' is missing in JSON response", field) } } // 验证字段类型 if _, ok := jsonMap["code"].(float64); !ok { t.Error("Field 'code' should be a number") } if _, ok := jsonMap["msg"].(string); !ok { t.Error("Field 'msg' should be a string") } if _, ok := jsonMap["timestamp"].(string); !ok { t.Error("Field 'timestamp' should be a string") } } // TestMultipleResponses 测试多个连续响应(T036) func TestMultipleResponses(t *testing.T) { app := fiber.New() callCount := 0 app.Get("/test", func(c *fiber.Ctx) error { callCount++ if callCount%2 == 0 { return Success(c, map[string]int{"count": callCount}) } return Error(c, 500, errors.CodeInternalError, "error occurred") }) // 发送多个请求 for i := 1; i <= 5; i++ { req := httptest.NewRequest("GET", "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("Request %d failed: %v", i, err) } body, _ := io.ReadAll(resp.Body) resp.Body.Close() var response Response if err := json.Unmarshal(body, &response); err != nil { t.Fatalf("Request %d: failed to unmarshal response: %v", i, err) } // 验证每个响应都有时间戳 if response.Timestamp == "" { t.Errorf("Request %d: timestamp should not be empty", i) } } } // TestTimestampFormat 测试时间戳格式(T036) func TestTimestampFormat(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return Success(c, nil) }) req := httptest.NewRequest("GET", "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("Failed to execute request: %v", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) var response Response if err := json.Unmarshal(body, &response); err != nil { t.Fatalf("Failed to unmarshal response: %v", err) } // 验证是 RFC3339 格式 parsedTime, err := time.Parse(time.RFC3339, response.Timestamp) if err != nil { t.Fatalf("Timestamp is not in RFC3339 format: %s, error: %v", response.Timestamp, err) } // 验证时间戳是最近的(应该在最近 1 秒内) now := time.Now() diff := now.Sub(parsedTime) if diff < 0 || diff > time.Second { t.Errorf("Timestamp seems incorrect: %s (diff from now: %v)", response.Timestamp, diff) } }