# Frontend Test Automation Plan

**Date**: 2026-01-18
**Status**: Awaiting Approval
**Related**: [2026-01-18-frontend-tests.md](../tests/2026-01-18-frontend-tests.md)

## Executive Summary

This plan formalizes the automated testing of 35+ API endpoints manually tested on 2026-01-18. The testing covered 7 major areas including end-to-end user flows, edge cases, queue behavior, authentication, performance, real-time features, and data integrity.

**Recommendation**: Most tests should be added as **integration tests** (Supertest-based), with select critical flows as **E2E tests**. This aligns with ADR-010 and ADR-040's guidance on testing economics.

---

## Analysis of Manual Tests vs Existing Coverage

### Current Test Coverage

| Test Type   | Existing Files | Existing Tests |
| ----------- | -------------- | -------------- |
| Integration | 21 files       | ~150+ tests    |
| E2E         | 9 files        | ~40+ tests     |

### Gap Analysis

| Manual Test Area           | Existing Coverage         | Gap                         | Priority |
| -------------------------- | ------------------------- | --------------------------- | -------- |
| Budget API                 | budget.integration.test   | Partial - add validation    | Medium   |
| Deals API                  | None                      | **New file needed**         | Low      |
| Reactions API              | None                      | **New file needed**         | Low      |
| Gamification API           | gamification.integration  | Good coverage               | None     |
| Recipe API                 | recipe.integration.test   | Add fork error, comment     | Medium   |
| Receipt API                | receipt.integration.test  | Good coverage               | None     |
| UPC API                    | upc.integration.test      | Good coverage               | None     |
| Price History API          | price.integration.test    | Good coverage               | None     |
| Personalization API        | public.routes.integration | Good coverage               | None     |
| Admin Routes               | admin.integration.test    | Add queue/trigger endpoints | Medium   |
| Edge Cases (Area 2)        | Scattered                 | **Consolidate/add**         | High     |
| Queue/Worker (Area 3)      | Partial                   | Add admin trigger tests     | Medium   |
| Auth Edge Cases (Area 4)   | auth.integration.test     | Add token malformation      | Medium   |
| Performance (Area 5)       | None                      | **Not recommended**         | Skip     |
| Real-time/Polling (Area 6) | notification.integration  | Add job status polling      | Low      |
| Data Integrity (Area 7)    | Scattered                 | **Consolidate**             | High     |

---

## Implementation Plan

### Phase 1: New Integration Test Files (Priority: High)

#### 1.1 Create `deals.integration.test.ts`

**Rationale**: Routes were unmounted until this testing session; no tests exist.

```typescript
// Tests to add:
describe('Deals API', () => {
  it('GET /api/deals/best-watched-prices requires auth');
  it('GET /api/deals/best-watched-prices returns watched items for user');
  it('Returns empty array when no watched items');
});
```

**Estimated effort**: 30 minutes

#### 1.2 Create `reactions.integration.test.ts`

**Rationale**: Routes were unmounted until this testing session; no tests exist.

```typescript
// Tests to add:
describe('Reactions API', () => {
  it('GET /api/reactions/summary/:targetType/:targetId returns counts');
  it('POST /api/reactions/toggle requires auth');
  it('POST /api/reactions/toggle toggles reaction on/off');
  it('Returns validation error for invalid target_type');
  it('Returns validation error for non-string entity_id');
});
```

**Estimated effort**: 45 minutes

#### 1.3 Create `edge-cases.integration.test.ts`

**Rationale**: Consolidate edge case tests discovered during manual testing.

```typescript
// Tests to add:
describe('Edge Cases', () => {
  describe('File Upload Validation', () => {
    it('Accepts small files');
    it('Processes corrupt file with IMAGE_CONVERSION_FAILED');
    it('Rejects wrong checksum format');
    it('Rejects short checksum');
  });

  describe('Input Sanitization', () => {
    it('Handles XSS payloads in shopping list names (stores as-is)');
    it('Handles unicode/emoji in text fields');
    it('Rejects null bytes in JSON');
    it('Handles very long input strings');
  });

  describe('Authorization Boundaries', () => {
    it('Cross-user access returns 404 (not 403)');
    it('SQL injection in query params is safely handled');
  });
});
```

**Estimated effort**: 1.5 hours

#### 1.4 Create `data-integrity.integration.test.ts`

**Rationale**: Consolidate FK/cascade/constraint tests.

```typescript
// Tests to add:
describe('Data Integrity', () => {
  describe('Cascade Deletes', () => {
    it('User deletion cascades to shopping lists, budgets, notifications');
    it('Shopping list deletion cascades to items');
    it('Admin cannot delete own account');
  });

  describe('FK Constraints', () => {
    it('Rejects invalid FK references via API');
    it('Rejects invalid FK references via direct DB');
  });

  describe('Unique Constraints', () => {
    it('Duplicate email returns CONFLICT');
    it('Duplicate flyer checksum is handled');
  });

  describe('CHECK Constraints', () => {
    it('Budget period rejects invalid values');
    it('Budget amount rejects negative values');
  });
});
```

**Estimated effort**: 2 hours

---

### Phase 2: Extend Existing Integration Tests (Priority: Medium)

#### 2.1 Extend `budget.integration.test.ts`

Add validation edge cases discovered during manual testing:

```typescript
// Tests to add:
it('Rejects period="yearly" (only weekly/monthly allowed)');
it('Rejects negative amount_cents');
it('Rejects invalid date format');
it('Returns 404 for update on non-existent budget');
it('Returns 404 for delete on non-existent budget');
```

**Estimated effort**: 30 minutes

#### 2.2 Extend `admin.integration.test.ts`

Add queue and trigger endpoint tests:

```typescript
// Tests to add:
describe('Queue Management', () => {
  it('GET /api/admin/queues/status returns all queue counts');
  it('POST /api/admin/trigger/analytics-report enqueues job');
  it('POST /api/admin/trigger/weekly-analytics enqueues job');
  it('POST /api/admin/trigger/daily-deal-check enqueues job');
  it('POST /api/admin/jobs/:queue/:id/retry retries failed job');
  it('POST /api/admin/system/clear-cache clears Redis cache');
  it('Returns validation error for invalid queue name');
  it('Returns 404 for retry on non-existent job');
});
```

**Estimated effort**: 1 hour

#### 2.3 Extend `auth.integration.test.ts`

Add token malformation edge cases:

```typescript
// Tests to add:
describe('Token Edge Cases', () => {
  it('Empty Bearer token returns Unauthorized');
  it('Token without dots returns Unauthorized');
  it('Token with 2 parts returns Unauthorized');
  it('Token with invalid signature returns Unauthorized');
  it('Lowercase "bearer" scheme is accepted');
  it('Basic auth scheme returns Unauthorized');
  it('Tampered token payload returns Unauthorized');
});

describe('Login Security', () => {
  it('Wrong password and non-existent user return same error');
  it('Forgot password returns same response for existing/non-existing');
});
```

**Estimated effort**: 45 minutes

#### 2.4 Extend `recipe.integration.test.ts`

Add fork error case and comment tests:

```typescript
// Tests to add:
it('Fork fails for seed recipes (null user_id)');
it('POST /api/recipes/:id/comments adds comment');
it('GET /api/recipes/:id/comments returns comments');
```

**Estimated effort**: 30 minutes

#### 2.5 Extend `notification.integration.test.ts`

Add job status polling tests:

```typescript
// Tests to add:
describe('Job Status Polling', () => {
  it('GET /api/ai/jobs/:id/status returns completed job');
  it('GET /api/ai/jobs/:id/status returns failed job with error');
  it('GET /api/ai/jobs/:id/status returns 404 for non-existent');
  it('Job status endpoint works without auth (public)');
});
```

**Estimated effort**: 30 minutes

---

### Phase 3: E2E Tests (Priority: Low-Medium)

Per ADR-040, E2E tests should be limited to critical user flows. The existing E2E tests cover the main flows well. However, we should consider:

#### 3.1 Do NOT Add

- Performance tests (handle via monitoring, not E2E)
- Pagination tests (integration level is sufficient)
- Cache behavior tests (integration level is sufficient)

#### 3.2 Consider Adding (Optional)

**Budget flow E2E** - If budget management becomes a critical feature:

```typescript
// budget-journey.e2e.test.ts
describe('Budget Journey', () => {
  it('User creates budget → tracks spending → sees analysis');
});
```

**Recommendation**: Defer unless budget becomes a core value proposition.

---

### Phase 4: Documentation Updates

#### 4.1 Update ADR-010

Add the newly discovered API gotchas to the testing documentation:

- `entity_id` must be STRING in reactions
- `customItemName` (camelCase) in shopping list items
- `scan_source` must be `manual_entry`, not `manual`

#### 4.2 Update CLAUDE.md

Add API reference section for correct endpoint calls (already captured in test doc).

---

## Tests NOT Recommended

Per ADR-040 (Testing Economics), the following tests from the manual session should NOT be automated:

| Test Area                   | Reason                                            |
| --------------------------- | ------------------------------------------------- |
| Performance benchmarks      | Use APM/monitoring tools instead (see ADR-015)    |
| Concurrent request handling | Connection pool behavior is framework-level       |
| Cache hit/miss timing       | Observable via Redis metrics, not test assertions |
| Response time consistency   | Better suited for production monitoring           |
| WebSocket/SSE               | Not implemented - polling is the architecture     |

---

## Implementation Timeline

| Phase     | Description                    | Effort       | Priority |
| --------- | ------------------------------ | ------------ | -------- |
| 1.1       | deals.integration.test.ts      | 30 min       | High     |
| 1.2       | reactions.integration.test.ts  | 45 min       | High     |
| 1.3       | edge-cases.integration.test.ts | 1.5 hours    | High     |
| 1.4       | data-integrity.integration.ts  | 2 hours      | High     |
| 2.1       | Extend budget tests            | 30 min       | Medium   |
| 2.2       | Extend admin tests             | 1 hour       | Medium   |
| 2.3       | Extend auth tests              | 45 min       | Medium   |
| 2.4       | Extend recipe tests            | 30 min       | Medium   |
| 2.5       | Extend notification tests      | 30 min       | Medium   |
| 4.x       | Documentation updates          | 30 min       | Low      |
| **Total** |                                | **~8 hours** |          |

---

## Verification Strategy

For each new test file, verify by running:

```bash
# In dev container
npm run test:integration -- --run src/tests/integration/<file>.test.ts
```

All tests should:

1. Pass consistently (no flaky tests)
2. Run in isolation (no shared state)
3. Clean up test data (use `cleanupDb()`)
4. Follow existing patterns in the codebase

---

## Risks and Mitigations

| Risk                                 | Mitigation                                          |
| ------------------------------------ | --------------------------------------------------- |
| Test flakiness from async operations | Use proper waitFor/polling utilities                |
| Database state leakage between tests | Strict cleanup in afterEach/afterAll                |
| Queue state affecting test isolation | Drain/pause queues in tests that interact with them |
| Port conflicts                       | Use dedicated test port (3099)                      |

---

## Approval Request

Please review and approve this plan. Upon approval, implementation will proceed in priority order (Phase 1 first).

**Questions for clarification**:

1. Should the deals/reactions routes remain mounted, or was that a temporary fix?
2. Is the recipe fork failure for seed recipes expected behavior or a bug to fix?
3. Any preference on splitting Phase 1 into multiple PRs vs one large PR?
