9.3 KiB
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 Permanentlyresponses 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:
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
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()andit()block descriptions- Comment lines (
//)
Execution Batches
Batch 1: High-Impact Integration (4 files, ~58 occurrences)
# 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)
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)
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)
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:
# 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 <file-path> --reporter=verbose
Full Verification
After all batches:
# 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
- No
301 Moved Permanentlyresponses in test output - All tests pass or fail for expected reasons (not redirect-related)
- Type check passes
- No regressions in unmodified tests
Edge Cases
Describe Block Text
Do NOT modify describe/it block descriptions:
// 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:
// KEEP AS-IS:
console.error('[DEBUG] GET /api/admin/stats failed:', ...);
Query Parameters
Include query parameters in update:
// OLD:
.get(`/api/budgets/spending-analysis?startDate=${start}&endDate=${end}`)
// NEW:
.get(`/api/v1/budgets/spending-analysis?startDate=${start}&endDate=${end}`)
Post-Completion Checklist
- All 23 files updated
- ~70 path occurrences migrated
- Exclusion files unchanged
- Type check passes
- Integration tests pass (345/348)
- E2E tests pass
- Commit with message:
fix(tests): Update API paths to use /api/v1/ prefix (ADR-008)
Rollback
If issues arise:
git checkout HEAD -- src/tests/
Related Documentation
- ADR-008: API Versioning Strategy
docs/architecture/api-versioning-infrastructure.mdsrc/routes/versioning.integration.test.ts(reference for expected behavior)