mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 21:50:57 +00:00
126 lines
4.0 KiB
TypeScript
126 lines
4.0 KiB
TypeScript
/**
|
|
* BULK IMPORT — the CW4 train fills from THREE wheat bookings; three more sit
|
|
* in the waiting pool of the same window. The selected trio pays through the
|
|
* gateway and allocates; when the cycle concludes FULL the waiting three
|
|
* expire with the day — they never ride and never pay.
|
|
*
|
|
* 70 T per CW4 wagon, 54-wagon consist:
|
|
* selected: BWA 1 400 T = 20 w, BWB 1 400 T = 20 w, BWC 980 T = 14 w → Σ 54
|
|
* waiting: BW1 / BW2 / BW3 700 T = 10 w each
|
|
*/
|
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
import { closeDb, gateway } from "./client";
|
|
import {
|
|
allocatedWagons,
|
|
bookBulkReady,
|
|
bookingRow,
|
|
closeBookingWindow,
|
|
completeDocReview,
|
|
createSchedule,
|
|
departureAt,
|
|
eatDayStr,
|
|
endPaymentPhase,
|
|
extendPayWindow,
|
|
ensureCorridorRoute,
|
|
forceWindowOpen,
|
|
payViaGateway,
|
|
pollAllocations,
|
|
pollBookingStatus,
|
|
pollWindow,
|
|
releaseUnpaidHolds,
|
|
resetCorridorDay,
|
|
seedTenantContracts,
|
|
setPriority,
|
|
} from "./flows";
|
|
|
|
const DEPARTURE = departureAt(21);
|
|
const BOOKING_DAY = eatDayStr(DEPARTURE);
|
|
const STAMP = String(Date.now());
|
|
|
|
const SELECTED = [
|
|
{ suffix: "BWA", tons: 1400, wagons: 20 },
|
|
{ suffix: "BWB", tons: 1400, wagons: 20 },
|
|
{ suffix: "BWC", tons: 980, wagons: 14 },
|
|
];
|
|
const WAITING = [
|
|
{ suffix: "BW1", tons: 700, wagons: 10 },
|
|
{ suffix: "BW2", tons: 700, wagons: 10 },
|
|
{ suffix: "BW3", tons: 700, wagons: 10 },
|
|
];
|
|
const ALL = [...SELECTED, ...WAITING];
|
|
|
|
describe("bulk import: three bookings fill the train, three wait and expire", () => {
|
|
const booking = new Map<string, string>();
|
|
let scheduleId: string;
|
|
|
|
beforeAll(async () => {
|
|
await gateway.reset();
|
|
await releaseUnpaidHolds();
|
|
await ensureCorridorRoute();
|
|
await resetCorridorDay(DEPARTURE);
|
|
|
|
const contracts = await seedTenantContracts(
|
|
STAMP,
|
|
ALL.map((b) => ({ suffix: b.suffix, freight: "BULK" as const })),
|
|
);
|
|
scheduleId = (
|
|
await createSchedule({
|
|
departure: DEPARTURE,
|
|
kind: "bulk",
|
|
locoPair: ["LOCO-IMP-17", "LOCO-IMP-18"],
|
|
})
|
|
).id;
|
|
await forceWindowOpen(scheduleId, 45);
|
|
|
|
for (const [i, b] of ALL.entries()) {
|
|
const id = await bookBulkReady({
|
|
contractId: contracts.get(b.suffix)!,
|
|
tons: b.tons,
|
|
scheduledDate: BOOKING_DAY,
|
|
});
|
|
booking.set(b.suffix, id);
|
|
// Priority order = the order above: the exact-fill trio picks first.
|
|
await setPriority(id, i + 1);
|
|
}
|
|
}, 1_800_000);
|
|
|
|
afterAll(closeDb);
|
|
|
|
it("the batch selects exactly the three that fill 54 wagons; the rest keep waiting", async () => {
|
|
await closeBookingWindow(scheduleId);
|
|
await completeDocReview(scheduleId);
|
|
|
|
for (const b of SELECTED) {
|
|
await pollBookingStatus(booking.get(b.suffix)!, ["SELECTED_FOR_BATCH", "AWAITING_PAYMENT"]);
|
|
}
|
|
for (const b of WAITING) {
|
|
const row = await bookingRow(booking.get(b.suffix)!);
|
|
expect(row.status, `${b.suffix} still waiting`).toBe("FULLY_EXECUTED");
|
|
expect(row.payment_deadline, `${b.suffix} has no pay deadline`).toBeNull();
|
|
}
|
|
});
|
|
|
|
it("the three selected pay through the gateway and allocate — 54/54", async () => {
|
|
await extendPayWindow(scheduleId, SELECTED.map((b) => booking.get(b.suffix)!));
|
|
for (const b of SELECTED) {
|
|
await payViaGateway(booking.get(b.suffix)!);
|
|
await pollAllocations(booking.get(b.suffix)!, b.wagons);
|
|
}
|
|
expect(await allocatedWagons(scheduleId), "54 wagons allocated").toBe(54);
|
|
});
|
|
|
|
it("the cycle concludes FULL — the three waiting bookings expire with the day", async () => {
|
|
await endPaymentPhase(scheduleId);
|
|
await pollWindow(
|
|
scheduleId,
|
|
(s) => s.booking_window_status === "FULL" && s.window_phase === "DONE",
|
|
"FULL + DONE",
|
|
);
|
|
for (const b of WAITING) await pollBookingStatus(booking.get(b.suffix)!, "EXPIRED", 40);
|
|
for (const b of SELECTED) {
|
|
const row = await bookingRow(booking.get(b.suffix)!);
|
|
expect(row.status, `${b.suffix} stays PAID`).toBe("PAID");
|
|
}
|
|
});
|
|
});
|