All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 17m56s
273 lines
9.3 KiB
Markdown
273 lines
9.3 KiB
Markdown
# 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 <file-path> --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)
|