package response import ( "encoding/json" "net/http" "net/http/httptest" "testing" apperrors "veza-backend-api/internal/errors" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestResponseFormat_Success verifies that Success() returns wrapped format // Action 1.3.2.5: Test wrapped format for success responses func TestResponseFormat_Success(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.GET("/test", func(c *gin.Context) { Success(c, gin.H{"id": "123", "name": "Test"}) }) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.True(t, response["success"].(bool), "Response should have success: true") assert.Contains(t, response, "data", "Response should have data field") data, ok := response["data"].(map[string]interface{}) require.True(t, ok, "Data should be an object") assert.Equal(t, "123", data["id"]) assert.Equal(t, "Test", data["name"]) } // TestResponseFormat_SuccessWithMessage verifies Success() with message // Action 1.3.2.5: Test wrapped format with message field func TestResponseFormat_SuccessWithMessage(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.GET("/test", func(c *gin.Context) { Success(c, gin.H{"id": "123"}, "Operation successful") }) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.True(t, response["success"].(bool)) assert.Contains(t, response, "data") assert.Equal(t, "Operation successful", response["message"]) } // TestResponseFormat_Created verifies that Created() returns wrapped format // Action 1.3.2.5: Test wrapped format for created responses func TestResponseFormat_Created(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.POST("/test", func(c *gin.Context) { Created(c, gin.H{"id": "456", "name": "New Resource"}) }) req := httptest.NewRequest("POST", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusCreated, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.True(t, response["success"].(bool), "Response should have success: true") assert.Contains(t, response, "data", "Response should have data field") data, ok := response["data"].(map[string]interface{}) require.True(t, ok, "Data should be an object") assert.Equal(t, "456", data["id"]) assert.Equal(t, "New Resource", data["name"]) } // TestResponseFormat_Error verifies that Error() returns wrapped format // Action 1.3.2.5: Test wrapped format for error responses func TestResponseFormat_Error(t *testing.T) { gin.SetMode(gin.TestMode) tests := []struct { name string statusCode int message string expectedStatus int }{ { name: "BadRequest", statusCode: http.StatusBadRequest, message: "Validation failed", expectedStatus: http.StatusBadRequest, }, { name: "Unauthorized", statusCode: http.StatusUnauthorized, message: "Unauthorized", expectedStatus: http.StatusUnauthorized, }, { name: "Forbidden", statusCode: http.StatusForbidden, message: "Forbidden", expectedStatus: http.StatusForbidden, }, { name: "NotFound", statusCode: http.StatusNotFound, message: "Not found", expectedStatus: http.StatusNotFound, }, { name: "InternalServerError", statusCode: http.StatusInternalServerError, message: "Internal server error", expectedStatus: http.StatusInternalServerError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { router := gin.New() router.GET("/test", func(c *gin.Context) { Error(c, tt.statusCode, tt.message) }) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, tt.expectedStatus, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.False(t, response["success"].(bool), "Error response should have success: false") assert.Nil(t, response["data"], "Error response should have null data") assert.Contains(t, response, "error", "Error response should have error field") // Verify error structure errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be an object") assert.Contains(t, errorObj, "code", "Error should have code field") assert.Contains(t, errorObj, "message", "Error should have message field") assert.Contains(t, errorObj, "timestamp", "Error should have timestamp field") assert.Equal(t, tt.message, errorObj["message"]) }) } } // TestResponseFormat_RespondWithAppError verifies RespondWithAppError() returns wrapped format // Action 1.3.2.5: Test wrapped format for AppError responses func TestResponseFormat_RespondWithAppError(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.GET("/test", func(c *gin.Context) { appErr := apperrors.New(apperrors.ErrCodeValidation, "Validation failed") RespondWithAppError(c, appErr) }) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.False(t, response["success"].(bool), "Error response should have success: false") assert.Nil(t, response["data"], "Error response should have null data") assert.Contains(t, response, "error", "Error response should have error field") // Verify error structure errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be an object") assert.Equal(t, float64(apperrors.ErrCodeValidation), errorObj["code"]) assert.Equal(t, "Validation failed", errorObj["message"]) assert.Contains(t, errorObj, "timestamp") } // TestResponseFormat_ValidationError verifies ValidationError() returns wrapped format // Action 1.3.2.5: Test wrapped format for validation error responses func TestResponseFormat_ValidationError(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() router.POST("/test", func(c *gin.Context) { details := map[string]string{ "email": "invalid email format", "password": "password too short", } ValidationError(c, "Validation failed", details) }) req := httptest.NewRequest("POST", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err) // Verify wrapped format assert.False(t, response["success"].(bool), "Error response should have success: false") assert.Nil(t, response["data"], "Error response should have null data") assert.Contains(t, response, "error", "Error response should have error field") // Verify error structure with details errorObj, ok := response["error"].(map[string]interface{}) require.True(t, ok, "Error should be an object") assert.Contains(t, errorObj, "details", "Error should have details field") details, ok := errorObj["details"].([]interface{}) require.True(t, ok, "Details should be an array") assert.Len(t, details, 2, "Should have 2 validation errors") } // TestResponseFormat_AllHelpersUseWrappedFormat verifies all helper functions use wrapped format // Action 1.3.2.5: Comprehensive test to ensure all response helpers return wrapped format func TestResponseFormat_AllHelpersUseWrappedFormat(t *testing.T) { gin.SetMode(gin.TestMode) helpers := []struct { name string handler gin.HandlerFunc }{ { name: "Success", handler: func(c *gin.Context) { Success(c, gin.H{"test": "data"}) }, }, { name: "Created", handler: func(c *gin.Context) { Created(c, gin.H{"test": "data"}) }, }, { name: "BadRequest", handler: func(c *gin.Context) { BadRequest(c, "Bad request") }, }, { name: "Unauthorized", handler: func(c *gin.Context) { Unauthorized(c, "Unauthorized") }, }, { name: "Forbidden", handler: func(c *gin.Context) { Forbidden(c, "Forbidden") }, }, { name: "NotFound", handler: func(c *gin.Context) { NotFound(c, "Not found") }, }, { name: "InternalServerError", handler: func(c *gin.Context) { InternalServerError(c, "Internal server error") }, }, } for _, helper := range helpers { t.Run(helper.name, func(t *testing.T) { router := gin.New() router.GET("/test", helper.handler) req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) var response map[string]interface{} err := json.Unmarshal(w.Body.Bytes(), &response) require.NoError(t, err, "Response should be valid JSON") // All responses must have 'success' field (wrapped format) assert.Contains(t, response, "success", "Response must have 'success' field (wrapped format)") // Success responses should have success: true and data field if helper.name == "Success" || helper.name == "Created" { assert.True(t, response["success"].(bool), "Success response should have success: true") assert.Contains(t, response, "data", "Success response should have data field") } else { // Error responses should have success: false and error field assert.False(t, response["success"].(bool), "Error response should have success: false") assert.Contains(t, response, "error", "Error response should have error field") } }) } }