mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-29 02:22:10 +03:00
test(config): fail seven days before a dated config pack lapses (#11891)
config/alibaba-free-tier-allowlist.json carried "validUntil": "2026-08-27". On the 28th the loader started rejecting it — correctly, that is the design — and a test that asserted "the shipped pack loads" turned every PR and main red with no commit involved (#11866). A time bomb: the one class of defect a diff review can never catch, because there is no diff. scripts/check/lib/configExpiry.mjs walks config/**/*.json for validUntil / validTo / expiresAt / expiry / expires (and snake_case forms), parses the dates, and classifies each as expired / expiring (< 7 days) / ok / unparseable. The repo-wide test fails on expired or expiring packs unless the file is in a small allowlist keyed to the issue that owns the renewal — and fails the OTHER way when an allowlisted pack is no longer expiring, so entries cannot go stale. A positive anchor requires at least one dated pack to be found, so a renamed key cannot silently turn the suite into a no-op. The Alibaba pack is allowlisted against #11866: whether the curated free-tier list still matches reality is an operator data decision, not a test fix. Removing that entry makes the suite fail as intended (verified).
This commit is contained in:
committed by
GitHub
parent
e4683cd22d
commit
09de69edc7
@@ -0,0 +1,4 @@
|
||||
- Added a unit test that fails seven days before any dated pack under `config/`
|
||||
(`validUntil` and sibling keys) lapses, naming the file and key. The Alibaba
|
||||
free-tier pack expired on 2026-08-27 and turned every PR red the next morning
|
||||
with no commit involved; renewal now happens on someone's terms, not the clock's.
|
||||
90
scripts/check/lib/configExpiry.mjs
Normal file
90
scripts/check/lib/configExpiry.mjs
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* scripts/check/lib/configExpiry.mjs
|
||||
*
|
||||
* Finds dated validity fields in JSON config packs so a test can fail BEFORE
|
||||
* they lapse. Origin: config/alibaba-free-tier-allowlist.json carried
|
||||
* `"validUntil": "2026-08-27"`; on 2026-08-28 the loader started (correctly)
|
||||
* rejecting the pack and a test that asserted "the shipped pack loads" turned
|
||||
* every PR and main red with no commit involved (#11866). A time bomb, not a
|
||||
* regression — and the only kind of defect a diff review can never catch.
|
||||
*
|
||||
* Pure helpers; the repo-wide assertion lives in
|
||||
* tests/unit/config-expiry-time-bomb.test.ts.
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const EXPIRY_KEY =
|
||||
/^(validUntil|valid_until|validTo|valid_to|expiresAt|expires_at|expiry|expires)$/;
|
||||
const DAY_MS = 86_400_000;
|
||||
|
||||
/**
|
||||
* Walks a parsed JSON value and returns every string-valued expiry field.
|
||||
* @returns {{ file: string, keyPath: string, raw: string, expiresAt: number|null }[]}
|
||||
*/
|
||||
export function collectExpiryFields(value, file, keyPath = []) {
|
||||
const out = [];
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((v, i) => out.push(...collectExpiryFields(v, file, [...keyPath, String(i)])));
|
||||
return out;
|
||||
}
|
||||
if (!value || typeof value !== "object") return out;
|
||||
for (const [key, v] of Object.entries(value)) {
|
||||
const kp = [...keyPath, key];
|
||||
if (EXPIRY_KEY.test(key) && typeof v === "string") {
|
||||
const ms = Date.parse(v);
|
||||
out.push({ file, keyPath: kp.join("."), raw: v, expiresAt: Number.isFinite(ms) ? ms : null });
|
||||
} else if (v && typeof v === "object") {
|
||||
out.push(...collectExpiryFields(v, file, kp));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @returns {"expired"|"expiring"|"ok"|"unparseable"} */
|
||||
export function classifyExpiry(field, nowMs, warnDays = 7) {
|
||||
if (field.expiresAt === null) return "unparseable";
|
||||
if (field.expiresAt < nowMs) return "expired";
|
||||
if (field.expiresAt < nowMs + warnDays * DAY_MS) return "expiring";
|
||||
return "ok";
|
||||
}
|
||||
|
||||
/** All *.json under dir, recursively, skipping node_modules. Sorted for stable output. */
|
||||
export function walkJsonFiles(dir) {
|
||||
const out = [];
|
||||
const stack = [dir];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(current, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
for (const e of entries) {
|
||||
const full = path.join(current, e.name);
|
||||
if (e.isDirectory()) {
|
||||
if (e.name !== "node_modules") stack.push(full);
|
||||
} else if (e.isFile() && e.name.endsWith(".json")) {
|
||||
out.push(full);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans every JSON file under `dir`; `file` in the result is relative to `dir`
|
||||
* with forward slashes, so allowlists can key on it portably.
|
||||
*/
|
||||
export function scanConfigExpiry(dir) {
|
||||
return walkJsonFiles(dir).flatMap((f) => {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(fs.readFileSync(f, "utf8"));
|
||||
} catch {
|
||||
return []; // not this scanner's job to validate JSON
|
||||
}
|
||||
return collectExpiryFields(parsed, path.relative(dir, f).split(path.sep).join("/"));
|
||||
});
|
||||
}
|
||||
135
tests/unit/config-expiry-time-bomb.test.ts
Normal file
135
tests/unit/config-expiry-time-bomb.test.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import {
|
||||
classifyExpiry,
|
||||
collectExpiryFields,
|
||||
scanConfigExpiry,
|
||||
} from "../../scripts/check/lib/configExpiry.mjs";
|
||||
|
||||
/**
|
||||
* Time bombs: config packs with a `validUntil` (or sibling key) that lapse with
|
||||
* no commit involved. The Alibaba free-tier pack expired on 2026-08-27 and from
|
||||
* the 28th every PR and main carried a red Unit Tests shard (#11866). Nothing a
|
||||
* diff review could have caught.
|
||||
*
|
||||
* This suite fails SEVEN DAYS BEFORE any pack under config/ lapses, naming the
|
||||
* file and key, so renewal happens on someone's terms instead of the clock's.
|
||||
*/
|
||||
const ROOT = join(import.meta.dirname, "../..");
|
||||
const CONFIG_DIR = join(ROOT, "config");
|
||||
const DAY = 86_400_000;
|
||||
const WARN_DAYS = 7;
|
||||
|
||||
/**
|
||||
* Packs known to be expired/expiring, each pinned to the issue that owns the
|
||||
* renewal decision. An entry whose pack is no longer expiring FAILS below as a
|
||||
* stale allowlist entry — remove it when the pack is renewed.
|
||||
*/
|
||||
const ALLOWLIST: Record<string, string> = {
|
||||
"alibaba-free-tier-allowlist.json":
|
||||
"#11866 — validUntil 2026-08-27 has passed; the loader already falls back to the embedded list, and renewing the curated free-tier pack is an operator data decision, not a test fix",
|
||||
};
|
||||
|
||||
const NOW = Date.UTC(2026, 7, 28); // 2026-08-28, fixed: this suite must not itself depend on the clock
|
||||
const day = (offset: number) => new Date(NOW + offset * DAY).toISOString().slice(0, 10);
|
||||
|
||||
test("collectExpiryFields: finds nested and array-nested expiry keys, ignores non-string values", () => {
|
||||
const fields = collectExpiryFields(
|
||||
{
|
||||
validUntil: day(3),
|
||||
nested: { expiresAt: day(30), other: "x" },
|
||||
list: [{ expiry: day(-1) }, { expires: 12345 }],
|
||||
expires_at: "not a date",
|
||||
},
|
||||
"pack.json"
|
||||
);
|
||||
assert.deepEqual(
|
||||
fields.map((f) => [f.keyPath, f.expiresAt === null ? null : "date"]),
|
||||
[
|
||||
["validUntil", "date"],
|
||||
["nested.expiresAt", "date"],
|
||||
["list.0.expiry", "date"],
|
||||
["expires_at", null],
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
test("classifyExpiry: expired / expiring inside the warning window / ok / unparseable", () => {
|
||||
const f = (raw: string) => ({
|
||||
file: "p",
|
||||
keyPath: "validUntil",
|
||||
raw,
|
||||
expiresAt: Number.isFinite(Date.parse(raw)) ? Date.parse(raw) : null,
|
||||
});
|
||||
assert.equal(classifyExpiry(f(day(-1)), NOW, WARN_DAYS), "expired");
|
||||
assert.equal(
|
||||
classifyExpiry(f(day(0)), NOW, WARN_DAYS),
|
||||
"expiring",
|
||||
"lapsing today is already too late to be 'ok'"
|
||||
);
|
||||
assert.equal(classifyExpiry(f(day(6)), NOW, WARN_DAYS), "expiring");
|
||||
assert.equal(classifyExpiry(f(day(8)), NOW, WARN_DAYS), "ok");
|
||||
assert.equal(classifyExpiry(f("never"), NOW, WARN_DAYS), "unparseable");
|
||||
});
|
||||
|
||||
test("scanConfigExpiry: walks a config tree, skips node_modules and invalid JSON, keys files portably", () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "cfg-expiry-"));
|
||||
try {
|
||||
mkdirSync(join(dir, "sub"), { recursive: true });
|
||||
mkdirSync(join(dir, "node_modules", "dep"), { recursive: true });
|
||||
writeFileSync(join(dir, "a.json"), JSON.stringify({ validUntil: day(3) }));
|
||||
writeFileSync(join(dir, "sub", "b.json"), JSON.stringify({ deep: { expiresAt: day(40) } }));
|
||||
writeFileSync(
|
||||
join(dir, "node_modules", "dep", "c.json"),
|
||||
JSON.stringify({ validUntil: day(-5) })
|
||||
);
|
||||
writeFileSync(join(dir, "broken.json"), "{ not json");
|
||||
writeFileSync(join(dir, "notes.txt"), JSON.stringify({ validUntil: day(-5) }));
|
||||
const found = scanConfigExpiry(dir).map((f) => `${f.file}:${f.keyPath}`);
|
||||
assert.deepEqual(found, ["a.json:validUntil", "sub/b.json:deep.expiresAt"]);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test(`repo: no pack under config/ lapses within ${WARN_DAYS} days unless its renewal is tracked`, (t) => {
|
||||
const fields = scanConfigExpiry(CONFIG_DIR);
|
||||
// Positive anchor: the scanner must be seeing SOMETHING, or a renamed key
|
||||
// would silently turn this whole suite into a no-op.
|
||||
assert.ok(
|
||||
fields.length >= 1,
|
||||
"expected at least one dated pack under config/ (the Alibaba allowlist) — if the key was renamed, extend EXPIRY_KEY"
|
||||
);
|
||||
|
||||
const failures: string[] = [];
|
||||
const seenAllowlisted = new Set<string>();
|
||||
for (const f of fields) {
|
||||
const status = classifyExpiry(f, Date.now(), WARN_DAYS);
|
||||
const tracked = ALLOWLIST[f.file];
|
||||
if (status === "unparseable") {
|
||||
t.diagnostic(`${f.file} ${f.keyPath}="${f.raw}" is not a date — not monitored`);
|
||||
continue;
|
||||
}
|
||||
if (status === "ok") continue;
|
||||
if (tracked) {
|
||||
seenAllowlisted.add(f.file);
|
||||
t.diagnostic(`${f.file} ${f.keyPath}=${f.raw} is ${status} — tracked: ${tracked}`);
|
||||
continue;
|
||||
}
|
||||
failures.push(
|
||||
`${f.file} → ${f.keyPath}=${f.raw} is ${status}: renew the pack (or track it in ALLOWLIST with its issue)`
|
||||
);
|
||||
}
|
||||
for (const file of Object.keys(ALLOWLIST)) {
|
||||
if (!seenAllowlisted.has(file)) {
|
||||
failures.push(
|
||||
`stale ALLOWLIST entry: ${file} is no longer expired/expiring — remove it (${ALLOWLIST[file]})`
|
||||
);
|
||||
}
|
||||
}
|
||||
assert.deepEqual(failures, [], failures.join("\n"));
|
||||
});
|
||||
Reference in New Issue
Block a user