All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 16m42s
350 lines
12 KiB
Markdown
350 lines
12 KiB
Markdown
# 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?
|