# Task 5 Report: Server-authoritative travel ## Outcome Implemented the NestJS travel domain and public endpoints: - `POST /api/travel` - `GET /api/travel/current` - injected deterministic/system clock abstraction - exact `StartTravelDto` input validation - stable idle, travelling, completed, and domain-error response shapes - `TravelModule` registration in the application Travel start and completion use `DataSource.transaction`. Both operations acquire a pessimistic write lock on the character row followed by the active travel row. Relations are deliberately not joined into either locking query, which keeps the queries compatible with TypeORM/PostgreSQL `FOR UPDATE` behavior. Due completion changes the travel status and character location through repositories owned by the same transaction; a failed second save rolls back both mutations. No ambush evaluation or other later-slice logic was added. Existing untracked frontend images and the visual asset guide were preserved and excluded from the commit. ## RED evidence Command: ```powershell npm test --workspace=@ashen-realms/api -- travel.service.spec.ts travel.controller.spec.ts --runInBand ``` Initial result: exit code 1. Both suites failed to resolve the missing production modules: ```text Cannot find module './travel.service' from 'travel/travel.service.spec.ts' Cannot find module './travel.controller' from 'travel/travel.controller.spec.ts' Test Suites: 2 failed, 2 total Tests: 0 total ``` The active-travel regression test was also mutation-checked. Temporarily removing the `TRAVEL_ALREADY_ACTIVE` guard produced the expected focused failure (`Expected constructor: TravelDomainError; Received constructor: Object`); restoring the guard returned the test to green. ## GREEN evidence Focused travel tests: ```powershell npm test --workspace=@ashen-realms/api -- travel.service.spec.ts travel.controller.spec.ts --runInBand ``` ```text Test Suites: 2 passed, 2 total Tests: 9 passed, 9 total Time: 10.973 s ``` The service suite covers all five required behaviors plus active-travel rejection, public current-travel mapping, idempotent repeat completion, pessimistic-lock observation, and rollback when the second completion save fails. The controller suite drives a real Nest HTTP pipeline and verifies that server-owned timestamp/duration fields receive HTTP 400. Full API suite: ```powershell npm test --workspace=@ashen-realms/api -- --runInBand ``` ```text Test Suites: 7 passed, 7 total Tests: 18 passed, 18 total Time: 15.87 s ``` API E2E: ```powershell npm run test:e2e --workspace=@ashen-realms/api -- --runInBand ``` ```text Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Time: 9.406 s ``` API build: ```powershell npm run build:api ``` Result: exit code 0 (`nest build`). Formatting and lint: ```powershell apps/api/node_modules/.bin/prettier.cmd --check node_modules/.bin/eslint.cmd ``` Both commands exited 0. Prettier reported `All matched files use Prettier code style!`; ESLint reported no findings. ## Integration notes and self-review - The first build exposed TypeScript `TS1272` for a decorated `Clock` parameter under `isolatedModules`; importing `Clock` as a type fixed the root cause, after which tests and build were rerun. - Registering the database-backed `TravelModule` exposed that the existing health E2E test replaced `DatabaseModule` and `CharactersModule` but not travel. The harness now replaces `TravelModule` as well, preserving the database-free health test. - Lock order is identical in start and completion, reducing deadlock risk. - The character row serializes concurrent starts even when no active travel row exists yet; the partial unique database index remains the final invariant. - The due comparison treats `arrivesAt === now` as due and never moves the character earlier. - Public responses contain only location summaries and travel timestamps/status, never entities, duration input, ambush probability, or persistence metadata. ## Remaining concern No live-PostgreSQL travel integration test was added because the repository's current E2E harness intentionally runs without a database; clean migration/seed/API database smoke coverage belongs to Task 10. The transaction code uses supported real `EntityManager.getRepository`, `Repository.findOne` lock options, `findOneBy`, `create`, and `save` behavior, and the stateful fake verifies transaction commit/rollback semantics rather than mock call counts.