Files
edr-platform/e2e/freight/cypress/e2e/flows/government_preemption.cy.ts
2026-07-24 13:26:19 +00:00

255 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* BATCH — government preemption:
*
* A government booking that fits nowhere on an already-committed train
* displaces the LOWEST-priority commercial reservation first (never the
* higher-priority one), then allocates directly — no pay window, since
* government rides unpaid (see booking-batch.service.ts `preemptForGovernment`
* + `allocate(..., "gov")`).
*
* Setup: a 3-wagon import train. CGA (priority 1, higher) and CGB (priority
* 2, lower) each book one 40ft container and reserve via the normal batch —
* 2/3 wagons used, 1 free. A standalone government booking (2×40ft = 2
* wagons) then needs more than the 1 free slot: it doesn't fit, so the
* engine preempts CGB (the lower-priority reservation) to free the second
* wagon, then allocates the government booking. CGA is untouched throughout.
*
* Government bookings don't go through the customer contract wizard —
* they're created directly via `POST /bookings` (isGovernment: true, billed
* to a real government company/profile — see seed-government.sql) and
* promoted with `POST /bookings/:id/government-expedite`.
*
* Sequential steps of one scenario — retries off.
*/
import {
acceptOperation,
apiPost,
bookContainers,
closeBookingWindow,
completeDocReview,
createImportSchedule,
db,
departureAt,
DEST,
eatDayStr,
ensureCorridorRoute,
forceWindowOpen,
ORIGIN,
pollBookingStatus,
pollDb,
resetCorridorDay,
seedImportContract,
setPriority,
superAdmin,
withBooking,
withSchedule,
type ScheduleRow,
} from "./import-utils";
const DEPARTURE = departureAt(27);
const BOOKING_DAY = eatDayStr(DEPARTURE);
const stamp = String(Date.now());
const stampedRef = (suffix: string) => `CTR-IMP-${stamp}-${suffix}`;
const GOV_COMPANY_ID = "0a1b0001-0000-4000-8000-000000000001";
const GOV_PROFILE_ID = "0b1c0001-0000-4000-8000-000000000001";
function withScheduleId(fn: (id: string, s: ScheduleRow) => void) {
withSchedule(DEPARTURE, (s) => fn(s.id, s));
}
interface GovBookingRow {
id: string;
status: string;
scheduling_status: string;
is_government: boolean;
train_schedule_id: string | null;
}
/** This run's government booking — always re-queried, never trusted from a
* create-response shape (only one exists per run: newest for this gov company). */
function withGovBooking(fn: (b: GovBookingRow) => void) {
db<GovBookingRow>(
`SELECT id, status, scheduling_status, is_government, train_schedule_id
FROM freight.bookings
WHERE company_id = $1 AND is_government = true AND deleted_at IS NULL
ORDER BY created_at DESC LIMIT 1`,
[GOV_COMPANY_ID],
).then(({ rows }) => {
expect(rows, "this run's government booking").to.have.length(1);
fn(rows[0]);
});
}
describe("batch: government booking preempts the lowest-priority commercial reservation", { retries: 0 }, () => {
before(() => {
cy.task("db:seedFile", "seed-import-corridor.sql");
cy.task("db:seedFile", "seed-government.sql");
["CGA", "CGB"].forEach((suffix) =>
seedImportContract({ suffix, reference: stampedRef(suffix) }),
);
});
it("operations schedules the dedicated 3-wagon built train (TRN-GOV-1) — window forced open", () => {
ensureCorridorRoute();
resetCorridorDay(DEPARTURE);
// A loco-pair schedule's max_wagons is NOT a real cap — it gets recomputed
// from the locomotive's length every fill pass (54 for a standard loco),
// ignoring maxWagonsPerTrain. A BUILT train's coupled count IS the cap.
createImportSchedule({
departure: DEPARTURE,
trainCode: "TRN-GOV-1",
maxWagons: 3,
});
withScheduleId((id) => forceWindowOpen(id, 45));
});
it("CGA and CGB each book one 40ft and reserve — 2/3 wagons used, 1 free", () => {
bookContainers({
suffix: "CGA",
runStamp: stamp,
isoSeed: 0,
forty: 1,
scheduledDate: BOOKING_DAY,
});
acceptOperation("CGA");
bookContainers({
suffix: "CGB",
runStamp: stamp,
isoSeed: 10,
forty: 1,
scheduledDate: BOOKING_DAY,
});
acceptOperation("CGB");
setPriority("CGA", 1); // higher priority — must survive
setPriority("CGB", 2); // lower priority — the preemption target
withScheduleId((id) => {
closeBookingWindow(id);
completeDocReview(id);
});
withBooking("CGA", (b) =>
expect(b.status, "CGA reserved").to.be.oneOf(["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
);
withBooking("CGB", (b) =>
expect(b.status, "CGB reserved").to.be.oneOf(["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
);
});
it("a government booking (2×40ft) is created and expedited to PAID, then pinned to the train", () => {
db<{ id: string }>(
`SELECT id FROM freight.yards WHERE code = $1`,
[ORIGIN],
).then(({ rows: o }) => {
db<{ id: string }>(`SELECT id FROM freight.yards WHERE code = $1`, [DEST]).then(
({ rows: d }) => {
db<{ id: string }>(
`SELECT id FROM freight.service_types ORDER BY created_at LIMIT 1`,
).then(({ rows: st }) => {
db<{ id: string }>(
`SELECT id FROM freight.container_types WHERE size_ft = 40 AND is_active LIMIT 1`,
).then(({ rows: ct }) => {
apiPost(superAdmin, "/api/bookings", {
isGovernment: true,
companyId: GOV_COMPANY_ID,
companyProfileId: GOV_PROFILE_ID,
contractType: "NEW",
serviceTypeId: st[0].id,
equipmentReturn: "WITHOUT_RETURN",
originYardId: o[0].id,
destinationYardId: d[0].id,
tradeDirection: "IMPORT",
freightType: "CONTAINER",
containers: [{ containerTypeId: ct[0].id, quantity: 2, vgmPerUnitTons: 10 }],
cargoTotalWeightVgm: 20,
paymentCurrency: "USD",
}).then((res) => {
expect(res.status, "government booking created").to.be.oneOf([200, 201]);
withGovBooking((created) => {
apiPost(
superAdmin,
`/api/bookings/${created.id}/government-expedite`,
).then((expRes) => {
expect(expRes.status, "expedited").to.be.oneOf([200, 201]);
});
});
withGovBooking((b) => {
expect(b.status, "PAID after expedite").to.eq("PAID");
expect(b.is_government, "flagged government").to.eq(true);
// Staff pin onto this schedule (the customer-facing OPEN-window
// pin can't be used here — the window already closed when
// completeDocReview ran). Mirrors a staff manual assign.
withScheduleId((scheduleId) => {
db(
`UPDATE freight.bookings SET train_schedule_id = $1 WHERE id = $2`,
[scheduleId, b.id],
);
});
});
});
});
});
},
);
});
});
it("run-batch: the government booking preempts CGB (lower priority), CGA is untouched", () => {
withScheduleId((scheduleId) => {
apiPost(superAdmin, `/api/train-scheduling/schedules/${scheduleId}/run-batch`).then(
(res) => expect(res.status, "run-batch").to.be.oneOf([200, 201]),
);
});
// CGB displaced: EXPIRED, back to ELIGIBLE, no pay window, invoice expired.
pollBookingStatus("CGB", "EXPIRED");
withBooking("CGB", (b) => {
expect(b.scheduling_status, "CGB back to ELIGIBLE").to.eq("ELIGIBLE");
expect(b.payment_deadline, "CGB pay window cleared").to.be.null;
db<{ n: string }>(
`SELECT count(*) AS n FROM freight.invoices
WHERE source = 'booking' AND source_id = $1
AND status NOT IN ('EXPIRED','CANCELLED','PAID') AND deleted_at IS NULL`,
[b.id],
).then(({ rows }) => expect(Number(rows[0].n), "CGB invoice expired").to.eq(0));
});
// CGA survives untouched — still reserved on the same train.
withScheduleId((scheduleId) => {
withBooking("CGA", (b) => {
expect(b.status, "CGA still reserved").to.be.oneOf([
"SELECTED_FOR_BATCH",
"AWAITING_PAYMENT",
]);
expect(b.train_schedule_id, "CGA still on this train").to.eq(scheduleId);
});
});
// Government booking allocated: SCHEDULED + linked via the schedule-bookings
// table (allocate() never sets the train_schedule_id column for gov — only
// the link row — so the pin from the previous step is what carries it).
withGovBooking((b) => {
expect(b.scheduling_status, "gov SCHEDULED").to.eq("SCHEDULED");
withScheduleId((scheduleId) => {
db<{ n: string }>(
`SELECT count(*) AS n FROM freight.train_schedule_bookings
WHERE booking_id = $1 AND train_schedule_id = $2 AND deleted_at IS NULL`,
[b.id, scheduleId],
).then(({ rows }) => expect(Number(rows[0].n), "gov linked to schedule").to.eq(1));
});
pollDb<{ n: string }>(
"gov booking allocated 2 wagons",
`SELECT count(*) AS n FROM freight.wagon_booking_allocations WHERE booking_id = $1`,
[b.id],
(row) => Number(row?.n ?? 0) >= 2,
);
});
});
});
export {};