Files
flyer-crawler.projectium.com/docs/plans/2026-01-27-unit-test-error-log-path-fix.md
Torben Sorensen 0d4b028a66
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 21m49s
design fixup and docs + api versioning
2026-01-28 00:04:56 -08:00

5.4 KiB

Unit Test Fix Plan: Error Log Path Mismatches

Date: 2026-01-27 Type: Technical Implementation Plan Related: ADR-008: API Versioning Strategy Status: Ready for Implementation


Problem Statement

16 unit tests fail due to error log message assertions expecting versioned paths (/api/v1/) while route handlers emit hardcoded unversioned paths (/api/).

Failure Pattern:

AssertionError: expected "Error PUT /api/users/profile" to contain "/api/v1/users/profile"

Scope: All failures are toContain assertions on logger.error() call arguments.


Root Cause Analysis

Layer Behavior Issue
Route Registration server.ts mounts at /api/v1/ Correct
Request Path req.path returns /users/profile (router-relative) No version info
Error Handlers Hardcode "Error PUT /api/users/profile" Version mismatch
Test Assertions Expect "/api/v1/users/profile" Correct expectation

Root Cause: Error log statements use template literals with hardcoded /api/ prefix instead of req.originalUrl which contains the full versioned path.

Example:

// Current (broken)
logger.error(`Error PUT /api/users/profile: ${err}`);

// Expected
logger.error(`Error PUT ${req.originalUrl}: ${err}`);
// Output: "Error PUT /api/v1/users/profile: ..."

Solution Approach

Replace hardcoded path strings with req.originalUrl in all error log statements.

Express Request Properties Reference

Property Example Value Use Case
req.originalUrl /api/v1/users/profile?foo=bar Full URL with version + query
req.path /profile Router-relative path only
req.baseUrl /api/v1/users Mount point

Decision: Use req.originalUrl for error logging to capture complete request context.


Implementation Plan

Affected Files

File Error Statements Methods
src/routes/users.routes.ts 3 PUT /profile, POST /profile/password, DELETE /
src/routes/recipe.routes.ts 2 POST /import, POST /:id/fork
src/routes/receipts.routes.ts 2 POST /, PATCH /:id
src/routes/flyers.routes.ts 2 POST /, PUT /:id

Total: 9 error log statements across 4 route files

Parallel Implementation Tasks

All 4 files can be modified independently:

Task 1: users.routes.ts

  • Line patterns: Error PUT /api/users/profile, Error POST /api/users/profile/password, Error DELETE /api/users
  • Change: Replace with Error ${req.method} ${req.originalUrl}

Task 2: recipe.routes.ts

  • Line patterns: Error POST /api/recipes/import, Error POST /api/recipes/:id/fork
  • Change: Replace with Error ${req.method} ${req.originalUrl}

Task 3: receipts.routes.ts

  • Line patterns: Error POST /api/receipts, Error PATCH /api/receipts/:id
  • Change: Replace with Error ${req.method} ${req.originalUrl}

Task 4: flyers.routes.ts

  • Line patterns: Error POST /api/flyers, Error PUT /api/flyers/:id
  • Change: Replace with Error ${req.method} ${req.originalUrl}

Verification

podman exec -it flyer-crawler-dev npm run test:unit

Expected: 16 failures → 0 failures (3,391/3,391 passing)


Test Files Affected

Tests that will pass after fix:

Test File Failing Tests
users.routes.test.ts 6
recipe.routes.test.ts 4
receipts.routes.test.ts 3
flyers.routes.test.ts 3

Expected Outcomes

Metric Before After
Unit test failures 16 0
Unit tests passing 3,375/3,391 3,391/3,391
Integration tests 345/348 345/348 (unchanged)

Benefits

  1. Version-agnostic logging: Error messages automatically reflect actual request URL
  2. Future-proof: No changes needed when v2 API is introduced
  3. Debugging clarity: Logs show exact URL including query parameters
  4. Consistency: All error handlers follow same pattern

Implementation Notes

Pattern to Apply

Before:

logger.error(`Error PUT /api/users/profile: ${error.message}`);

After:

logger.error(`Error ${req.method} ${req.originalUrl}: ${error.message}`);

Edge Cases

  • req.originalUrl includes query string if present (acceptable for debugging)
  • No sanitization needed as URL is from Express parsed request
  • Works correctly with route parameters (:id becomes actual value)