# 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)
