Add a minimal payment mock server for end-to-end testing

This commit is contained in:
Marshal
2026-08-01 18:59:14 +00:00
parent 8ccb0e1558
commit 21fdf144ca
8 changed files with 420 additions and 105 deletions

View File

@@ -139,3 +139,47 @@ cast past the type checker (`last-mile-invoice.service.ts:41`), while
varchar with no constraint, so both spellings persist. Writes and reads agree
within each module so billing works — but S39's "invoice source LASTMILE"
assertion must match `'last_mile'`, not the enum value.
## Two engine changes the committed specs predate
Both were found by running the suite, and both broke EVERY scenario until
fixed. They are recorded here because neither is visible from the scenario
document — only from the API source.
**1. Every contract booking is born in the clearance gate.**
`contract-booking.service.ts:211` — *"EVERY contract booking clears per booking
now — both contract kinds, both paths, intercity included."* A booking is
created in `AWAITING_DOCUMENTS` regardless of whether customs clearance is
enabled, so `bookContainers` followed by `acceptOperation` always 409s with
`Cannot perform this action on status "AWAITING_DOCUMENTS". Allowed:
OPERATION_REQUEST_PENDING`.
The gate is upload → GL approve → finalize → customer proceeds with the day.
`clearToOperationRequestPending` (import-utils) runs it; `bookAndClear`
(g1-utils) wraps book + clear + accept and is what the g-specs use.
NOTE: the pre-existing corridor specs (e.g. `import_full_train.cy.ts`) still
call `acceptOperation` directly and fail for this reason — 3 passing / 10
failing when last run. They predate the gate and need the same treatment.
**2. An unpaid hold cannot expire without a reachable payment gateway.**
Before expiring a reservation the engine asks the gateway whether a late
payment landed (`booking-batch.service.ts:3484-3506`), and treats ANY error as
`unverifiable: true` — deferring the expiry rather than risk expiring a
customer who paid:
[BATCH] expire deferred for BK-… — settlement unverifiable at the
gateway; retrying next settle tick
`PAYMENT_API_URL` defaults to the real `https://paymentcallback.triaplc.com`
(`payment-client.service.ts:25`), unreachable from e2e, so every expiry
deferred forever. Six scenarios turn on an expiry: G1·S1, G1·S5, G1·S6,
G3·S16, G5·S22, G5·S24.
Fixed with a stand-in service — `e2e/freight/payment-mock/server.js`, wired as
`payment-mock-e2e` in `docker-compose.e2e.yaml` with
`PAYMENT_API_URL: http://payment-mock-e2e:4500`. It answers
`POST /payments/reconcile` with `{paid:false, unverifiable:false}` so the
engine gets a definite "no payment exists" and expires the hold as designed.
A stack that does NOT point PAYMENT_API_URL at a reachable service will hang
on every expiry assertion.