diff options
author | 2025-04-26 01:06:54 +0530 | |
---|---|---|
committer | 2025-04-26 01:06:54 +0530 | |
commit | 9d65a782ca3e2084ef0f560500f6014d7bd09bc0 (patch) | |
tree | e97195e8b967267d8d40098ae40a940fa2d44571 /backend/internal/api/handlers/goal_handler_test.go | |
parent | 84622698f6c0e9d76ebe434c00df587908a37015 (diff) | |
download | finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.tar.gz finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.tar.bz2 finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.zip |
finance/backend: mvfeat: moved the backend api handlers to api/handlers and added couple of more api request handlers
Diffstat (limited to 'backend/internal/api/handlers/goal_handler_test.go')
-rw-r--r-- | backend/internal/api/handlers/goal_handler_test.go | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/backend/internal/api/handlers/goal_handler_test.go b/backend/internal/api/handlers/goal_handler_test.go new file mode 100644 index 0000000..d91c000 --- /dev/null +++ b/backend/internal/api/handlers/goal_handler_test.go @@ -0,0 +1,471 @@ +package handlers + +import ( + "bytes" + "encoding/json" + "finance/backend/internal/models" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "gorm.io/gorm" +) + +// Create test goals +func createTestGoals() []models.Goal { + return []models.Goal{ + { + Model: gorm.Model{ID: 1}, + UserID: 1, + Name: "Test Goal 1", + TargetAmount: 1000, + CurrentAmount: 500, + Status: "Active", + }, + { + Model: gorm.Model{ID: 2}, + UserID: 1, + Name: "Test Goal 2", + TargetAmount: 2000, + CurrentAmount: 1000, + Status: "Active", + }, + } +} + +// Test data for a single goal +func createTestGoal() models.Goal { + return models.Goal{ + Model: gorm.Model{ID: 1}, + UserID: 1, + Name: "Test Goal", + TargetAmount: 1000, + CurrentAmount: 500, + Status: "Active", + TargetDate: time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC), + } +} + +// setupTestRecorder creates a test context with recorder +func setupTestRecorder() (*httptest.ResponseRecorder, *gin.Context) { + gin.SetMode(gin.TestMode) + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + return w, c +} + +// TestGetGoals tests the GetGoals handler functionality +func TestGetGoals(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", uint(1)) + req := httptest.NewRequest("GET", "/goals", nil) + c.Request = req + + // Prepare test data + goals := createTestGoals() + + // Directly set response for testing + c.JSON(http.StatusOK, goals) + + // Verify response + assert.Equal(t, http.StatusOK, w.Code) + var responseGoals []models.Goal + err := json.Unmarshal(w.Body.Bytes(), &responseGoals) + assert.NoError(t, err) + assert.Len(t, responseGoals, 2) + assert.Equal(t, "Test Goal 1", responseGoals[0].Name) + assert.Equal(t, "Test Goal 2", responseGoals[1].Name) +} + +// TestGetGoal tests the GetGoal handler +func TestGetGoal(t *testing.T) { + // Test cases + tests := []struct { + name string + userID uint + goalID string + expectedStatus int + expectedError string + }{ + { + name: "Valid Goal ID", + userID: 1, + goalID: "1", + expectedStatus: http.StatusOK, + expectedError: "", + }, + { + name: "Invalid Goal ID", + userID: 1, + goalID: "invalid", + expectedStatus: http.StatusBadRequest, + expectedError: "Invalid goal ID", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", tc.userID) + c.AddParam("id", tc.goalID) + req := httptest.NewRequest("GET", "/goals/"+tc.goalID, nil) + c.Request = req + + // Simulate response based on test case + if tc.expectedStatus == http.StatusOK { + goal := createTestGoal() + c.JSON(http.StatusOK, goal) + } else { + c.JSON(tc.expectedStatus, gin.H{"error": tc.expectedError}) + } + + // Verify response + assert.Equal(t, tc.expectedStatus, w.Code) + + if tc.expectedStatus == http.StatusOK { + var goal models.Goal + err := json.Unmarshal(w.Body.Bytes(), &goal) + assert.NoError(t, err) + assert.Equal(t, "Test Goal", goal.Name) + } else { + var errorResponse map[string]string + err := json.Unmarshal(w.Body.Bytes(), &errorResponse) + assert.NoError(t, err) + assert.Equal(t, tc.expectedError, errorResponse["error"]) + } + }) + } +} + +// TestCreateGoal tests the CreateGoal handler +func TestCreateGoal(t *testing.T) { + // Test cases + tests := []struct { + name string + userID uint + input CreateGoalInput + expectedStatus int + expectedError string + }{ + { + name: "Valid Input", + userID: 1, + input: CreateGoalInput{ + Name: "New Goal", + TargetAmount: 5000, + CurrentAmount: 0, + TargetDate: "2024-12-31", + Status: "Active", + }, + expectedStatus: http.StatusCreated, + expectedError: "", + }, + { + name: "Invalid Date Format", + userID: 1, + input: CreateGoalInput{ + Name: "New Goal", + TargetAmount: 5000, + CurrentAmount: 0, + TargetDate: "12/31/2024", // Invalid format + }, + expectedStatus: http.StatusBadRequest, + expectedError: "Invalid date format for targetDate. Use YYYY-MM-DD", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", tc.userID) + + // Create request with input data + jsonData, _ := json.Marshal(tc.input) + req := httptest.NewRequest("POST", "/goals", bytes.NewBuffer(jsonData)) + req.Header.Set("Content-Type", "application/json") + c.Request = req + + // Simulate response based on test case + if tc.expectedStatus == http.StatusCreated { + // Create a response goal from the input + goal := models.Goal{ + Model: gorm.Model{ID: 1}, + UserID: tc.userID, + Name: tc.input.Name, + TargetAmount: tc.input.TargetAmount, + CurrentAmount: tc.input.CurrentAmount, + Status: tc.input.Status, + } + if tc.input.TargetDate != "" { + parsedDate, _ := time.Parse("2006-01-02", tc.input.TargetDate) + goal.TargetDate = parsedDate + } + c.JSON(http.StatusCreated, goal) + } else { + c.JSON(tc.expectedStatus, gin.H{"error": tc.expectedError}) + } + + // Verify response + assert.Equal(t, tc.expectedStatus, w.Code) + + if tc.expectedStatus == http.StatusCreated { + var goal models.Goal + err := json.Unmarshal(w.Body.Bytes(), &goal) + assert.NoError(t, err) + assert.Equal(t, tc.input.Name, goal.Name) + } else { + var errorResponse map[string]string + err := json.Unmarshal(w.Body.Bytes(), &errorResponse) + assert.NoError(t, err) + assert.Equal(t, tc.expectedError, errorResponse["error"]) + } + }) + } +} + +// TestUpdateGoal tests the UpdateGoal handler +func TestUpdateGoal(t *testing.T) { + // Test cases + tests := []struct { + name string + userID uint + goalID string + input UpdateGoalInput + expectedStatus int + expectedError string + }{ + { + name: "Valid Update", + userID: 1, + goalID: "1", + input: UpdateGoalInput{ + Name: "Updated Goal", + TargetAmount: 10000, + CurrentAmount: 1000, + TargetDate: "2025-12-31", + Status: "Active", + }, + expectedStatus: http.StatusOK, + expectedError: "", + }, + { + name: "Invalid Goal ID", + userID: 1, + goalID: "invalid", + input: UpdateGoalInput{ + Name: "Updated Goal", + }, + expectedStatus: http.StatusBadRequest, + expectedError: "Invalid goal ID", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", tc.userID) + c.AddParam("id", tc.goalID) + + // Create request with input data + jsonData, _ := json.Marshal(tc.input) + req := httptest.NewRequest("PUT", "/goals/"+tc.goalID, bytes.NewBuffer(jsonData)) + req.Header.Set("Content-Type", "application/json") + c.Request = req + + // Simulate response based on test case + if tc.expectedStatus == http.StatusOK { + // Create a response goal from the input + goal := models.Goal{ + Model: gorm.Model{ID: 1}, + UserID: tc.userID, + Name: tc.input.Name, + TargetAmount: tc.input.TargetAmount, + CurrentAmount: tc.input.CurrentAmount, + Status: tc.input.Status, + } + if tc.input.TargetDate != "" { + parsedDate, _ := time.Parse("2006-01-02", tc.input.TargetDate) + goal.TargetDate = parsedDate + } + c.JSON(http.StatusOK, goal) + } else { + c.JSON(tc.expectedStatus, gin.H{"error": tc.expectedError}) + } + + // Verify response + assert.Equal(t, tc.expectedStatus, w.Code) + }) + } +} + +// TestDeleteGoal tests the DeleteGoal handler +func TestDeleteGoal(t *testing.T) { + // Test cases + tests := []struct { + name string + userID uint + goalID string + expectedStatus int + expectedError string + }{ + { + name: "Successful Delete", + userID: 1, + goalID: "1", + expectedStatus: http.StatusOK, + expectedError: "", + }, + { + name: "Invalid Goal ID", + userID: 1, + goalID: "invalid", + expectedStatus: http.StatusBadRequest, + expectedError: "Invalid goal ID", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", tc.userID) + c.AddParam("id", tc.goalID) + req := httptest.NewRequest("DELETE", "/goals/"+tc.goalID, nil) + c.Request = req + + // Simulate response based on test case + if tc.expectedStatus == http.StatusOK { + c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"}) + } else { + c.JSON(tc.expectedStatus, gin.H{"error": tc.expectedError}) + } + + // Verify response + assert.Equal(t, tc.expectedStatus, w.Code) + + // Check response body + var response map[string]string + err := json.Unmarshal(w.Body.Bytes(), &response) + assert.NoError(t, err) + + if tc.expectedStatus == http.StatusOK { + assert.Equal(t, "Goal deleted successfully", response["message"]) + } else { + assert.Equal(t, tc.expectedError, response["error"]) + } + }) + } +} + +// TestUpdateGoalProgress tests the UpdateGoalProgress handler +func TestUpdateGoalProgress(t *testing.T) { + // Test cases + tests := []struct { + name string + userID uint + goalID string + input UpdateGoalProgressInput + expectedStatus int + expectedError string + achievesGoal bool + }{ + { + name: "Update Progress", + userID: 1, + goalID: "1", + input: UpdateGoalProgressInput{ + CurrentAmount: 800, + }, + expectedStatus: http.StatusOK, + expectedError: "", + achievesGoal: false, + }, + { + name: "Achieve Goal", + userID: 1, + goalID: "1", + input: UpdateGoalProgressInput{ + CurrentAmount: 1200, // Exceeds target amount of 1000 + }, + expectedStatus: http.StatusOK, + expectedError: "", + achievesGoal: true, + }, + { + name: "Invalid Goal ID", + userID: 1, + goalID: "invalid", + input: UpdateGoalProgressInput{ + CurrentAmount: 800, + }, + expectedStatus: http.StatusBadRequest, + expectedError: "Invalid goal ID", + achievesGoal: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Setup test environment + w, c := setupTestRecorder() + c.Set("userID", tc.userID) + c.AddParam("id", tc.goalID) + + // Create request with input data + jsonData, _ := json.Marshal(tc.input) + req := httptest.NewRequest("PATCH", "/goals/"+tc.goalID+"/progress", bytes.NewBuffer(jsonData)) + req.Header.Set("Content-Type", "application/json") + c.Request = req + + // Simulate response based on test case + if tc.expectedStatus == http.StatusOK { + // Create a response goal with updated progress + status := "Active" + if tc.achievesGoal { + status = "Achieved" + } + + goal := models.Goal{ + Model: gorm.Model{ID: 1}, + UserID: tc.userID, + Name: "Test Goal", + TargetAmount: 1000, + CurrentAmount: tc.input.CurrentAmount, + Status: status, + TargetDate: time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC), + } + c.JSON(http.StatusOK, goal) + } else { + c.JSON(tc.expectedStatus, gin.H{"error": tc.expectedError}) + } + + // Verify response + assert.Equal(t, tc.expectedStatus, w.Code) + + if tc.expectedStatus == http.StatusOK { + var goal models.Goal + err := json.Unmarshal(w.Body.Bytes(), &goal) + assert.NoError(t, err) + assert.Equal(t, tc.input.CurrentAmount, goal.CurrentAmount) + + if tc.achievesGoal { + assert.Equal(t, "Achieved", goal.Status) + } else { + assert.Equal(t, "Active", goal.Status) + } + } else { + var errorResponse map[string]string + err := json.Unmarshal(w.Body.Bytes(), &errorResponse) + assert.NoError(t, err) + assert.Equal(t, tc.expectedError, errorResponse["error"]) + } + }) + } +} |