Files
OmniRoute/tests/unit/validate-release-green.test.ts
Diego Rodrigues de Sa e Souza fb40d21276 chore(quality): expand check:release-green to the FULL release-PR gate set (#4758)
The release-green pre-flight (Solution C) previously covered only a subset of the
gates that run exclusively on the release PR (PR→main), so reds still accrued
silently on release/** and surfaced in ~40-min layers at release time (v3.8.34:
3 CI rounds — CodeQL sanitization, then the fail-fast Quality Ratchet revealing
openapi then cyclomatic-complexity one push at a time, plus zizmor/integration).

Now check:release-green reproduces the COMPLETE release-PR gate set and reports
EVERY red in one pass (collected, not fail-fast):

- New DRIFT ratchets (report-only, rebaselined at release, never block):
  cyclomatic complexity, dead-code, type-coverage, compression-budget,
  openapi-coverage, workflow-lint (zizmor), codeql-ratchet.
- New HARD gates (real defects): docs-all (fabricated-docs strict + i18n mirror
  sync) and the integration test suite (gated behind !--quick).

The only release-PR gates it still cannot reproduce locally are GitHub-side CodeQL
semantic analysis and SonarQube/SonarCloud (external services).

The nightly-release-green workflow and /green-prs inherit the expanded coverage
automatically (they invoke this script), so cycle drift is now surfaced
continuously and the release PR is green on its first CI run.

Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
2026-06-23 07:35:31 -03:00

99 lines
3.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
// Pure helpers of the release-green validator (Solution C). The orchestration is
// guarded behind a direct-run check, so importing the module here is side-effect-free.
const mod = await import("../../scripts/quality/validate-release-green.mjs");
const {
firstFailureLine,
eslintCounts,
parseEslintJson,
parseCognitiveCount,
isDrift,
computeVerdict,
} = mod;
test("eslintCounts sums errors + warnings across files", () => {
const parsed = [
{ errorCount: 2, warningCount: 5 },
{ errorCount: 0, warningCount: 3 },
{},
];
assert.deepEqual(eslintCounts(parsed), { errors: 2, warnings: 8 });
});
test("parseEslintJson tolerates a leading non-JSON banner", () => {
const out = "npm warn something\n[{\"errorCount\":0,\"warningCount\":1}]";
assert.deepEqual(parseEslintJson(out), [{ errorCount: 0, warningCount: 1 }]);
assert.equal(parseEslintJson("no json here"), null);
});
test("parseCognitiveCount reads the gate's count (en + pt)", () => {
assert.equal(parseCognitiveCount("[cognitive-complexity] 797 function(s) exceed the threshold (15)."), 797);
assert.equal(parseCognitiveCount("[cognitive-complexity] REGRESSÃO — 801 violações > baseline 797"), 801);
assert.equal(parseCognitiveCount("no number"), null);
});
test("isDrift flags only growth past the committed baseline (down-direction ratchets)", () => {
assert.equal(isDrift(3900, 3867), true); // grew → drift
assert.equal(isDrift(3867, 3867), false); // equal → ok
assert.equal(isDrift(3800, 3867), false); // improved → ok
assert.equal(isDrift(10, null), false); // no baseline → never drift
assert.equal(isDrift(null, 10), false); // unparsed → never drift
});
test("firstFailureLine surfaces the meaningful failure, not boilerplate", () => {
const out = [
"> omniroute@3.8.34 typecheck:core",
"src/x.ts(10,5): error TS2322: Type 'string' is not assignable to 'number'.",
"done",
].join("\n");
assert.match(firstFailureLine(out), /error TS2322/);
});
test("computeVerdict: releaseGreen iff zero HARD failures (drift never blocks)", () => {
const onlyDrift = computeVerdict([
{ kind: "hard", ok: true },
{ kind: "drift", ok: false },
]);
assert.equal(onlyDrift.releaseGreen, true);
assert.equal(onlyDrift.drift.length, 1);
const hardFail = computeVerdict([
{ kind: "hard", ok: false },
{ kind: "drift", ok: false },
]);
assert.equal(hardFail.releaseGreen, false);
assert.equal(hardFail.hardFailures.length, 1);
const allGreen = computeVerdict([
{ kind: "hard", ok: true },
{ kind: "drift", ok: true },
]);
assert.equal(allGreen.releaseGreen, true);
});
test("computeVerdict: full-coverage classification — ratchets are drift, defects are hard", () => {
// Mirrors the expanded check set: the ratchets that historically surfaced in
// layers on the release PR (complexity/openapi/zizmor/…) are DRIFT → never block;
// the new real-defect gates (docs-all, integration) are HARD → block.
const results = [
{ id: "complexity", kind: "drift", ok: false },
{ id: "openapi-coverage", kind: "drift", ok: false },
{ id: "workflow-lint", kind: "drift", ok: false },
{ id: "dead-code", kind: "drift", ok: true },
{ id: "codeql-ratchet", kind: "drift", ok: true },
{ id: "docs-all", kind: "hard", ok: true },
{ id: "integration", kind: "hard", ok: true },
];
const v = computeVerdict(results);
// Three ratchets drifted but NONE block — release is still green, all reported.
assert.equal(v.releaseGreen, true);
assert.equal(v.drift.length, 3);
// A hard gate (integration assertion regression) flips it red.
const withHardFail = computeVerdict([...results, { id: "integration", kind: "hard", ok: false }]);
assert.equal(withHardFail.releaseGreen, false);
assert.equal(withHardFail.hardFailures.length, 1);
});