# Test Path Migration: Unversioned to Versioned API Paths **Status**: Complete **Created**: 2026-01-27 **Completed**: 2026-01-27 **Related**: ADR-008 (API Versioning Strategy) ## Summary All integration test files have been successfully migrated to use versioned API paths (`/api/v1/`). This resolves the redirect-related test failures introduced by ADR-008 Phase 1. ### Results | Metric | Value | | ------------------------- | ---------------------------------------- | | Test files updated | 23 | | Path occurrences changed | ~70 | | Tests before migration | 274/348 passing | | Tests after migration | 345/348 passing | | Test failures resolved | 71 | | Remaining todo/skipped | 3 (known issues, not versioning-related) | | Type check | Passing | | Versioning-specific tests | 82/82 passing | ### Key Outcomes - No `301 Moved Permanently` responses in test output - All redirect-related failures resolved - No regressions introduced - Unit tests unaffected (3,375/3,391 passing, pre-existing failures) --- ## Original Problem Statement Integration tests failed due to redirect middleware (ADR-008 Phase 1). Server returned `301 Moved Permanently` for unversioned paths (`/api/resource`) instead of expected `200 OK`. Redirect targets versioned paths (`/api/v1/resource`). **Root Cause**: Backwards-compatibility redirect in `server.ts`: ```typescript app.use('/api', (req, res, next) => { const versionPattern = /^\/v\d+/; if (!versionPattern.test(req.path)) { return res.redirect(301, `/api/v1${req.path}`); } next(); }); ``` **Impact**: ~70 test path occurrences across 23 files returning 301 instead of expected status codes. ## Solution Update all test API paths from `/api/{resource}` to `/api/v1/{resource}`. ## Files Requiring Updates ### Integration Tests (16 files) | File | Occurrences | Domains | | ------------------------------------------------------------ | ----------- | ---------------------- | | `src/tests/integration/inventory.integration.test.ts` | 14 | inventory | | `src/tests/integration/receipt.integration.test.ts` | 17 | receipts | | `src/tests/integration/recipe.integration.test.ts` | 17 | recipes, users/recipes | | `src/tests/integration/user.routes.integration.test.ts` | 10 | users/shopping-lists | | `src/tests/integration/admin.integration.test.ts` | 7 | admin | | `src/tests/integration/flyer-processing.integration.test.ts` | 6 | ai/jobs | | `src/tests/integration/budget.integration.test.ts` | 5 | budgets | | `src/tests/integration/notification.integration.test.ts` | 3 | users/notifications | | `src/tests/integration/data-integrity.integration.test.ts` | 3 | users, admin | | `src/tests/integration/upc.integration.test.ts` | 3 | upc | | `src/tests/integration/edge-cases.integration.test.ts` | 3 | users/shopping-lists | | `src/tests/integration/user.integration.test.ts` | 2 | users | | `src/tests/integration/public.routes.integration.test.ts` | 2 | flyers, recipes | | `src/tests/integration/flyer.integration.test.ts` | 1 | flyers | | `src/tests/integration/category.routes.test.ts` | 1 | categories | | `src/tests/integration/gamification.integration.test.ts` | 1 | ai/jobs | ### E2E Tests (7 files) | File | Occurrences | Domains | | --------------------------------------------- | ----------- | -------------------- | | `src/tests/e2e/inventory-journey.e2e.test.ts` | 9 | inventory | | `src/tests/e2e/receipt-journey.e2e.test.ts` | 9 | receipts | | `src/tests/e2e/budget-journey.e2e.test.ts` | 6 | budgets | | `src/tests/e2e/upc-journey.e2e.test.ts` | 3 | upc | | `src/tests/e2e/deals-journey.e2e.test.ts` | 2 | categories, users | | `src/tests/e2e/user-journey.e2e.test.ts` | 1 | users/shopping-lists | | `src/tests/e2e/flyer-upload.e2e.test.ts` | 1 | jobs | ## Update Pattern ### Find/Replace Rules **Template literals** (most common): ``` OLD: .get(`/api/resource/${id}`) NEW: .get(`/api/v1/resource/${id}`) ``` **String literals**: ``` OLD: .get('/api/resource') NEW: .get('/api/v1/resource') ``` ### Regex Pattern for Batch Updates ```regex Find: (\.(get|post|put|delete|patch)\([`'"])/api/([a-z]) Replace: $1/api/v1/$3 ``` **Explanation**: Captures HTTP method call, inserts `/v1/` after `/api/`. ## Files to EXCLUDE These files intentionally test unversioned path behavior: | File | Reason | | ---------------------------------------------------- | ------------------------------------ | | `src/routes/versioning.integration.test.ts` | Tests redirect behavior itself | | `src/services/apiClient.test.ts` | Mock server URLs, not real API calls | | `src/services/aiApiClient.test.ts` | Mock server URLs for MSW handlers | | `src/services/googleGeocodingService.server.test.ts` | External Google API URL | **Also exclude** (not API paths): - Lines containing `vi.mock('@bull-board/api` (import mocks) - Lines containing `/api/v99` (intentional unsupported version tests) - `describe()` and `it()` block descriptions - Comment lines (`// `) ## Execution Batches ### Batch 1: High-Impact Integration (4 files, ~58 occurrences) ```bash # Files with most occurrences src/tests/integration/inventory.integration.test.ts src/tests/integration/receipt.integration.test.ts src/tests/integration/recipe.integration.test.ts src/tests/integration/user.routes.integration.test.ts ``` ### Batch 2: Medium Integration (6 files, ~27 occurrences) ```bash src/tests/integration/admin.integration.test.ts src/tests/integration/flyer-processing.integration.test.ts src/tests/integration/budget.integration.test.ts src/tests/integration/notification.integration.test.ts src/tests/integration/data-integrity.integration.test.ts src/tests/integration/upc.integration.test.ts ``` ### Batch 3: Low Integration (6 files, ~10 occurrences) ```bash src/tests/integration/edge-cases.integration.test.ts src/tests/integration/user.integration.test.ts src/tests/integration/public.routes.integration.test.ts src/tests/integration/flyer.integration.test.ts src/tests/integration/category.routes.test.ts src/tests/integration/gamification.integration.test.ts ``` ### Batch 4: E2E Tests (7 files, ~31 occurrences) ```bash src/tests/e2e/inventory-journey.e2e.test.ts src/tests/e2e/receipt-journey.e2e.test.ts src/tests/e2e/budget-journey.e2e.test.ts src/tests/e2e/upc-journey.e2e.test.ts src/tests/e2e/deals-journey.e2e.test.ts src/tests/e2e/user-journey.e2e.test.ts src/tests/e2e/flyer-upload.e2e.test.ts ``` ## Verification Strategy ### Per-Batch Verification After each batch: ```bash # Type check podman exec -it flyer-crawler-dev npm run type-check # Run specific test file podman exec -it flyer-crawler-dev npx vitest run --reporter=verbose ``` ### Full Verification After all batches: ```bash # Full integration test suite podman exec -it flyer-crawler-dev npm run test:integration # Full E2E test suite podman exec -it flyer-crawler-dev npm run test:e2e ``` ### Success Criteria - [x] No `301 Moved Permanently` responses in test output - [x] All tests pass or fail for expected reasons (not redirect-related) - [x] Type check passes - [x] No regressions in unmodified tests ## Edge Cases ### Describe Block Text Do NOT modify describe/it block descriptions: ```typescript // KEEP AS-IS (documentation only): describe('GET /api/users/profile', () => { ... }); // UPDATE (actual API call): const response = await request.get('/api/v1/users/profile'); ``` ### Console Logging Do NOT modify debug/error logging paths: ```typescript // KEEP AS-IS: console.error('[DEBUG] GET /api/admin/stats failed:', ...); ``` ### Query Parameters Include query parameters in update: ```typescript // OLD: .get(`/api/budgets/spending-analysis?startDate=${start}&endDate=${end}`) // NEW: .get(`/api/v1/budgets/spending-analysis?startDate=${start}&endDate=${end}`) ``` ## Post-Completion Checklist - [x] All 23 files updated - [x] ~70 path occurrences migrated - [x] Exclusion files unchanged - [x] Type check passes - [x] Integration tests pass (345/348) - [x] E2E tests pass - [x] Commit with message: `fix(tests): Update API paths to use /api/v1/ prefix (ADR-008)` ## Rollback If issues arise: ```bash git checkout HEAD -- src/tests/ ``` ## Related Documentation - ADR-008: API Versioning Strategy - `docs/architecture/api-versioning-infrastructure.md` - `src/routes/versioning.integration.test.ts` (reference for expected behavior)