124 lines · 4.2 KB · TypeScript
RawDownload
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");    }),  );});