mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 12:22:34 +03:00
Adds a shadow release-acceptance report alongside release-green: an inventory/reduce/oracle pipeline under `scripts/quality/release-acceptance/` with a JSON schema, fixtures and a workflow that uploads the report as an artifact. Contained by design, which is why it merges as-is: it runs only on push to `release/v*` and on manual dispatch (never on pull requests), the step is `continue-on-error`, `permissions: contents: read`, `persist-credentials: false`, and it consumes no secrets. Nothing in the product changes; the report is advisory until we decide to promote it. Validated as a combined board first (this PR merged with the 11 siblings of the same batch on the release tip): eslint on every changed file with the suppressions file, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 275 passing / 0 failing focused node:test cases across the 28 test files the batch touches. Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thanks @HouMinXi!
103 lines
3.6 KiB
JavaScript
103 lines
3.6 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import {
|
|
COLLECTORS,
|
|
globToRegExp,
|
|
} from "../../check/check-test-discovery.mjs";
|
|
|
|
const UNIT_CI_GLOBS = new Set([
|
|
"tests/unit/*.test.ts",
|
|
"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,translator,ui,usage}/**/*.test.ts",
|
|
"tests/unit/dashboard/**/*.test.ts",
|
|
"tests/unit/serial/**/*.test.ts",
|
|
"tests/unit/**/*.test.mjs",
|
|
]);
|
|
|
|
const INTEGRATION_GLOBS = new Set([
|
|
"tests/integration/*.test.ts",
|
|
"tests/integration/combo-matrix/*.test.ts",
|
|
]);
|
|
|
|
function inScope(collector, scopeSuites) {
|
|
const suites = new Set(scopeSuites);
|
|
if (suites.has("test:unit:ci") && UNIT_CI_GLOBS.has(collector.glob)) return true;
|
|
if (suites.has("test:integration") && INTEGRATION_GLOBS.has(collector.glob)) return true;
|
|
if (suites.has("test:vitest") && collector.sources?.includes("vitest.mcp.config.ts")) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function walkTestFiles(root = process.cwd()) {
|
|
const out = [];
|
|
function walk(dir) {
|
|
if (!fs.existsSync(dir)) return;
|
|
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
if (e.name === "node_modules" || e.name === ".git") continue;
|
|
const p = path.join(dir, e.name);
|
|
if (e.isDirectory()) walk(p);
|
|
else if (/\.(test|spec)\.(ts|tsx|mjs|js)$/.test(e.name)) {
|
|
out.push(path.relative(root, p).split(path.sep).join("/"));
|
|
}
|
|
}
|
|
}
|
|
walk(path.join(root, "tests"));
|
|
walk(path.join(root, "open-sse"));
|
|
walk(path.join(root, "src"));
|
|
return out;
|
|
}
|
|
|
|
export function canonicalSet(scopeSuites, collectors = COLLECTORS, files) {
|
|
const scoped = collectors.filter((c) => inScope(c, scopeSuites));
|
|
const regexes = scoped.map((c) => globToRegExp(c.glob));
|
|
const discovered = files ?? walkTestFiles();
|
|
return discovered.filter((f) => regexes.some((re) => re.test(f)));
|
|
}
|
|
|
|
export function knownUnexecuted(scopeSuites, collectors = COLLECTORS, baseline, files) {
|
|
const discovered = files ?? walkTestFiles();
|
|
const orphans = baseline?.orphans ?? [];
|
|
const outOfScope = collectors.filter((c) => !inScope(c, scopeSuites));
|
|
const collectorsOut = outOfScope.map((c) => {
|
|
const re = globToRegExp(c.glob);
|
|
const count = discovered.filter((f) => re.test(f)).length;
|
|
return {
|
|
glob: c.glob,
|
|
count,
|
|
reason: "collector runner is not a suite of this scope",
|
|
};
|
|
});
|
|
return {
|
|
orphans: { count: orphans.length, paths: orphans },
|
|
collectors: collectorsOut,
|
|
};
|
|
}
|
|
|
|
export function inventoryErrors(scopeSuites, collectors, baseline, discoveredFiles) {
|
|
const errors = [];
|
|
const full = COLLECTORS;
|
|
const givenGlobs = new Set(collectors.map((c) => c.glob));
|
|
for (const c of full) {
|
|
if (!givenGlobs.has(c.glob)) {
|
|
errors.push({
|
|
code: "collector_omitted",
|
|
glob: c.glob,
|
|
detail: `collector ${c.glob} omitted without known_unexecuted listing`,
|
|
});
|
|
}
|
|
}
|
|
const ku = knownUnexecuted(scopeSuites, collectors, baseline, discoveredFiles);
|
|
const knownGlobs = new Set(ku.collectors.map((c) => c.glob));
|
|
const knownOrphans = new Set(ku.orphans.paths);
|
|
const scoped = collectors.filter((c) => inScope(c, scopeSuites));
|
|
const regexes = scoped.map((c) => globToRegExp(c.glob));
|
|
for (const f of discoveredFiles ?? []) {
|
|
const inCanonical = regexes.some((re) => re.test(f));
|
|
const inKnown = knownOrphans.has(f) || [...knownGlobs].some((g) => globToRegExp(g).test(f));
|
|
if (!inCanonical && !inKnown) {
|
|
errors.push({ code: "unmapped_file", path: f, detail: "discovered file belongs to no set" });
|
|
}
|
|
}
|
|
return errors;
|
|
}
|