From 008132e49c780dd8a530fa473ca99058475cde9c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sun, 4 Oct 2026 00:39:36 -0400 Subject: [PATCH] test(server): the text generation's fake Claude CLI is a fixture file, not a generated string Signed-off-by: Yordis Prieto --- .../ClaudeTextGeneration.test.ts | 80 ++----------------- .../testing/ClaudeTextGeneration.fixture.mjs | 72 +++++++++++++++++ 2 files changed, 78 insertions(+), 74 deletions(-) create mode 100644 apps/server/src/textGeneration/testing/ClaudeTextGeneration.fixture.mjs diff --git a/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts b/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts index fc51a7248ad3..42483614043f 100644 --- a/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts +++ b/apps/server/src/textGeneration/ClaudeTextGeneration.test.ts @@ -34,86 +34,18 @@ const ClaudeTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces function makeFakeClaudeBinary(dir: string) { return Effect.gen(function* () { const path = yield* Path.Path; + const fs = yield* FileSystem.FileSystem; const platform = yield* HostProcessPlatform; const binDir = path.join(dir, "bin"); + const fixturePath = yield* path.fromFileUrl( + new URL("./testing/ClaudeTextGeneration.fixture.mjs", import.meta.url), + ); + const source = yield* fs.readFileString(fixturePath); writeFakeCli({ directory: binDir, name: "claude", platform, - source: [ - "const argv = process.argv.slice(2);", - 'const args = argv.join(" ");', - 'const { realpathSync } = await import("node:fs");', - "", - "function fail(message, code) {", - ' process.stderr.write(message + "\\n");', - " process.exit(code);", - "}", - "", - 'const permissionIndex = argv.indexOf("--permission-mode");', - 'if (permissionIndex === -1 || argv[permissionIndex + 1] !== "dontAsk") {', - ' fail("text generation must deny permission prompts", 12);', - "}", - 'const toolsIndex = argv.indexOf("--tools");', - 'if (toolsIndex === -1 || argv[toolsIndex + 1] !== "") {', - ' fail("text generation must receive an explicit empty tool set", 6);', - "}", - 'if (argv.includes("--dangerously-skip-permissions")) {', - ' fail("text generation must not bypass permissions", 7);', - "}", - 'if (!argv.includes("--disable-slash-commands")) {', - ' fail("text generation must disable skills", 8);', - "}", - 'if (!argv.includes("--strict-mcp-config")) {', - ' fail("text generation must not load configured MCP servers", 9);', - "}", - 'const settingsIndex = argv.indexOf("--settings");', - "if (settingsIndex === -1 || JSON.parse(argv[settingsIndex + 1]).disableAllHooks !== true) {", - ' fail("text generation must disable hooks", 10);', - "}", - "const cwdMustNotBe = process.env.T3_FAKE_CLAUDE_CWD_MUST_NOT_BE;", - "if (cwdMustNotBe && realpathSync(process.cwd()) === realpathSync(cwdMustNotBe)) {", - ' fail("text generation ran in the project directory", 11);', - "}", - "", - 'let stdinContent = "";', - "if (!process.stdin.isTTY) {", - " const chunks = [];", - " for await (const chunk of process.stdin) {", - " chunks.push(chunk);", - " }", - ' stdinContent = Buffer.concat(chunks).toString("utf8");', - "}", - "", - "const argsMustContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN;", - "if (argsMustContain && !args.includes(argsMustContain)) {", - ' fail("args missing expected content", 2);', - "}", - "", - "const argsMustNotContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN;", - "if (argsMustNotContain && args.includes(argsMustNotContain)) {", - ' fail("args contained forbidden content", 3);', - "}", - "", - "const stdinMustContain = process.env.T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN;", - "if (stdinMustContain && !stdinContent.includes(stdinMustContain)) {", - ' fail("stdin missing expected content", 4);', - "}", - "", - "const configDirMustBe = process.env.T3_FAKE_CLAUDE_CONFIG_DIR_MUST_BE;", - "if (configDirMustBe && process.env.CLAUDE_CONFIG_DIR !== configDirMustBe) {", - ' fail("CLAUDE_CONFIG_DIR was " + (process.env.CLAUDE_CONFIG_DIR ?? ""), 5);', - "}", - "", - "const stderrText = process.env.T3_FAKE_CLAUDE_STDERR;", - "if (stderrText) {", - ' process.stderr.write(stderrText + "\\n");', - "}", - "", - 'process.stdout.write(process.env.T3_FAKE_CLAUDE_OUTPUT ?? "");', - "process.exitCode = Number(process.env.T3_FAKE_CLAUDE_EXIT_CODE ?? 0);", - "", - ].join("\n"), + source, }); return binDir; }); diff --git a/apps/server/src/textGeneration/testing/ClaudeTextGeneration.fixture.mjs b/apps/server/src/textGeneration/testing/ClaudeTextGeneration.fixture.mjs new file mode 100644 index 000000000000..d5ca2f185eb8 --- /dev/null +++ b/apps/server/src/textGeneration/testing/ClaudeTextGeneration.fixture.mjs @@ -0,0 +1,72 @@ +import * as NodeFS from "node:fs"; + +const argv = process.argv.slice(2); +const args = argv.join(" "); + +function fail(message, code) { + process.stderr.write(message + "\n"); + process.exit(code); +} + +const permissionIndex = argv.indexOf("--permission-mode"); +if (permissionIndex === -1 || argv[permissionIndex + 1] !== "dontAsk") { + fail("text generation must deny permission prompts", 12); +} +const toolsIndex = argv.indexOf("--tools"); +if (toolsIndex === -1 || argv[toolsIndex + 1] !== "") { + fail("text generation must receive an explicit empty tool set", 6); +} +if (argv.includes("--dangerously-skip-permissions")) { + fail("text generation must not bypass permissions", 7); +} +if (!argv.includes("--disable-slash-commands")) { + fail("text generation must disable skills", 8); +} +if (!argv.includes("--strict-mcp-config")) { + fail("text generation must not load configured MCP servers", 9); +} +const settingsIndex = argv.indexOf("--settings"); +if (settingsIndex === -1 || JSON.parse(argv[settingsIndex + 1]).disableAllHooks !== true) { + fail("text generation must disable hooks", 10); +} +const cwdMustNotBe = process.env.T3_FAKE_CLAUDE_CWD_MUST_NOT_BE; +if (cwdMustNotBe && NodeFS.realpathSync(process.cwd()) === NodeFS.realpathSync(cwdMustNotBe)) { + fail("text generation ran in the project directory", 11); +} + +let stdinContent = ""; +if (!process.stdin.isTTY) { + const chunks = []; + for await (const chunk of process.stdin) { + chunks.push(chunk); + } + stdinContent = Buffer.concat(chunks).toString("utf8"); +} + +const argsMustContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN; +if (argsMustContain && !args.includes(argsMustContain)) { + fail("args missing expected content", 2); +} + +const argsMustNotContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN; +if (argsMustNotContain && args.includes(argsMustNotContain)) { + fail("args contained forbidden content", 3); +} + +const stdinMustContain = process.env.T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN; +if (stdinMustContain && !stdinContent.includes(stdinMustContain)) { + fail("stdin missing expected content", 4); +} + +const configDirMustBe = process.env.T3_FAKE_CLAUDE_CONFIG_DIR_MUST_BE; +if (configDirMustBe && process.env.CLAUDE_CONFIG_DIR !== configDirMustBe) { + fail("CLAUDE_CONFIG_DIR was " + (process.env.CLAUDE_CONFIG_DIR ?? ""), 5); +} + +const stderrText = process.env.T3_FAKE_CLAUDE_STDERR; +if (stderrText) { + process.stderr.write(stderrText + "\n"); +} + +process.stdout.write(process.env.T3_FAKE_CLAUDE_OUTPUT ?? ""); +process.exitCode = Number(process.env.T3_FAKE_CLAUDE_EXIT_CODE ?? 0);