diff --git a/changelog.d/fixes/stryker-tapfiles-drift-release-line.md b/changelog.d/fixes/stryker-tapfiles-drift-release-line.md new file mode 100644 index 0000000000..bf3e37a56b --- /dev/null +++ b/changelog.d/fixes/stryker-tapfiles-drift-release-line.md @@ -0,0 +1,2 @@ +- **fix(ci):** register the four unit tests whose mutant kills were not counting — `daily-reset-tz-threading`, `noauth-model-lockout`, `free-badge-provider-gate` and `local-token-budget-429-skips-cooldown` — in `stryker.conf.json` `tap.testFiles`. `check-mutation-test-coverage --strict` reported 6 missing coverings across 4 mutated modules (`accountFallback`, `sse/services/auth`, `combo/comboPredicates`, `combo/rrState`) on the release line itself with no PR diff involved, so the `mutation-test-coverage` gate was red on every open PR regardless of its contents; the gate now reports no drift +- fix(ci): drop a `tap.testFiles` entry naming a deleted test file, and guard the direction the existing drift check never covered — an entry left behind after its test is removed or renamed costs mutation coverage silently, because Stryker resolves the list into its sandbox without failing on a dangling path ([#13814](https://github.com/diegosouzapw/OmniRoute/pull/13814)) diff --git a/stryker.conf.json b/stryker.conf.json index a821949e9f..d2e063b572 100644 --- a/stryker.conf.json +++ b/stryker.conf.json @@ -62,6 +62,7 @@ "tests/unit/combo-pin-implicit-allowlist.test.ts", "tests/unit/combo-predicates-epoch-cooldown.test.ts", "tests/unit/combo/execute-target-gates.test.ts", + "tests/unit/daily-reset-tz-threading.test.ts", "tests/unit/moonshot-quota-writeback.test.ts", "tests/unit/accountfallback-ratelimit-400-4976.test.ts", "tests/unit/adaptive-admission-route-matrix.test.ts", @@ -331,6 +332,7 @@ "tests/unit/antigravityUpstreamError.test.ts", "tests/unit/issue-13089-roundrobin-live-ws-events.test.ts", "tests/unit/ollama-404-model-lockout-11071.test.ts", + "tests/unit/noauth-model-lockout.test.ts", "tests/unit/non-streaming-client-translate.test.ts", "tests/unit/non-streaming-provider-leg.test.ts", "tests/unit/non-streaming-sse-terminal-typescan-4459.test.ts", @@ -353,7 +355,6 @@ "tests/unit/permanent-failure-hammering-other-providers.test.ts", "tests/unit/persist-429-cooldown-account-fallback.test.ts", "tests/unit/plan3-p0.test.ts", - "tests/unit/plugin-sandbox-permissions.test.ts", "tests/unit/plugins-route-error-sanitization.test.ts", "tests/unit/probe-gate-autodisable.test.ts", "tests/unit/probe-production-path.test.ts", diff --git a/tests/unit/build/check-mutation-test-coverage.test.ts b/tests/unit/build/check-mutation-test-coverage.test.ts index acaf7f5736..a392e22068 100644 --- a/tests/unit/build/check-mutation-test-coverage.test.ts +++ b/tests/unit/build/check-mutation-test-coverage.test.ts @@ -1,5 +1,7 @@ import { test } from "node:test"; import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; import { moduleFragment, testImportsModule, @@ -25,7 +27,10 @@ test("testImportsModule matches static, dynamic and require imports of the modul ); // dynamic await import, even split across lines assert.equal( - testImportsModule(`const { y } = await import(\n "../../open-sse/handlers/chatCore/headers.ts"\n);`, frag), + testImportsModule( + `const { y } = await import(\n "../../open-sse/handlers/chatCore/headers.ts"\n);`, + frag + ), true ); // require() @@ -35,7 +40,10 @@ test("testImportsModule matches static, dynamic and require imports of the modul ); // unrelated module is not matched assert.equal( - testImportsModule(`import { a } from "@omniroute/open-sse/handlers/chatCore/idempotency";`, frag), + testImportsModule( + `import { a } from "@omniroute/open-sse/handlers/chatCore/idempotency";`, + frag + ), false ); // the fragment appearing only in a comment (not an import string) is NOT a match @@ -54,11 +62,20 @@ test("findCoverageDrift flags covering unit tests absent from tap.testFiles", () const tapTestFiles = ["tests/unit/chatcore-headers.test.ts"]; const unitTests = [ // covers headers, already in tap -> not drift - { path: "tests/unit/chatcore-headers.test.ts", content: `await import("../../open-sse/handlers/chatCore/headers.ts");` }, + { + path: "tests/unit/chatcore-headers.test.ts", + content: `await import("../../open-sse/handlers/chatCore/headers.ts");`, + }, // covers headers, NOT in tap -> drift - { path: "tests/unit/no-memory-header.test.ts", content: `const { isNoMemoryRequested } = await import("../../open-sse/handlers/chatCore/headers.ts");` }, + { + path: "tests/unit/no-memory-header.test.ts", + content: `const { isNoMemoryRequested } = await import("../../open-sse/handlers/chatCore/headers.ts");`, + }, // covers idempotency, NOT in tap -> drift - { path: "tests/unit/idempo.test.ts", content: `import { x } from "@omniroute/open-sse/handlers/chatCore/idempotency";` }, + { + path: "tests/unit/idempo.test.ts", + content: `import { x } from "@omniroute/open-sse/handlers/chatCore/idempotency";`, + }, // covers nothing mutated -> ignored { path: "tests/unit/unrelated.test.ts", content: `import { z } from "@/lib/foo";` }, ]; @@ -72,3 +89,21 @@ test("findCoverageDrift flags covering unit tests absent from tap.testFiles", () // comment-only mutate entries are skipped assert.equal("_a_comment_entry" in drift, false); }); + +test("every tap.testFiles entry names a file that still exists", () => { + // The existing drift check runs one way: a test that covers a mutated module + // but is missing from tap.testFiles. The other way is silent — an entry left + // behind after its test file is deleted or renamed. Stryker resolves the list + // into its sandbox, so a dangling path costs coverage without failing loudly. + const repoRoot = path.resolve(import.meta.dirname, "../../.."); + const conf = JSON.parse(fs.readFileSync(path.join(repoRoot, "stryker.conf.json"), "utf8")) as { + tap?: { testFiles?: string[] }; + }; + const entries = conf.tap?.testFiles ?? []; + assert.ok(entries.length > 0, "stryker.conf.json declares no tap.testFiles"); + + const missing = entries + .filter((entry) => !entry.includes("*")) + .filter((entry) => !fs.existsSync(path.join(repoRoot, entry))); + assert.deepEqual(missing, [], `tap.testFiles names ${missing.length} file(s) that do not exist`); +});