| ... | @@ -34,86 +34,18 @@ const ClaudeTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces | ... | @@ -34,86 +34,18 @@ const ClaudeTextGenerationTestLayer = ServerConfig.ServerConfig.layerTest(proces |
| 34 | function makeFakeClaudeBinary(dir: string) { | 34 | function makeFakeClaudeBinary(dir: string) { |
| 35 | return Effect.gen(function* () { | 35 | return Effect.gen(function* () { |
| 36 | const path = yield* Path.Path; | 36 | const path = yield* Path.Path; |
| | 37 | const fs = yield* FileSystem.FileSystem; |
| 37 | const platform = yield* HostProcessPlatform; | 38 | const platform = yield* HostProcessPlatform; |
| 38 | const binDir = path.join(dir, "bin"); | 39 | 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); |
| 39 | writeFakeCli({ | 44 | writeFakeCli({ |
| 40 | directory: binDir, | 45 | directory: binDir, |
| 41 | name: "claude", | 46 | name: "claude", |
| 42 | platform, | 47 | platform, |
| 43 | source: [ | 48 | 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"), | | |
| 117 | }); | 49 | }); |
| 118 | return binDir; | 50 | return binDir; |
| 119 | }); | 51 | }); |