mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 13:52:09 +03:00
* test(infra): isolate DATA_DIR per test process; raise Stryker concurrency 1→4 Every test process resolved DATA_DIR to the same default (~/.omniroute) when the env var was unset (src/lib/dataPaths.ts::resolveDataDir), so concurrent test files opened the SAME on-disk storage.sqlite. node:test spawns a process per file and Stryker spawns one per sandbox, so this shared file caused cross-file state races: - SQLite lock contention that hung `npm run test:unit` under high --test-concurrency (the ~95-min local hang), and - the non-deterministic baseline that forced stryker.conf.json to concurrency: 1, which in turn could not finish the ~15k-mutant run inside the nightly timeout (the cancelled 2026-06-16/17 nightly-mutation runs) — blocking Quality Gate v2 / Fase 9 Onda 2. open-sse/utils/setupPolyfill.ts could NOT host the fix: it is imported by production (bin/omniroute.mjs, proxyFetch.ts, proxyDispatcher.ts), where redirecting DATA_DIR would point the live SQLite DB at a throwaway temp dir. So this adds a TEST-ONLY tests/_setup/isolateDataDir.ts that gives each process its own temp DATA_DIR when none is set (tests that set DATA_DIR explicitly still win), wired via --import into the test, mutation and CI invocations. Verified: - Stryker dry-run A/B at concurrency=4: FAILS without the isolation import (account-fallback-service tap exit 9, a cross-file race) and PASSES with it. - Full `npm run test:unit` green with isolation (0 fail; a one-off chatcore-translation-paths timeout flake did not reproduce and passes 3/3 isolated) and noticeably faster — the DB lock contention is gone. - New tests/unit/isolate-datadir.test.ts guards the contract (unique temp DATA_DIR when unset; explicit DATA_DIR respected). Wired the --import into: package.json (13 test scripts), stryker.conf.json (tap.nodeArgs + concurrency 1→4), .github/workflows/quality.yml (TIA step), ci.yml (the 5 unit/coverage/integration commands), and bumped nightly-mutation.yml timeout 120→180 for the first cold run before the incremental cache is seeded. * ci(quality): run the TIA gate at CI concurrency (4) to stop oversubscription flakes The TIA "Impacted unit tests" step (made blocking in #4069) ran its fail-safe via `npm run test:unit` — concurrency=20, tuned for multi-core dev machines. On a 4-vCPU CI runner that is 5x oversubscribed, so timing-sensitive tests flake under the load (e.g. `db-backup-extended` "The database connection is not open", `chatcore-translation-paths` upstream-timeout). That intermittently fails a blocking gate on legitimate PRs — exactly what surfaced on the DATA_DIR-isolation PR, whose package.json/workflow changes trip the __RUN_ALL__ fail-safe. Run both the impacted set and the fail-safe at --test-concurrency=4, matching the stable ci.yml unit job. Adds a `test:unit:ci` script (test:unit at concurrency=4). The DATA_DIR isolation in this PR keeps the parallel run race-free, so the only change here is matching the runner's core count. Verified locally: db-backup-extended passes 8/8 in isolation (5 with isolation, 3 without).
46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
// Regression guard for tests/_setup/isolateDataDir.ts — the test-only module that
|
|
// gives each test process its own DATA_DIR so concurrent test files never share the
|
|
// on-disk SQLite DB. Removing or breaking it brings back the cross-file state races
|
|
// (the `test:unit` hang under high concurrency and the non-deterministic Stryker
|
|
// baseline that forced concurrency: 1).
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import os from "node:os";
|
|
|
|
function dataDirFromChild(envDataDir: string | undefined): string {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[
|
|
"--import",
|
|
"tsx",
|
|
"--import",
|
|
"./tests/_setup/isolateDataDir.ts",
|
|
"-e",
|
|
"console.log(process.env.DATA_DIR ?? '')",
|
|
],
|
|
{
|
|
encoding: "utf8",
|
|
cwd: process.cwd(),
|
|
// Pass DATA_DIR through verbatim; an empty string means "unset" for the module's
|
|
// `if (!process.env.DATA_DIR)` guard.
|
|
env: { ...process.env, DATA_DIR: envDataDir ?? "" },
|
|
}
|
|
);
|
|
return result.stdout.trim().split("\n").pop() ?? "";
|
|
}
|
|
|
|
test("isolateDataDir assigns a unique temp DATA_DIR when none is set", () => {
|
|
const a = dataDirFromChild(undefined);
|
|
const b = dataDirFromChild(undefined);
|
|
|
|
assert.ok(a.startsWith(os.tmpdir()), `expected a temp dir under ${os.tmpdir()}, got ${a}`);
|
|
assert.match(a, /omniroute-test-/, `expected the omniroute-test- prefix, got ${a}`);
|
|
assert.notEqual(a, b, "two processes must each get their own DATA_DIR");
|
|
});
|
|
|
|
test("isolateDataDir respects an explicitly set DATA_DIR", () => {
|
|
const explicit = "/tmp/omniroute-explicit-fixture";
|
|
assert.equal(dataDirFromChild(explicit), explicit);
|
|
});
|