mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
81 lines
3.0 KiB
TypeScript
81 lines
3.0 KiB
TypeScript
import {
|
|
DEFAULT_PAYMENT_DRAIN_MINUTES,
|
|
paymentDrainEndsAtIso,
|
|
paymentDrainMs,
|
|
payWindowLapsed,
|
|
} from "./booking-batch.constants";
|
|
|
|
/**
|
|
* The drain tail is what keeps a pay window's LAST payment from being thrown
|
|
* away: settlement is asynchronous (provider confirm → payment-api → outbox
|
|
* relay), so a payment made in the window's final seconds lands after
|
|
* `paymentDeadline`. Nothing may expire, no wagons may be resold and no window
|
|
* cycle may conclude until the tail has passed.
|
|
*/
|
|
describe("payWindowLapsed — pay-window drain tail", () => {
|
|
const deadline = new Date("2026-08-02T22:04:41Z");
|
|
const at = (offsetMs: number) => deadline.getTime() + offsetMs;
|
|
const MIN = 60_000;
|
|
|
|
afterEach(() => {
|
|
delete process.env.FREIGHT_PAYMENT_DRAIN_MINUTES;
|
|
});
|
|
|
|
it("is not lapsed before the deadline", () => {
|
|
expect(payWindowLapsed(deadline, at(-1 * MIN))).toBe(false);
|
|
});
|
|
|
|
it("is not lapsed inside the drain tail", () => {
|
|
// The reproduced finding: settled ~7 minutes late. With the default 5-minute
|
|
// tail the booking is still live at 4 minutes.
|
|
expect(payWindowLapsed(deadline, at(4 * MIN))).toBe(false);
|
|
expect(payWindowLapsed(deadline, at(DEFAULT_PAYMENT_DRAIN_MINUTES * MIN - 1))).toBe(
|
|
false,
|
|
);
|
|
});
|
|
|
|
it("is lapsed once the tail passes", () => {
|
|
expect(payWindowLapsed(deadline, at(DEFAULT_PAYMENT_DRAIN_MINUTES * MIN))).toBe(
|
|
true,
|
|
);
|
|
expect(payWindowLapsed(deadline, at(7 * MIN))).toBe(true);
|
|
});
|
|
|
|
it("never lapses without a deadline — callers decide what unknown means", () => {
|
|
expect(payWindowLapsed(null, at(60 * MIN))).toBe(false);
|
|
expect(payWindowLapsed(undefined, at(60 * MIN))).toBe(false);
|
|
});
|
|
|
|
it("honours FREIGHT_PAYMENT_DRAIN_MINUTES", () => {
|
|
process.env.FREIGHT_PAYMENT_DRAIN_MINUTES = "20";
|
|
expect(paymentDrainMs()).toBe(20 * MIN);
|
|
expect(payWindowLapsed(deadline, at(10 * MIN))).toBe(false);
|
|
expect(payWindowLapsed(deadline, at(20 * MIN))).toBe(true);
|
|
});
|
|
|
|
it("allows an explicit zero drain (old deadline-is-the-line behaviour)", () => {
|
|
process.env.FREIGHT_PAYMENT_DRAIN_MINUTES = "0";
|
|
expect(paymentDrainMs()).toBe(0);
|
|
expect(payWindowLapsed(deadline, at(0))).toBe(true);
|
|
});
|
|
|
|
it("falls back to the default on garbage or negative values", () => {
|
|
for (const bad of ["", "abc", "-3"]) {
|
|
process.env.FREIGHT_PAYMENT_DRAIN_MINUTES = bad;
|
|
expect(paymentDrainMs()).toBe(DEFAULT_PAYMENT_DRAIN_MINUTES * MIN);
|
|
}
|
|
});
|
|
|
|
it("paymentDrainEndsAtIso reports deadline + drain, null/garbage-safe", () => {
|
|
expect(paymentDrainEndsAtIso(deadline)).toBe(
|
|
new Date(at(DEFAULT_PAYMENT_DRAIN_MINUTES * MIN)).toISOString(),
|
|
);
|
|
expect(paymentDrainEndsAtIso(deadline.toISOString())).toBe(
|
|
new Date(at(DEFAULT_PAYMENT_DRAIN_MINUTES * MIN)).toISOString(),
|
|
);
|
|
expect(paymentDrainEndsAtIso(null)).toBeNull();
|
|
expect(paymentDrainEndsAtIso(undefined)).toBeNull();
|
|
expect(paymentDrainEndsAtIso("not-a-date")).toBeNull();
|
|
});
|
|
});
|