diff --git a/tests/runner-e2e/chat-catalog-fingerprint.test.ts b/tests/runner-e2e/chat-catalog-fingerprint.test.ts index 5f51aa824f4..ec021c60381 100644 --- a/tests/runner-e2e/chat-catalog-fingerprint.test.ts +++ b/tests/runner-e2e/chat-catalog-fingerprint.test.ts @@ -3,7 +3,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, expect, it, vi } from "vitest"; -import { assertChatCatalogFingerprintLane, chatCatalogCaseFingerprint, fingerprintChatDependencyClosure } from "./chat-catalog-fingerprint.js"; +import { assertChatCatalogFingerprintLane, chatCatalogCaseFingerprint, chatCatalogSpawnedSourceEntries, fingerprintChatDependencyClosure } from "./chat-catalog-fingerprint.js"; import { findChatCatalogCase, loadChatCatalogImplementation, type ChatCatalogDescriptor } from "./chat-catalog-registry.js"; const directories: string[] = []; afterEach(() => { vi.unstubAllEnvs(); for (const directory of directories.splice(0)) rmSync(directory, { recursive: true, force: true }); }); @@ -23,6 +23,17 @@ it("invalidates shared, selected and transitive dependencies while ignoring an u f.write(target, "export const changed = true;"); expect(f.fingerprint()).not.toBe(before); } }); +it("binds actual spawned supervisor and entry bytes even when launch refers to them only by path", () => { + expect(chatCatalogSpawnedSourceEntries).toEqual(["tests/runner-e2e/server.ts", "tests/runner-e2e/server-entry.ts"]); + for (const changed of chatCatalogSpawnedSourceEntries) { + const f = fixture(); mkdirSync(path.join(f.root, "tests/runner-e2e"), { recursive: true }); + for (const file of chatCatalogSpawnedSourceEntries) f.write(file, "export const behavior = 'original';"); + const fingerprint = () => fingerprintChatDependencyClosure({ root: f.root, entries: ["selected.ts", ...chatCatalogSpawnedSourceEntries], selected: {} }); + const before = fingerprint(); + expect(before.sources.map(row => row.file)).toEqual(expect.arrayContaining([...chatCatalogSpawnedSourceEntries])); + f.write(changed, "export const behavior = 'changed executable';"); expect(fingerprint().digest).not.toBe(before.digest); + } +}); it("conservatively binds unknown dynamic imports", () => { const f = fixture(); f.write("selected.ts", "export const load = (name: string) => import(name);"); const before = f.fingerprint(); f.write("unselected.ts", "export const changed = true;"); @@ -59,16 +70,16 @@ it("refuses formerly inactive launch targets and source drift falls back conserv expect(fingerprintChatDependencyClosure({ root: f.root, entries: ["selected.ts"], selected: lane }).digest).not.toBe(before.digest); }); -it("binds the real selected area and reviewed worker closure without importing unrelated domain areas", () => { +it("binds selected and spawned code, conservatively closing unresolved CLI and production loaders", () => { const descriptor = findChatCatalogCase("projects.create-project")!; const result = chatCatalogCaseFingerprint(descriptor, { suiteId: "chat-features", flow: "agent_chat", environment: "local" }); const files = result.sources.map(source => source.file); expect(files).toContain("tests/runner-e2e/chat-catalog-domains/internal-projects.ts"); expect(files).toContain("tests/runner-e2e/chat-catalog-domains/internal-support.ts"); expect(files).toContain("packages/adapter-utils/src/workspace-snapshot-thread-worker.ts"); - expect(files).not.toContain("tests/runner-e2e/chat-catalog-domains/internal-goals.ts"); + expect(files).toEqual(expect.arrayContaining([...chatCatalogSpawnedSourceEntries, "tests/runner-e2e/chat-catalog-domains/internal-goals.ts"])); expect(files).toContain("tests/runner-e2e/chat-catalog-domains/identity-auth.ts"); - expect(result.unknownDynamicImports).toEqual([]); + expect(result.unknownDynamicImports).toEqual(expect.arrayContaining(["cli/src/commands/run.ts", "server/src/adapters/plugin-loader.ts"])); }); it("fails closed when preserved prerequisites or a rejected query lack their independent verifier", async () => { const base: ChatCatalogDescriptor = { scenarioId: "selected", modulePath: "./chat-catalog-domains/selected.js", exportName: "cases", diff --git a/tests/runner-e2e/chat-catalog-fingerprint.ts b/tests/runner-e2e/chat-catalog-fingerprint.ts index 3b2ef639939..39cee9a349d 100644 --- a/tests/runner-e2e/chat-catalog-fingerprint.ts +++ b/tests/runner-e2e/chat-catalog-fingerprint.ts @@ -11,6 +11,8 @@ import { buildSync } from "esbuild"; import type { MatrixExecution } from "./types.js"; import type { ChatCatalogDescriptor } from "./chat-catalog-registry.js"; +export const chatCatalogSpawnedSourceEntries = ["tests/runner-e2e/server.ts", "tests/runner-e2e/server-entry.ts"] as const; + const hash = (value: string) => createHash("sha256").update(value).digest("hex"); function resolveSource(specifier: string, parent: string): string { const absolute = path.resolve(path.dirname(parent), specifier); @@ -116,7 +118,7 @@ export function chatCatalogCaseFingerprint(descriptor: ChatCatalogDescriptor, se const originIdentity = publicOriginFingerprintIdentity((selectedExecution as { executionId: string }).executionId, descriptor.scenarioId); const originReaders = originIdentity.selected ? ["server/src/config.ts", "server/src/auth/better-auth.ts", "server/src/routes/tool-access.ts", "server/src/services/chat-task-url.ts", "server/src/services/muse-setup.ts", "server/src/services/workspace-runtime.ts", "tests/runner-e2e/chat-catalog-domains/external-public-signin-worker.ts", "tests/runner-e2e/chat-catalog-domains/external-public-signin-contract.ts", "ui/src/pages/Auth.tsx"] : []; const result = fingerprintChatDependencyClosure({ root, - entries: [selectedModule, "tests/runner-e2e/chat-catalog-flow.ts", "tests/runner-e2e/chat-catalog-fingerprint.ts", "tests/runner-e2e/chat-catalog-dynamic-resolutions.json", + entries: [selectedModule, ...chatCatalogSpawnedSourceEntries, "tests/runner-e2e/chat-catalog-flow.ts", "tests/runner-e2e/chat-catalog-fingerprint.ts", "tests/runner-e2e/chat-catalog-dynamic-resolutions.json", "tests/runner-e2e/chat-feature-flow.ts", "tests/runner-e2e/runner.spec.ts", "tests/runner-e2e/launch.ts", "tests/runner-e2e/chat-scenarios/defaults.yaml", ...originReaders, ...descriptor.sourceFiles.map(file => `tests/runner-e2e/${file}`)], selected: { descriptor, selectedExecution, lane, publicOriginIdentity: originIdentity, attachmentFormatter, privateInputRequirements: chatPrivateInputRequirements(descriptor), privateInputNameIdentity, installedIdentity, scenario: { id, capabilities, context, query, expected_outcome } },