mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 23:35:42 +00:00
137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
/**
|
|
* GENERAL mixed reopen (D+26) — the same two GENERAL contracts (one
|
|
* container, one bulk) book AGAIN after their first bookings die: cycle 1
|
|
* reserves a container + a bulk drawdown, nobody pays, both expire, the
|
|
* window reopens — and the SAME contracts issue fresh drawdowns in cycle 2
|
|
* that pay and allocate typed. GENERAL contracts survive dead bookings and
|
|
* dead cycles alike.
|
|
*
|
|
* Sequential steps of one journey — retries off.
|
|
*/
|
|
|
|
import {
|
|
bookBulk,
|
|
bookContainers,
|
|
clearGeneralBooking,
|
|
closeBookingWindow,
|
|
completeDocReview,
|
|
createImportSchedule,
|
|
departureAt,
|
|
eatDayStr,
|
|
endPaymentPhase,
|
|
ensureCorridorRoute,
|
|
expectWagonType,
|
|
forceReservationExpiry,
|
|
forceWindowOpen,
|
|
markPaid,
|
|
pollAllocations,
|
|
pollBookingStatus,
|
|
pollDb,
|
|
resetCorridorDay,
|
|
seedImportContract,
|
|
withBooking,
|
|
withSchedule,
|
|
type ScheduleRow,
|
|
} from "./import-utils";
|
|
|
|
const DEPARTURE = departureAt(26);
|
|
const BOOKING_DAY = eatDayStr(DEPARTURE);
|
|
|
|
const stamp = String(Date.now());
|
|
const stampedRef = (suffix: string) => `CTR-IMP-${stamp}-${suffix}`;
|
|
|
|
describe("GENERAL mixed reopen: the same contracts book again after a dead cycle", { retries: 0 }, () => {
|
|
before(() => {
|
|
cy.task("db:seedFile", "seed-import-corridor.sql");
|
|
seedImportContract({ suffix: "GMC", reference: stampedRef("GMC"), kind: "GENERAL" });
|
|
seedImportContract({
|
|
suffix: "GMB",
|
|
reference: stampedRef("GMB"),
|
|
kind: "GENERAL",
|
|
freight: "BULK",
|
|
});
|
|
});
|
|
|
|
it("operations prepares the corridor train — first window opens (cycle 1)", () => {
|
|
ensureCorridorRoute();
|
|
resetCorridorDay(DEPARTURE);
|
|
createImportSchedule({ departure: DEPARTURE, locoPair: ["LOCO-IMP-19", "LOCO-IMP-20"] });
|
|
withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 45));
|
|
withSchedule(DEPARTURE, (s) => expect(s.booking_cycle_no, "cycle 1").to.eq(1));
|
|
});
|
|
|
|
it("cycle 1: a container and a bulk GENERAL drawdown clear per booking and are reserved", () => {
|
|
bookContainers({
|
|
suffix: "GMC",
|
|
runStamp: stamp,
|
|
isoSeed: 19_000,
|
|
twenty: 12, // 6 wagons
|
|
scheduledDate: BOOKING_DAY,
|
|
});
|
|
clearGeneralBooking("GMC", BOOKING_DAY);
|
|
bookBulk({ suffix: "GMB", tons: 700, scheduledDate: BOOKING_DAY }); // 10 wagons
|
|
clearGeneralBooking("GMB", BOOKING_DAY);
|
|
|
|
withSchedule(DEPARTURE, (s) => {
|
|
closeBookingWindow(s.id);
|
|
completeDocReview(s.id);
|
|
});
|
|
["GMC", "GMB"].forEach((suffix) =>
|
|
pollBookingStatus(suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
|
|
);
|
|
});
|
|
|
|
it("nobody pays — both drawdowns expire and the window reopens", () => {
|
|
["GMC", "GMB"].forEach((suffix) => forceReservationExpiry(suffix));
|
|
withSchedule(DEPARTURE, (s) => {
|
|
endPaymentPhase(s.id);
|
|
// Same-tick guard-loop can chain PRE_WINDOW straight into OPEN when the
|
|
// reopen instant falls inside office hours — assert it left PAYMENT
|
|
// without concluding FULL/DONE, whichever phase it lands on.
|
|
pollDb<ScheduleRow>(
|
|
"window concludes not-full (PRE_WINDOW or fast-forwarded to OPEN)",
|
|
`SELECT window_phase FROM freight.train_schedules WHERE id = $1`,
|
|
[s.id],
|
|
(row) => !!row && row.window_phase !== "PAYMENT" && row.window_phase !== "DONE",
|
|
);
|
|
});
|
|
});
|
|
|
|
it("cycle 2: the SAME contracts issue fresh drawdowns that pay and allocate typed", () => {
|
|
withSchedule(DEPARTURE, (s) => forceWindowOpen(s.id, 45));
|
|
withSchedule(DEPARTURE, (s) => expect(s.booking_cycle_no, "cycle 2").to.eq(2));
|
|
|
|
bookContainers({
|
|
suffix: "GMC",
|
|
runStamp: stamp,
|
|
isoSeed: 19_200,
|
|
twenty: 12,
|
|
scheduledDate: BOOKING_DAY,
|
|
});
|
|
clearGeneralBooking("GMC", BOOKING_DAY);
|
|
bookBulk({ suffix: "GMB", tons: 700, scheduledDate: BOOKING_DAY });
|
|
clearGeneralBooking("GMB", BOOKING_DAY);
|
|
|
|
withSchedule(DEPARTURE, (s) => {
|
|
closeBookingWindow(s.id);
|
|
completeDocReview(s.id);
|
|
});
|
|
["GMC", "GMB"].forEach((suffix) =>
|
|
pollBookingStatus(suffix, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]),
|
|
);
|
|
markPaid("GMC");
|
|
pollAllocations("GMC", 6);
|
|
expectWagonType("GMC", "NW5", 6);
|
|
markPaid("GMB");
|
|
pollAllocations("GMB", 10);
|
|
expectWagonType("GMB", "CW4", 10);
|
|
|
|
// The cycle-1 corpses stay dead; both contracts remain ACTIVE.
|
|
["GMC", "GMB"].forEach((suffix) =>
|
|
withBooking(suffix, (b) => expect(b.status, `${suffix} cycle-2 rides`).to.eq("PAID")),
|
|
);
|
|
});
|
|
});
|
|
|
|
export {};
|