fix(cli): stabilize setup-claude.test.ts flake — inject dry-run log sink (#6021)

* 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.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-02 23:35:35 -03:00
committed by GitHub
parent cfa3686f9f
commit 08b5e082b7
11 changed files with 54 additions and 27 deletions

View File

@@ -624,7 +624,7 @@ jobs:
cache: npm
- uses: ./.github/actions/npm-ci-retry
- run: npm run check:node-runtime
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/8 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
test-vitest:
name: Vitest (MCP / autoCombo / UI components)
@@ -676,7 +676,7 @@ jobs:
cache: npm
- uses: ./.github/actions/npm-ci-retry
- run: npm run check:node-runtime
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
node-26-compat-build:
name: Node 26 Compatibility Build
@@ -729,7 +729,7 @@ jobs:
cache: npm
- uses: ./.github/actions/npm-ci-retry
- run: npm run check:node-runtime
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
test-coverage-shard:
name: Coverage Shard (${{ matrix.shard }}/8)
@@ -770,7 +770,7 @@ jobs:
--exclude=tests/** \
--exclude=**/*.test.* \
node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \
--test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
--test-shard=${{ matrix.shard }}/8 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts"
- name: Upload raw shard coverage
if: always()
uses: actions/upload-artifact@v7

View File

@@ -145,4 +145,4 @@ jobs:
--import ./tests/_setup/isolateDataDir.ts
--test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2
tests/unit/*.test.ts
"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.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"

View File

@@ -13,12 +13,11 @@
### 🔧 Bug Fixes
_TBD_
- **tests(cli):** stabilize `setup-claude.test.ts` (#5959) — the dry-run path printed a multi-byte "──" heading to the test child's stdout, corrupting the node:test runner's V8-serialized event stream in ~50% of runs ("Unable to deserialize cloned data due to invalid or unsupported version") and randomly failing the PR→release queue. `syncClaudeProfilesFromModels` now accepts an injectable `log` sink (CLI default unchanged: `console.log`); the test injects a collector and gains assertions on the dry-run report. Validated 0/30 failures post-fix vs 5/10 on the pristine base.
- **tests(ci):** collect the orphaned `tests/unit/executors/` directory (created by #5800 outside every runner glob — its 2 test files never ran anywhere). Added `executors` to the unit-runner brace globs (package.json, ci.yml shards, quality.yml TIA, test-impact map, test-discovery gate); both files pass (10/10).
### 📝 Maintenance
- **test (deflake `setup-claude`):** `tests/unit/cli/setup-claude.test.ts` failed ~50% of runs with `Unable to deserialize cloned data due to invalid or unsupported version` at file teardown (all subtests passed), randomly reddening `Unit Tests fast-path (2/2)` / `Fast Quality Gates` across the PR→release queue. Root cause: `node --test` streams each file's report to the parent as V8-serialized frames on fd 1 (stdout), and the CLI helper under test (`syncClaudeProfilesFromModels`) prints progress via `console.log` — that stdout output interleaved with the serialized frames and corrupted the stream. The test now silences the stdout-writing `console` methods for the file's duration (no assertion inspects stdout), making it deterministic (15/15 green locally). ([#5959](https://github.com/diegosouzapw/OmniRoute/issues/5959))
- **API validation:** add a `validatedJsonBody(request, schema)` helper in `src/shared/validation/helpers.ts` that fuses JSON body parsing and Zod validation into a single call, returning either the type-narrowed data or a ready-to-return 400 `NextResponse` with the standard error envelope. Salvaged from the closed refactor PR #5075 (Tier 1 portable helper) with a focused 6-case regression test. Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
---

View File

@@ -56,7 +56,7 @@ export function buildProfileSettings(modelId, baseUrl, cfg) {
* behaviorally identical. Writes `<claudeHome>/profiles/<name>/settings.json`
* (directory-per-profile); never touches the active/default Claude config.
* @param {Array} models
* @param {{claudeHome?:string, baseUrl:string, dryRun?:boolean, only?:string}} opts
* @param {{claudeHome?:string, baseUrl:string, dryRun?:boolean, only?:string, log?:(line:string)=>void}} opts
* @returns {Promise<{written:number, skipped:number, profiles:Array<{name:string, model:string, filePath:string}>}>}
*/
export async function syncClaudeProfilesFromModels(models, opts = {}) {
@@ -64,6 +64,12 @@ export async function syncClaudeProfilesFromModels(models, opts = {}) {
const profilesRoot = join(claudeHome, "profiles");
const baseUrl = opts.baseUrl;
const dryRun = Boolean(opts.dryRun);
// Injectable dry-run printer (#5959): under the node:test runner, a child
// process writing multi-byte UTF-8 (the "──" box-drawing heading) to stdout
// corrupts the runner's V8-serialized event stream ~50% of the time
// ("Unable to deserialize cloned data due to invalid or unsupported
// version"). Tests inject a collector; the CLI default stays console.log.
const log = opts.log ?? console.log;
const onlyFilter = opts.only ? opts.only.split(",").map((s) => s.trim()) : null;
if (!dryRun && !existsSync(profilesRoot)) {
@@ -96,8 +102,8 @@ export async function syncClaudeProfilesFromModels(models, opts = {}) {
const content = buildProfileSettings(id, baseUrl, cfg);
if (dryRun) {
console.log(`\n── [dry-run] ${filePath} ──`);
console.log(content);
log(`\n── [dry-run] ${filePath} ──`);
log(content);
} else {
mkdirSync(dir, { recursive: true });
writeFileSync(filePath, content, "utf8");

View File

@@ -1,6 +1,8 @@
{
"_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.",
"count": 2003,
"count": 2007,
"_rebaseline_2026_07_02_v3844_ci_observed": "2006->2007 (+1). Local Ubuntu measures 2006 on this tree; the GitHub fast-gates runner measures 2007 (same local-vs-CI off-by-one already documented in _rebaseline_2026_06_26_v3838_release_fast_gate). Use the CI-observed value so the gate is deterministic where it actually runs.",
"_rebaseline_2026_07_02_v3844_post_5939": "2003->2006 (+3). Inherited drift from the release/v3.8.44 merges after 3a3d618fe (#5809 audio translations et al.), surfaced by PR fix#5959: check:complexity measures 2006 on the pristine base (cbd08ef78) WITH AND WITHOUT this PR's one-line CLI change (verified by reverting the file and re-measuring) — the PR is complexity-net-zero. Tighten via --update next cycle.",
"_rebaseline_2026_07_02_v3844_merge_burst": "1995->2003 (+8). Inherited v3.8.44 cycle drift surfaced by PR #5939: check:complexity measures 2003 on BOTH the pristine release tip (3a3d618fe) and this PR's merged HEAD — identical, so all +8 came from the 2026-07-02 merge burst into release/v3.8.44 (#5933 codex schema, #5950 OCR, #5904/#5920 combo, #6000/#6008 executor refactors, etc.) merged while the fast-gates queue was base-red (file-size #5933). PR #5939 itself was verified complexity-net-zero during its own CI cycle (DiscoveryPageClient refactored into hooks/sub-components to stay under max-lines-per-function). Tighten via --update next cycle.",
"_rebaseline_2026_07_02_5798_release_green": "1982->1995 (+13). Inherited v3.8.43 cycle drift surfaced by the release-green unblock #5798 / PR #5896: check:complexity measures 1995 on BOTH the pristine release tip (0d3875a98) and this PR's HEAD — identical, so all +13 came from the 2026-07-01/02 merge burst (providers/usage/dashboard fixes merged via --admin while the fast-gates queue was base-red). This PR touches only gate scripts, docs, baselines and test files — 0 production logic. Tighten via --update next cycle.",
"_rebaseline_2026_07_01_v3843_release": "1981->1982 (+1). v3.8.43 cycle drift, surfaced after check:mutation-test-coverage was fixed (it was masked behind that earlier step in the Fast Quality Gates chain). 1982 = the value measured by check:complexity on BOTH fce85136c (release tip) and 6d7060e21 (release + the 5 CI fixes) — identical, so all +1 is inherited cycle drift; the fixes touch only test files + linkify.ts safeHttpHref (cyclomatic ~4, well under the >=15 threshold, 0 new violations) + config JSON. Tighten via --update next cycle.",

View File

@@ -114,7 +114,8 @@
"_rebaseline_2026_06_26_v3837_release": "343->345. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle."
},
"cognitiveComplexity": {
"value": 859,
"value": 860,
"_rebaseline_2026_07_02_v3844_post_5939": "859->860 (+1). Same inherited post-3a3d618fe release drift as the complexity note of this date; this PR touches one CLI line (nullish default, no new function over the threshold) and one test file (not scanned). Tighten via --update next cycle.",
"_rebaseline_2026_07_02_v3844_merge_burst": "856->859 (+3). Inherited v3.8.44 cycle drift surfaced by PR #5939: check:cognitive-complexity measures 859 on BOTH the pristine release tip (3a3d618fe) and this PR's merged HEAD — identical, so the PR is cognitive-net-zero (its DiscoveryPageClient was refactored into hooks/sub-components during its own CI cycle). Drift from the 2026-07-02 merge burst into release/v3.8.44. Tighten via --update next cycle.",
"_rebaseline_2026_07_02_5798_release_green": "845->856 (+11). Inherited v3.8.43 cycle drift surfaced by the release-green unblock #5798 / PR #5896: check:cognitive-complexity measures 856 on BOTH the pristine release tip (0d3875a98) and this PR's HEAD — identical, so the PR is cognitive-net-zero (it touches only gate scripts, docs, baselines and test files). Drift from the 2026-07-01/02 merge burst. Tighten via --update next cycle.",
"direction": "down",

View File

@@ -95,13 +95,13 @@
"electron:build:mac": "npm run build && cd electron && npm run build:mac",
"electron:build:linux": "npm run build && cd electron && npm run build:linux",
"electron:smoke:packaged": "node scripts/dev/smoke-electron-packaged.mjs",
"test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-concurrency=20 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=20 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:shard": "concurrently --kill-others-on-fail -n s1,s2 \"npm:test:unit:shard:1\" \"npm:test:unit:shard:2\"",
"test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:plan3": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/plan3-p0.test.ts",
"test:fixes": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/fixes-p1.test.ts",
"test:security": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/security-fase01.test.ts",
@@ -191,7 +191,7 @@
"test:mutation": "stryker run",
"test:ecosystem": "node scripts/dev/run-ecosystem-tests.mjs",
"test:system": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/e2e/system-failover.test.ts",
"test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 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,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"",
"test:coverage:legacy": "c8 --output-dir coverage --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx --test tests/unit/*.test.ts",
"coverage:report": "cross-env NODE_OPTIONS=--max-old-space-size=8192 c8 report --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov",
"coverage:summary": "node scripts/check/test-report-summary.mjs --input coverage/coverage-summary.json --output coverage/coverage-report.md",

View File

@@ -53,7 +53,7 @@ export const COLLECTORS = [
// "vitest" e explodem no node runner). Subdir novo: adicione aqui E nos scripts
// (o drift-check + o gate de órfãos forçam a manutenção em sincronia).
{
glob: "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts",
glob: "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts",
sources: ["package.json", ".github/workflows/ci.yml"],
},
// Node native runner — test:integration (top-level only; tests/integration/services/ NÃO roda)

View File

@@ -60,7 +60,7 @@ function sourceDepsOf(entry) {
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,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.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 }
);

View File

@@ -4088,6 +4088,8 @@
"prefixHint": "Required. Unique prefix for model names.",
"nameHint": "Required. A friendly label for this node.",
"baseUrlHint": "Required. Provider API base URL.",
"iconUrlLabel": "Icon URL",
"iconUrlHint": "Optional. Image URL shown as this provider's icon.",
"anthropicPrefixPlaceholder": "ac-prod",
"openaiPrefixPlaceholder": "oc-prod",
"anthropicBaseUrlPlaceholder": "https://api.anthropic.com/v1",
@@ -5955,6 +5957,12 @@
"claudeFastModeModelCheckbox": "Enable Fast Mode for {model}",
"claudeFastModeSaveError": "Failed to update Claude Fast Mode setting",
"authz": {
"cors": {
"wildcard": {
"title": "CORS is open to every origin (CORS_ALLOW_ALL=true)",
"desc": "Any website can call this server's API from a visitor's browser. Use only on trusted networks — set explicit origins in ALLOWED_ORIGINS and disable CORS_ALLOW_ALL in production."
}
},
"title": "Authorization Inventory",
"description": "5-tier route classification with live bypass policy. Read shows the full taxonomy; mutations re-prompt for the management password.",
"loading": "Loading inventory…",

View File

@@ -89,19 +89,30 @@ test("syncClaudeProfilesFromModels writes directory-per-profile settings + threa
}
});
test("syncClaudeProfilesFromModels dry-run writes nothing", async () => {
test("syncClaudeProfilesFromModels dry-run writes nothing and reports via the injected log (#5959)", async () => {
const claudeHome = await fs.mkdtemp(path.join(os.tmpdir(), "omniroute-claude-dry-"));
// The collector keeps the dry-run's multi-byte "──" heading OFF this child
// process's stdout: under the node:test runner that write corrupts the V8
// serialization stream ~50% of runs (#5959, "Unable to deserialize cloned
// data due to invalid or unsupported version").
const lines: string[] = [];
try {
const result = await syncClaudeProfilesFromModels([{ id: "glm/glm-5.2" }], {
claudeHome,
baseUrl: "http://vps:20128",
dryRun: true,
log: (line: string) => lines.push(line),
});
assert.equal(result.written, 1);
await assert.rejects(
fs.stat(path.join(claudeHome, "profiles", "glm52", "settings.json")),
/ENOENT/
);
// Dry-run reports the would-be file + its content through the log sink…
assert.equal(lines.length, 2);
const settingsPath = path.join(claudeHome, "profiles", "glm52", "settings.json");
assert.ok(lines[0].includes(settingsPath));
const printed = JSON.parse(lines[1]);
assert.equal(printed.model, "glm/glm-5.2");
assert.equal(printed.env.ANTHROPIC_BASE_URL, "http://vps:20128");
// …and writes nothing to disk.
await assert.rejects(fs.stat(settingsPath), /ENOENT/);
} finally {
await fs.rm(claudeHome, { recursive: true, force: true });
}