mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
fix(gate): reject invalid exclusion expiry dates (#199)
Signed-off-by: Rudy Celekli <[email protected]>
This commit is contained in:
1 parent
e1139e2276
commit
0ab94672de
2 files changed
+24
-1
No files matched your search
@@ -21,6 +21,12 @@ export const CUT_CEILING_ISO = "2026-09-30";
|
||||
// Date-only lexicographic compare: for YYYY-MM-DD, string order IS chronological order.
|
||||
const day = (d) => String(d).slice(0, 10);
|
||||
|
||||
function isCalendarDate(value) {
|
||||
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
||||
const parsed = new Date(`${value}T00:00:00.000Z`);
|
||||
return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rail 3 + Rail 4 static validation: every resident MUST carry the full schema, and its expiry MUST
|
||||
* NOT outlive the cut ceiling. Returns { valid, errors } — an invalid ledger is a loud gate failure,
|
||||
@@ -36,7 +42,9 @@ export function validateLedger(ledger = [], { cutCeiling = CUT_CEILING_ISO } = {
|
||||
errors.push(`${tag}: missing required field "${field}"`);
|
||||
}
|
||||
}
|
||||
if (entry && entry.expiry && day(entry.expiry) > day(cutCeiling)) {
|
||||
if (!isCalendarDate(entry?.expiry)) {
|
||||
errors.push(`${tag}: expiry must be a valid YYYY-MM-DD calendar date`);
|
||||
} else if (day(entry.expiry) > day(cutCeiling)) {
|
||||
errors.push(`${tag}: expiry ${day(entry.expiry)} exceeds the 0.5.2 cut ceiling ${day(cutCeiling)}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -96,3 +96,18 @@ test("EMPTY SEED — zero residents + any failure → FAIL (the gate is strict b
|
||||
const r = resolveGateWithLedger({ failures: ["anything"], ledger: [], now: "2025-08-10", cutCeiling: CEIL });
|
||||
assert.equal(r.gate, "fail");
|
||||
});
|
||||
|
||||
// Invalid exclusion data must never turn a failing suite into a passing gate.
|
||||
test("rail 3 — expiry must be a calendar date, not a coerced or truncated value", () => {
|
||||
for (const expiry of [false, 0, true, 123, {}, [], "invalid", "2025-00-10", "2025-02-29", "2025-04-31", "2025-08-20junk", "2025-08-20T00:00:00Z"]) {
|
||||
const r = resolveGateWithLedger({ failures: ["flaky-suite"], ledger: [ok({ expiry })], now: "2025-08-10", cutCeiling: CEIL });
|
||||
assert.equal(r.gate, "fail", `invalid expiry ${JSON.stringify(expiry)} must refuse`);
|
||||
assert.equal(r.validity.valid, false);
|
||||
assert.ok(r.validity.errors.some((e) => e.includes("expiry")));
|
||||
}
|
||||
});
|
||||
|
||||
test("rail 3 — leap days validate only in leap years", () => {
|
||||
assert.equal(validateLedger([ok({ expiry: "2024-02-29" })], { cutCeiling: CEIL }).valid, true);
|
||||
assert.equal(validateLedger([ok({ expiry: "2025-02-29" })], { cutCeiling: CEIL }).valid, false);
|
||||
});
|
||||
Reference in new issue
Block a user