package handlers import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "go.uber.org/zap" "veza-backend-api/internal/dto" ) func TestBindAndValidateJSON_Validation(t *testing.T) { gin.SetMode(gin.TestMode) logger := zap.NewNop() commonHandler := NewCommonHandler(logger) // Test case struct type TestRequest struct { Name string `json:"name" validate:"required,min=3"` Email string `json:"email" validate:"required,email"` Age int `json:"age" validate:"gte=18"` } tests := []struct { name string payload interface{} expectedStatus int expectedError string checkDetails bool }{ { name: "Valid Request", payload: TestRequest{ Name: "John Doe", Email: "john@example.com", Age: 25, }, expectedStatus: http.StatusOK, }, { name: "Missing Required Fields", payload: map[string]interface{}{ "age": 25, }, expectedStatus: http.StatusBadRequest, // or 422 depending on implementation, BindAndValidateJSON returns 400 for validation error wrapper expectedError: "Validation failed", checkDetails: true, }, { name: "Invalid Email", payload: TestRequest{ Name: "John", Email: "not-an-email", Age: 25, }, expectedStatus: http.StatusBadRequest, expectedError: "Validation failed", checkDetails: true, }, { name: "Min Length Violation", payload: TestRequest{ Name: "Jo", Email: "john@example.com", Age: 25, }, expectedStatus: http.StatusBadRequest, expectedError: "Validation failed", checkDetails: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) jsonBytes, _ := json.Marshal(tt.payload) c.Request = httptest.NewRequest("POST", "/test", bytes.NewBuffer(jsonBytes)) c.Request.Header.Set("Content-Type", "application/json") var req TestRequest appErr := commonHandler.BindAndValidateJSON(c, &req) if tt.expectedStatus == http.StatusOK { assert.Nil(t, appErr) } else { assert.NotNil(t, appErr) // Verify error response format by actually calling RespondWithAppError logic simulation // Or check AppError properties assert.Contains(t, appErr.Message, tt.expectedError) if tt.checkDetails { // check logic in commonHandler.BindAndValidateJSON // It returns apperrors.NewValidationError which puts details in Details field assert.NotEmpty(t, appErr.Details, "Should have validation details") } } }) } } func TestBindAndValidateJSON_DTOs(t *testing.T) { // Verify actual DTOs used in handlers gin.SetMode(gin.TestMode) logger := zap.NewNop() commonHandler := NewCommonHandler(logger) t.Run("RegisterRequest_Invalid", func(t *testing.T) { payload := dto.RegisterRequest{ Username: "ab", // Too short Email: "bad-email", Password: "short", } w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) jsonBytes, _ := json.Marshal(commonHandler) // Mistake in previous line logic, fix payload jsonBytes, _ = json.Marshal(payload) c.Request = httptest.NewRequest("POST", "/register", bytes.NewBuffer(jsonBytes)) var req dto.RegisterRequest appErr := commonHandler.BindAndValidateJSON(c, &req) assert.NotNil(t, appErr) // Should catch Username min, Email format, Password min assert.NotEmpty(t, appErr.Details) }) t.Run("LoginRequest_Invalid", func(t *testing.T) { payload := dto.LoginRequest{ Email: "bad-email", } w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) jsonBytes, _ := json.Marshal(payload) c.Request = httptest.NewRequest("POST", "/login", bytes.NewBuffer(jsonBytes)) var req dto.LoginRequest appErr := commonHandler.BindAndValidateJSON(c, &req) assert.NotNil(t, appErr) assert.NotEmpty(t, appErr.Details) }) }