authorgravatar for yordis.prieto@gmail.comYordis Prieto <yordis.prieto@gmail.com> 2026-10-04 01:27:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 22:27:19-07:00
logee7b49d638e412590b09b415cc48399d51644b60
tree7250f54bf0d5e3ffbad1d5534c397265207c7b98
parentd073c261b0e4ba8fd77bf92030c9a372b87fff27
signature Signed by PGP key B5690EEEBB952194

test(server): the text generation's fake Claude CLI is a fixture file, not a generated string (#15479)

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>

2 files changed, 78 insertions(+), 74 deletions(-)

apps/server/src/textGeneration/ClaudeTextGeneration.test.ts+6-74
......@@ -34,86 +34,18 @@ const ClaudeTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces
3434function makeFakeClaudeBinary(dir: string) {
3535 return Effect.gen(function* () {
3636 const path = yield* Path.Path;
37 const fs = yield* FileSystem.FileSystem;
3738 const platform = yield* HostProcessPlatform;
3839 const binDir = path.join(dir, "bin");
40 const fixturePath = yield* path.fromFileUrl(
41 new URL("./testing/ClaudeTextGeneration.fixture.mjs", import.meta.url),
42 );
43 const source = yield* fs.readFileString(fixturePath);
3944 writeFakeCli({
4045 directory: binDir,
4146 name: "claude",
4247 platform,
43 source: [
44 "const argv = process.argv.slice(2);",
45 'const args = argv.join(" ");',
46 'const { realpathSync } = await import("node:fs");',
47 "",
48 "function fail(message, code) {",
49 ' process.stderr.write(message + "\\n");',
50 " process.exit(code);",
51 "}",
52 "",
53 'const permissionIndex = argv.indexOf("--permission-mode");',
54 'if (permissionIndex === -1 || argv[permissionIndex + 1] !== "dontAsk") {',
55 ' fail("text generation must deny permission prompts", 12);',
56 "}",
57 'const toolsIndex = argv.indexOf("--tools");',
58 'if (toolsIndex === -1 || argv[toolsIndex + 1] !== "") {',
59 ' fail("text generation must receive an explicit empty tool set", 6);',
60 "}",
61 'if (argv.includes("--dangerously-skip-permissions")) {',
62 ' fail("text generation must not bypass permissions", 7);',
63 "}",
64 'if (!argv.includes("--disable-slash-commands")) {',
65 ' fail("text generation must disable skills", 8);',
66 "}",
67 'if (!argv.includes("--strict-mcp-config")) {',
68 ' fail("text generation must not load configured MCP servers", 9);',
69 "}",
70 'const settingsIndex = argv.indexOf("--settings");',
71 "if (settingsIndex === -1 || JSON.parse(argv[settingsIndex + 1]).disableAllHooks !== true) {",
72 ' fail("text generation must disable hooks", 10);',
73 "}",
74 "const cwdMustNotBe = process.env.T3_FAKE_CLAUDE_CWD_MUST_NOT_BE;",
75 "if (cwdMustNotBe && realpathSync(process.cwd()) === realpathSync(cwdMustNotBe)) {",
76 ' fail("text generation ran in the project directory", 11);',
77 "}",
78 "",
79 'let stdinContent = "";',
80 "if (!process.stdin.isTTY) {",
81 " const chunks = [];",
82 " for await (const chunk of process.stdin) {",
83 " chunks.push(chunk);",
84 " }",
85 ' stdinContent = Buffer.concat(chunks).toString("utf8");',
86 "}",
87 "",
88 "const argsMustContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN;",
89 "if (argsMustContain && !args.includes(argsMustContain)) {",
90 ' fail("args missing expected content", 2);',
91 "}",
92 "",
93 "const argsMustNotContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN;",
94 "if (argsMustNotContain && args.includes(argsMustNotContain)) {",
95 ' fail("args contained forbidden content", 3);',
96 "}",
97 "",
98 "const stdinMustContain = process.env.T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN;",
99 "if (stdinMustContain && !stdinContent.includes(stdinMustContain)) {",
100 ' fail("stdin missing expected content", 4);',
101 "}",
102 "",
103 "const configDirMustBe = process.env.T3_FAKE_CLAUDE_CONFIG_DIR_MUST_BE;",
104 "if (configDirMustBe && process.env.CLAUDE_CONFIG_DIR !== configDirMustBe) {",
105 ' fail("CLAUDE_CONFIG_DIR was " + (process.env.CLAUDE_CONFIG_DIR ?? ""), 5);',
106 "}",
107 "",
108 "const stderrText = process.env.T3_FAKE_CLAUDE_STDERR;",
109 "if (stderrText) {",
110 ' process.stderr.write(stderrText + "\\n");',
111 "}",
112 "",
113 'process.stdout.write(process.env.T3_FAKE_CLAUDE_OUTPUT ?? "");',
114 "process.exitCode = Number(process.env.T3_FAKE_CLAUDE_EXIT_CODE ?? 0);",
115 "",
116 ].join("\n"),
48 source,
11749 });
11850 return binDir;
11951 });
apps/server/src/textGeneration/testing/ClaudeTextGeneration.fixture.mjs created+72
......@@ -0,0 +1,72 @@
1import * as NodeFS from "node:fs";
2
3const argv = process.argv.slice(2);
4const args = argv.join(" ");
5
6function fail(message, code) {
7 process.stderr.write(message + "\n");
8 process.exit(code);
9}
10
11const permissionIndex = argv.indexOf("--permission-mode");
12if (permissionIndex === -1 || argv[permissionIndex + 1] !== "dontAsk") {
13 fail("text generation must deny permission prompts", 12);
14}
15const toolsIndex = argv.indexOf("--tools");
16if (toolsIndex === -1 || argv[toolsIndex + 1] !== "") {
17 fail("text generation must receive an explicit empty tool set", 6);
18}
19if (argv.includes("--dangerously-skip-permissions")) {
20 fail("text generation must not bypass permissions", 7);
21}
22if (!argv.includes("--disable-slash-commands")) {
23 fail("text generation must disable skills", 8);
24}
25if (!argv.includes("--strict-mcp-config")) {
26 fail("text generation must not load configured MCP servers", 9);
27}
28const settingsIndex = argv.indexOf("--settings");
29if (settingsIndex === -1 || JSON.parse(argv[settingsIndex + 1]).disableAllHooks !== true) {
30 fail("text generation must disable hooks", 10);
31}
32const cwdMustNotBe = process.env.T3_FAKE_CLAUDE_CWD_MUST_NOT_BE;
33if (cwdMustNotBe && NodeFS.realpathSync(process.cwd()) === NodeFS.realpathSync(cwdMustNotBe)) {
34 fail("text generation ran in the project directory", 11);
35}
36
37let stdinContent = "";
38if (!process.stdin.isTTY) {
39 const chunks = [];
40 for await (const chunk of process.stdin) {
41 chunks.push(chunk);
42 }
43 stdinContent = Buffer.concat(chunks).toString("utf8");
44}
45
46const argsMustContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_CONTAIN;
47if (argsMustContain && !args.includes(argsMustContain)) {
48 fail("args missing expected content", 2);
49}
50
51const argsMustNotContain = process.env.T3_FAKE_CLAUDE_ARGS_MUST_NOT_CONTAIN;
52if (argsMustNotContain && args.includes(argsMustNotContain)) {
53 fail("args contained forbidden content", 3);
54}
55
56const stdinMustContain = process.env.T3_FAKE_CLAUDE_STDIN_MUST_CONTAIN;
57if (stdinMustContain && !stdinContent.includes(stdinMustContain)) {
58 fail("stdin missing expected content", 4);
59}
60
61const configDirMustBe = process.env.T3_FAKE_CLAUDE_CONFIG_DIR_MUST_BE;
62if (configDirMustBe && process.env.CLAUDE_CONFIG_DIR !== configDirMustBe) {
63 fail("CLAUDE_CONFIG_DIR was " + (process.env.CLAUDE_CONFIG_DIR ?? ""), 5);
64}
65
66const stderrText = process.env.T3_FAKE_CLAUDE_STDERR;
67if (stderrText) {
68 process.stderr.write(stderrText + "\n");
69}
70
71process.stdout.write(process.env.T3_FAKE_CLAUDE_OUTPUT ?? "");
72process.exitCode = Number(process.env.T3_FAKE_CLAUDE_EXIT_CODE ?? 0);