Files
flyer-crawler.projectium.com/docs/plans/2026-01-18-frontend-test-automation-plan.md
Torben Sorensen c24103d9a0
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 16m42s
frontend direct testing result and fixes
2026-01-18 13:57:47 -08:00

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_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).


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:

  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?