12 KiB
Frontend Test Automation Plan
Date: 2026-01-18 Status: Awaiting Approval Related: 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.
// 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.
// 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.
// 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.
// 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:
// 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:
// 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:
// 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:
// 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:
// 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:
// 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_idmust be STRING in reactionscustomItemName(camelCase) in shopping list itemsscan_sourcemust bemanual_entry, notmanual
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:
# In dev container
npm run test:integration -- --run src/tests/integration/<file>.test.ts
All tests should:
- Pass consistently (no flaky tests)
- Run in isolation (no shared state)
- Clean up test data (use
cleanupDb()) - 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:
- Should the deals/reactions routes remain mounted, or was that a temporary fix?
- Is the recipe fork failure for seed recipes expected behavior or a bug to fix?
- Any preference on splitting Phase 1 into multiple PRs vs one large PR?