Files
OmniRoute/tests/unit/openapi-security-tiers-gate.test.ts
Diego Rodrigues de Sa e Souza ec4f951e39 test(ci): pin the openapi-security-tiers two-arm contract with an executing gate test (#12581) (#12652)
Merged as a reduced diff, and worth recording why.

The two-arm `ALWAYS_PROTECTED` read this PR proposed had already landed in #12605 while this branch was open — the tip carries `coveredByAlwaysProtected()` with both arms and the new error wording. I ran the gate on the current tip to be sure: `PASS — all security tier annotations match routeGuard.ts`. Merging the whole branch would have reintroduced the same logic under a different comment.

What was genuinely missing, and is what merged:

- **`tests/unit/openapi-security-tiers-gate.test.ts`** — executes the real gate and asserts exit 0 with no "NOT covered" line. #12605 fixed the defect but left no guard, so the LOCAL_ONLY-arm bug (#12350) could reappear on the ALWAYS_PROTECTED arm exactly as it did the first time. 1/1 green.
- **The parse guard** — `ALWAYS_PROTECTED_PATTERNS.length === 0` now fails the constant-parse check with its own count in the message. Without it, a regex array that stops parsing degrades into "every pattern-covered route is an annotation mismatch" instead of saying so.

A note for the record: my first read of this PR was wrong. I ran the gate in the main checkout, which was 11 commits behind `origin/release/v3.8.51`, saw the pre-#12605 failure, and classified this as fixing a live red. It was not — the checkout was stale. Corrected before anything was merged.
2026-09-05 03:14:46 -03:00

39 lines
1.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
const GATE = join(ROOT, "scripts", "check", "check-openapi-security-tiers.mjs");
function runGate(): { code: number; out: string } {
try {
const out = execFileSync(process.execPath, [GATE], {
cwd: ROOT,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
return { code: 0, out };
} catch (err) {
const e = err as { status?: number; stdout?: string; stderr?: string };
return { code: e.status ?? 1, out: `${e.stdout ?? ""}${e.stderr ?? ""}` };
}
}
// routeGuard protects a path when EITHER list matches — `isAlwaysProtectedPath`
// ORs ALWAYS_PROTECTED_API_PATHS with ALWAYS_PROTECTED_API_PATTERNS. The gate
// used to read only the prefix array, so every regex-covered route was reported
// as an annotation mismatch: the four `{claude,codex}-auth/{export,apply-local}`
// routes turned release/v3.8.51 red while being correctly protected at runtime.
// Same defect class the LOCAL_ONLY arm already had (#12350).
test("openapi-security-tiers accepts routes covered only by ALWAYS_PROTECTED_API_PATTERNS", () => {
const { code, out } = runGate();
assert.ok(
!/has x-always-protected but is NOT/.test(out),
`gate reported an always-protected route as uncovered:\n${out}`
);
assert.equal(code, 0, `gate must pass on a clean tree, got exit ${code}:\n${out}`);
});