Migrate from Gin to Echo framework and add comprehensive integration tests

Major changes:
- Migrate all handlers from Gin to Echo framework
- Add new apperrors, echohelpers, and validator packages
- Update middleware for Echo compatibility
- Add ArchivedHandler to task categorization chain (archived tasks go to cancelled_tasks column)
- Add 6 new integration tests:
  - RecurringTaskLifecycle: NextDueDate advancement for weekly/monthly tasks
  - MultiUserSharing: Complex sharing with user removal
  - TaskStateTransitions: All state transitions and kanban column changes
  - DateBoundaryEdgeCases: Threshold boundary testing
  - CascadeOperations: Residence deletion cascade effects
  - MultiUserOperations: Shared residence collaboration
- Add single-purpose repository functions for kanban columns (GetOverdueTasks, GetDueSoonTasks, etc.)
- Fix RemoveUser route param mismatch (userId -> user_id)
- Fix determineExpectedColumn helper to correctly prioritize in_progress over overdue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-16 13:52:08 -06:00
parent c51f1ce34a
commit 6dac34e373
98 changed files with 8209 additions and 4425 deletions

View File

@@ -8,14 +8,16 @@ import (
"sync"
"testing"
"github.com/gin-gonic/gin"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/treytartt/casera-api/internal/apperrors"
"github.com/treytartt/casera-api/internal/i18n"
"github.com/treytartt/casera-api/internal/models"
"github.com/treytartt/casera-api/internal/validator"
)
var i18nOnce sync.Once
@@ -68,14 +70,16 @@ func SetupTestDB(t *testing.T) *gorm.DB {
return db
}
// SetupTestRouter creates a test Gin router
func SetupTestRouter() *gin.Engine {
gin.SetMode(gin.TestMode)
return gin.New()
// SetupTestRouter creates a test Echo router with the custom error handler
func SetupTestRouter() *echo.Echo {
e := echo.New()
e.Validator = validator.NewCustomValidator()
e.HTTPErrorHandler = apperrors.HTTPErrorHandler
return e
}
// MakeRequest makes a test HTTP request and returns the response
func MakeRequest(router *gin.Engine, method, path string, body interface{}, token string) *httptest.ResponseRecorder {
func MakeRequest(router *echo.Echo, method, path string, body interface{}, token string) *httptest.ResponseRecorder {
var reqBody *bytes.Buffer
if body != nil {
jsonBody, _ := json.Marshal(body)
@@ -90,9 +94,9 @@ func MakeRequest(router *gin.Engine, method, path string, body interface{}, toke
req.Header.Set("Authorization", "Token "+token)
}
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
return w
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
return rec
}
// ParseJSON parses JSON response body into a map
@@ -287,11 +291,13 @@ func AssertStatusCode(t *testing.T, w *httptest.ResponseRecorder, expected int)
}
// MockAuthMiddleware creates middleware that sets a test user in context
func MockAuthMiddleware(user *models.User) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("auth_user", user)
c.Set("auth_token", "test-token")
c.Next()
func MockAuthMiddleware(user *models.User) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Set("auth_user", user)
c.Set("auth_token", "test-token")
return next(c)
}
}
}
@@ -329,3 +335,22 @@ func CreateTestDocument(t *testing.T, db *gorm.DB, residenceID, createdByID uint
require.NoError(t, err)
return doc
}
// AssertAppError asserts that an error is an AppError with a specific status code and message key
func AssertAppError(t *testing.T, err error, expectedCode int, expectedMessageKey string) {
require.Error(t, err, "expected an error")
var appErr *apperrors.AppError
require.ErrorAs(t, err, &appErr, "expected an AppError")
require.Equal(t, expectedCode, appErr.Code, "unexpected status code")
require.Equal(t, expectedMessageKey, appErr.MessageKey, "unexpected message key")
}
// AssertAppErrorCode asserts that an error is an AppError with a specific status code
func AssertAppErrorCode(t *testing.T, err error, expectedCode int) {
require.Error(t, err, "expected an error")
var appErr *apperrors.AppError
require.ErrorAs(t, err, &appErr, "expected an AppError")
require.Equal(t, expectedCode, appErr.Code, "unexpected status code")
}