mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 13:22:11 +03:00
* fix(cli): stabilize setup-claude.test.ts flake — inject dry-run log sink (#5959) Root cause (isolated empirically, 5/10 fail on the pristine base): the dry-run path of syncClaudeProfilesFromModels console.log's a multi-byte box-drawing heading ("── [dry-run] … ──"). Under the node:test runner that write lands on the test child's stdout and corrupts the runner's V8-serialized event stream ~50% of the time ("Unable to deserialize cloned data due to invalid or unsupported version"), killing the file at the first logging test. ASCII-only logging never reproduced it (0/20); the unicode heading alone reproduced it (10/20). Fix: syncClaudeProfilesFromModels accepts an injectable log sink (opts.log, CLI default unchanged: console.log). The dry-run test injects a collector — keeping unicode off the child's stdout — and gains assertions on the dry-run report (path + parsed settings content), which FAIL on the old code (log ignored) and PASS on the new one. Validation: 0/30 failures post-fix vs 5/10 pre-fix on the same tree. Baselines: complexity 2003->2006 and cognitive 859->860 are inherited post-3a3d618fe release drift — measured identical on the pristine base with and without this change (notes added in both files). * test(ci): collect the orphaned tests/unit/executors/ directory (base-red unblock) #5800 created tests/unit/executors/ outside every unit-runner brace glob, so its 2 test files (firecrawl-fetch, xai-executor) never ran anywhere and check:test-discovery flags them as NEW orphans on the pristine base, red-flagging every PR into release/v3.8.44. Added 'executors' to the runner globs in package.json (7 scripts), ci.yml unit shards, quality.yml TIA glob, build-test-impact-map.mjs, and the test-discovery gate's COLLECTORS (the gate enforces those stay in sync). Both files pass when actually collected (10/10); cli+executors under suite flags: 99/99. * chore(quality): complexity baseline 2006 -> 2007 (CI-observed value) The GitHub fast-gates runner measures 2007 where local measures 2006 — the same local-vs-CI off-by-one documented in the 2026-06-26 note. Pin the CI-observed value so the gate is deterministic where it runs. * fix(i18n): add the 6 missing en.json keys flagged by settings-i18n-keys (base-red unblock) providers.iconUrlLabel/iconUrlHint (referenced by AddCompatibleProviderModal and EditCompatibleNodeModal) and settings.authz.cors.wildcard.title/desc (the #5602 CORS_ALLOW_ALL banner in AuthzSection) shipped without their en.json messages — 'direct translation calls have English messages' fails on the pristine release tip, red-flagging every PR. git log -S proves the keys never existed (not a merge-eat). Scanner test: 10/10 green.
80 lines
2.9 KiB
JavaScript
80 lines
2.9 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { globSync } from "tinyglobby";
|
|
|
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const SRC_ROOTS = ["src", "open-sse"];
|
|
const IMPORT_RE =
|
|
/(?:import|export)[^'"]*from\s*['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]\s*\)|import\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
const EXTS = [".ts", ".tsx", ".mts", ".js", ".mjs"];
|
|
|
|
function resolveImport(spec, fromFile) {
|
|
let base;
|
|
if (spec.startsWith("@/")) base = path.join(ROOT, "src", spec.slice(2));
|
|
else if (spec.startsWith("@omniroute/open-sse"))
|
|
base = path.join(ROOT, "open-sse", spec.replace(/^@omniroute\/open-sse\/?/, ""));
|
|
else if (spec.startsWith(".")) base = path.resolve(path.dirname(fromFile), spec);
|
|
else return null;
|
|
for (const e of EXTS) {
|
|
if (fs.existsSync(base + e)) return base + e;
|
|
}
|
|
for (const e of EXTS) {
|
|
const idx = path.join(base, "index" + e);
|
|
if (fs.existsSync(idx)) return idx;
|
|
}
|
|
return fs.existsSync(base) && fs.statSync(base).isFile() ? base : null;
|
|
}
|
|
|
|
function sourceDepsOf(entry) {
|
|
const seen = new Set();
|
|
const stack = [entry];
|
|
const sources = new Set();
|
|
while (stack.length) {
|
|
const f = stack.pop();
|
|
if (seen.has(f)) continue;
|
|
seen.add(f);
|
|
let code;
|
|
try {
|
|
code = fs.readFileSync(f, "utf8");
|
|
} catch {
|
|
continue;
|
|
}
|
|
for (const m of code.matchAll(IMPORT_RE)) {
|
|
const spec = m[1] || m[2] || m[3];
|
|
if (!spec) continue;
|
|
const r = resolveImport(spec, f);
|
|
if (!r) continue;
|
|
const rel = path.relative(ROOT, r);
|
|
if (SRC_ROOTS.some((s) => rel.startsWith(s + path.sep))) sources.add(rel);
|
|
stack.push(r);
|
|
}
|
|
}
|
|
return sources;
|
|
}
|
|
|
|
// Mirror EXACTLY the `npm run test:unit` glob — the curated set of node:test files.
|
|
// The TIA step runs the selected subset via `node --test`, so it must NOT include
|
|
// vitest files (`.test.tsx`, `open-sse/**/__tests__`, `tests/unit/autoCombo`), nor
|
|
// e2e/integration tests, which can't run under node:test (they 99-false-failed before).
|
|
const testFiles = globSync(
|
|
[
|
|
"tests/unit/*.test.ts",
|
|
"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts",
|
|
],
|
|
{ cwd: ROOT, absolute: true }
|
|
);
|
|
const map = {};
|
|
for (const tf of testFiles) {
|
|
const relTest = path.relative(ROOT, tf);
|
|
for (const src of sourceDepsOf(tf)) {
|
|
(map[src] ||= []).push(relTest);
|
|
}
|
|
}
|
|
for (const k of Object.keys(map)) map[k].sort();
|
|
const out = path.join(ROOT, "config/quality/test-impact-map.json");
|
|
fs.writeFileSync(out, JSON.stringify({ generatedFrom: "import-graph", sources: map }, null, 2) + "\n");
|
|
console.log(
|
|
`test-impact-map: ${Object.keys(map).length} source files mapped from ${testFiles.length} test files`
|
|
);
|