Repository navigation
Expand file tree
/
Copy pathClaudeExecutable.test.ts
More file actions
124 lines (113 loc) · 4.2 KB
/
Copy pathClaudeExecutable.test.ts
File metadata and controls
124 lines (113 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { describe, expect, it } from "@effect/vitest";
import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
import { SpawnExecutableResolution } from "@t3tools/shared/shell";
import * as Effect from "effect/Effect";
import { ClaudeExecutableFileCheck, resolveClaudeSdkExecutablePath } from "./ClaudeExecutable.ts";
const NPM_DIR = "C:\\Users\\dev\\AppData\\Roaming\\npm";
const NPM_SHIM = `${NPM_DIR}\\claude.cmd`;
const NPM_PACKAGE_EXE = `${NPM_DIR}\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe`;
const NPM_PACKAGE_CLI = `${NPM_DIR}\\node_modules\\@anthropic-ai\\claude-code\\cli.js`;
function withWindowsResolution(input: {
readonly resolvedCommand: string | undefined;
readonly existingFiles?: ReadonlyArray<string>;
}) {
const existing = new Set(input.existingFiles ?? []);
return <A, E, R>(effect: Effect.Effect<A, E, R>) =>
effect.pipe(
Effect.provideService(HostProcessPlatform, "win32"),
Effect.provideService(SpawnExecutableResolution, () => input.resolvedCommand),
Effect.provideService(ClaudeExecutableFileCheck, (filePath) => existing.has(filePath)),
);
}
describe("resolveClaudeSdkExecutablePath", () => {
it.effect("returns the configured path unchanged on non-Windows platforms", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
Effect.provideService(HostProcessPlatform, "darwin"),
Effect.provideService(SpawnExecutableResolution, () => {
throw new Error("must not resolve on non-Windows platforms");
}),
),
).toBe("claude");
}),
);
it.effect("returns the resolved absolute path for native Windows executables", () =>
Effect.gen(function* () {
const nativeBinary = "C:\\Users\\dev\\.local\\bin\\claude.exe";
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({ resolvedCommand: nativeBinary }),
),
).toBe(nativeBinary);
}),
);
it.effect("follows an npm launcher shim to the packaged native binary", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({
resolvedCommand: NPM_SHIM,
existingFiles: [NPM_PACKAGE_EXE, NPM_PACKAGE_CLI],
}),
),
).toBe(NPM_PACKAGE_EXE);
}),
);
it.effect("follows .bat and .ps1 launcher shims the same way", () =>
Effect.gen(function* () {
for (const shim of [`${NPM_DIR}\\claude.bat`, `${NPM_DIR}\\claude.ps1`]) {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({
resolvedCommand: shim,
existingFiles: [NPM_PACKAGE_EXE],
}),
),
).toBe(NPM_PACKAGE_EXE);
}
}),
);
it.effect("normalizes mixed-case shim extensions before matching", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({
resolvedCommand: `${NPM_DIR}\\claude.CMD`,
existingFiles: [NPM_PACKAGE_EXE],
}),
),
).toBe(NPM_PACKAGE_EXE);
}),
);
it.effect("falls back to cli.js when the package ships no native binary", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({
resolvedCommand: NPM_SHIM,
existingFiles: [NPM_PACKAGE_CLI],
}),
),
).toBe(NPM_PACKAGE_CLI);
}),
);
it.effect("returns the configured path when a shim has no known package entry", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({ resolvedCommand: NPM_SHIM }),
),
).toBe("claude");
}),
);
it.effect("returns the configured path when command resolution finds nothing", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({ resolvedCommand: undefined }),
),
).toBe("claude");
}),
);
});