diff --git a/tests/unit/check-provider-asset-provenance.test.ts b/tests/unit/check-provider-asset-provenance.test.ts index 35fded6298..8a834f138c 100644 --- a/tests/unit/check-provider-asset-provenance.test.ts +++ b/tests/unit/check-provider-asset-provenance.test.ts @@ -40,7 +40,10 @@ function writeManifest( recordType: "manifest", schemaVersion: 1, expectedAssetCount: records.filter((record) => record.recordType === "asset").length, - auditedCommit: "091589089cd134a94df9f6cdab9ba562b2cefd18", + // HEAD instead of a pinned SHA: the fast-unit shards run on a shallow checkout, + // where a historical commit object does not exist and the gate would reject + // the fixture before exercising what the test is about. + auditedCommit: gitObjectId("HEAD"), auditedAt: "2026-08-26", legalScope: "Provenance records source matching only; it does not establish copyright or trademark clearance.", @@ -98,12 +101,53 @@ function gitObjectId(revision: string) { return result.stdout.trim(); } -function gitRootCommit() { - const result = spawnSync("git", ["-C", REPO_ROOT, "rev-list", "--max-parents=0", "HEAD"], { +function gitHasCommit(objectId: string) { + return ( + spawnSync("git", ["-C", REPO_ROOT, "cat-file", "-e", `${objectId}^{commit}`], { + encoding: "utf8", + }).status === 0 + ); +} + +function isShallowRepository() { + const result = spawnSync("git", ["-C", REPO_ROOT, "rev-parse", "--is-shallow-repository"], { encoding: "utf8", }); - assert.equal(result.status, 0, result.stderr); - return result.stdout.trim().split(/\r?\n/)[0]; + return result.status === 0 && result.stdout.trim() === "true"; +} + +/** + * A commit whose tree is empty, so every physical provider file is "missing" + * from its snapshot. Built as a dangling object (no ref is written) so it also + * works on the shallow checkouts the unit shards use, where the root commit is + * the grafted HEAD itself and would match the physical snapshot exactly. + */ +function emptyTreeCommit() { + const tree = spawnSync("git", ["-C", REPO_ROOT, "hash-object", "-w", "-t", "tree", "--stdin"], { + input: "", + encoding: "utf8", + }); + assert.equal(tree.status, 0, tree.stderr); + const identity = { + GIT_AUTHOR_NAME: "provenance-fixture", + GIT_AUTHOR_EMAIL: "provenance-fixture@example.invalid", + GIT_COMMITTER_NAME: "provenance-fixture", + GIT_COMMITTER_EMAIL: "provenance-fixture@example.invalid", + }; + const commit = spawnSync( + "git", + [ + "-C", + REPO_ROOT, + "commit-tree", + tree.stdout.trim(), + "-m", + "provenance fixture: empty snapshot", + ], + { encoding: "utf8", env: { ...process.env, ...identity } } + ); + assert.equal(commit.status, 0, commit.stderr); + return commit.stdout.trim(); } function workflowJob(source: string, name: string) { @@ -479,7 +523,7 @@ test("provider asset provenance gate binds auditedCommit to the physical provide .trim() .split("\n") .map((line) => JSON.parse(line)); - records[0] = { ...records[0], auditedCommit: gitRootCommit() }; + records[0] = { ...records[0], auditedCommit: emptyTreeCommit() }; writeFileSync( fixture.manifestPath, `${records.map((record) => JSON.stringify(record)).join("\n")}\n` @@ -497,11 +541,17 @@ test("provider asset provenance gate binds auditedCommit to the physical provide } }); -test("repository provider asset manifest covers the audited 142-file snapshot", () => { - const result = runGate( - join(REPO_ROOT, "public/providers"), - join(REPO_ROOT, "config/quality/provider-assets-provenance.jsonl") - ); +test("repository provider asset manifest covers the audited 142-file snapshot", (t) => { + const manifestPath = join(REPO_ROOT, "config/quality/provider-assets-provenance.jsonl"); + const { auditedCommit } = JSON.parse(readFileSync(manifestPath, "utf8").split("\n")[0]); + if (!gitHasCommit(auditedCommit) && isShallowRepository()) { + // The real manifest pins a historical commit. The unit shards check out with + // depth 1, so it is not fetched there; the gate itself still runs on both + // blocking rails with fetch-depth 0 (asserted by the test right below). + t.skip(`shallow checkout without auditedCommit ${auditedCommit}`); + return; + } + const result = runGate(join(REPO_ROOT, "public/providers"), manifestPath); assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`); assert.match( diff --git a/tests/unit/gemini-malformed-function-call-finish-reason-2462.test.ts b/tests/unit/gemini-malformed-function-call-finish-reason-2462.test.ts index 3e53aec263..5234524e6e 100644 --- a/tests/unit/gemini-malformed-function-call-finish-reason-2462.test.ts +++ b/tests/unit/gemini-malformed-function-call-finish-reason-2462.test.ts @@ -70,6 +70,12 @@ function runGeminiToClaude(geminiChunk) { const converted = openaiToClaudeResponse(chunk, claudeState); if (converted) claudeEvents.push(...converted); } + // End-of-stream flush: production calls the translator once more with `null` + // when the upstream stream closes (open-sse/utils/stream.ts flush → + // translateResponse(..., null, state)). Since dd35750e5f a finish chunk that + // carries no usage is deferred until that flush, so the driver must mirror it. + const flushed = openaiToClaudeResponse(null, claudeState); + if (flushed) claudeEvents.push(...flushed); return { openaiEvents, claudeEvents }; } diff --git a/tests/unit/model-lifecycle-integration.test.ts b/tests/unit/model-lifecycle-integration.test.ts index 2676e0d880..dd2426d3b1 100644 --- a/tests/unit/model-lifecycle-integration.test.ts +++ b/tests/unit/model-lifecycle-integration.test.ts @@ -115,7 +115,12 @@ test("unified catalog suppresses stale OpenAI chat rows but retains typed media" assert.equal(ids.has("openai/gpt-5.2-codex"), false); assert.equal(ids.has("openai/sora-2"), false); assert.equal(ids.has("openai/sora-2-pro"), false); - assert.equal(ids.has("openai/gpt-5.6-sol"), true); + // Since #11919 (fixes #11829) an authoritative live catalog REPLACES the static + // registry: a static-only row like gpt-5.6-sol that the synced catalog does not + // list is suppressed instead of leaking into /v1/models. The lifecycle contract + // this file guards (#8627: stale chat rows suppressed, typed media retained) + // is unchanged — only the "static rows survive a sync" expectation moved. + assert.equal(ids.has("openai/gpt-5.6-sol"), false); assert.equal(body.data.find((item) => item.id === "openai/gpt-image-2")?.type, "image"); }); diff --git a/tests/unit/stream-markdown-token-boundary.test.ts b/tests/unit/stream-markdown-token-boundary.test.ts index 3254d2857d..ac2c56dd20 100644 --- a/tests/unit/stream-markdown-token-boundary.test.ts +++ b/tests/unit/stream-markdown-token-boundary.test.ts @@ -203,7 +203,10 @@ test("OpenAI to Claude: finish flushes a fully-held boundary before message stop }, state ); - const result = flatten([chunk1, chunk2]); + // End-of-stream flush (see dd35750e5f): a finish chunk without usage is deferred + // until production's null flush, so mirror it before asserting the terminal events. + const chunk3 = openaiToClaudeResponse(null, state); + const result = flatten([chunk1, chunk2, chunk3]); assert.deepEqual(getTextDeltas(result), ["`"]); assert.equal(state._markdownBuffer, ""); @@ -251,7 +254,10 @@ test("OpenAI to Claude: tool call flushes a fully-held boundary before tool use" }, state ); - const result = flatten([chunk1, chunk2]); + // End-of-stream flush (see dd35750e5f): a finish chunk without usage is deferred + // until production's null flush, so mirror it before asserting the terminal events. + const chunk3 = openaiToClaudeResponse(null, state); + const result = flatten([chunk1, chunk2, chunk3]); const contentEvents = result.filter((event) => String((event as Record).type).startsWith("content_block_") ); diff --git a/tests/unit/translator-tool-call-shim.test.ts b/tests/unit/translator-tool-call-shim.test.ts index 2c5bde48df..08242e5f92 100644 --- a/tests/unit/translator-tool-call-shim.test.ts +++ b/tests/unit/translator-tool-call-shim.test.ts @@ -1,12 +1,10 @@ import test from "node:test"; import assert from "node:assert/strict"; -const { applyToolCallShimToBuffer, hasToolCallShim, __test } = await import( - "../../open-sse/translator/helpers/toolCallShim.ts" -); -const { openaiToClaudeResponse } = await import( - "../../open-sse/translator/response/openai-to-claude.ts" -); +const { applyToolCallShimToBuffer, hasToolCallShim, __test } = + await import("../../open-sse/translator/helpers/toolCallShim.ts"); +const { openaiToClaudeResponse } = + await import("../../open-sse/translator/response/openai-to-claude.ts"); const { coerceToArray } = __test as { coerceToArray: (v: unknown) => unknown[] }; @@ -120,30 +118,21 @@ test("applyToolCallShimToBuffer: Read coerces numeric-string limit/offset", () = test("applyToolCallShimToBuffer: Read strips pages for non-PDF files", () => { const out = JSON.parse( - applyToolCallShimToBuffer( - "Read", - JSON.stringify({ file_path: "/etc/hosts", pages: "1-3" }) - ) + applyToolCallShimToBuffer("Read", JSON.stringify({ file_path: "/etc/hosts", pages: "1-3" })) ); assert.equal("pages" in out, false); }); test("applyToolCallShimToBuffer: Read strips malformed pages even on PDFs", () => { const out = JSON.parse( - applyToolCallShimToBuffer( - "Read", - JSON.stringify({ file_path: "/tmp/doc.pdf", pages: "abc" }) - ) + applyToolCallShimToBuffer("Read", JSON.stringify({ file_path: "/tmp/doc.pdf", pages: "abc" })) ); assert.equal("pages" in out, false); }); test("applyToolCallShimToBuffer: Read accepts a single page on PDFs", () => { const out = JSON.parse( - applyToolCallShimToBuffer( - "Read", - JSON.stringify({ file_path: "/tmp/doc.PDF", pages: "7" }) - ) + applyToolCallShimToBuffer("Read", JSON.stringify({ file_path: "/tmp/doc.PDF", pages: "7" })) ); assert.equal(out.pages, "7"); }); @@ -255,6 +244,12 @@ function streamChunks(chunks: any[], state: any): any[] { const out = openaiToClaudeResponse(c, state); if (out) all.push(...out); } + // End-of-stream flush: production calls the translator once more with `null` + // when the upstream stream closes (open-sse/utils/stream.ts flush → + // translateResponse(..., null, state)). Since dd35750e5f a finish chunk that + // carries no usage is deferred until that flush, so the driver must mirror it. + const flushed = openaiToClaudeResponse(null, state); + if (flushed) all.push(...flushed); return all; }