157 lines · 4.9 KB · TypeScript
RawDownload
import { describe, expect, it } from "@effect/vitest";import type { ModelCapabilities } from "@t3tools/contracts";import { HostProcessPlatform } from "@t3tools/shared/hostProcess";import { createModelCapabilities } from "@t3tools/shared/model";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as PlatformError from "effect/PlatformError";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import {  isCommandMissingCause,  providerModelsFromSettings,  spawnAndCollect,} from "./providerSnapshot.ts"; const OPENCODE_CUSTOM_MODEL_CAPABILITIES: ModelCapabilities = createModelCapabilities({  optionDescriptors: [    {      id: "variant",      label: "Reasoning",      type: "select",      options: [{ id: "medium", label: "Medium", isDefault: true }],      currentValue: "medium",    },    {      id: "agent",      label: "Agent",      type: "select",      options: [{ id: "build", label: "Build", isDefault: true }],      currentValue: "build",    },  ],}); describe("providerModelsFromSettings", () => {  it("applies the provided capabilities to custom models", () => {    const models = providerModelsFromSettings(      [],      ["openai/gpt-5"],      OPENCODE_CUSTOM_MODEL_CAPABILITIES,    );     expect(models).toEqual([      {        slug: "openai/gpt-5",        name: "openai/gpt-5",        isCustom: true,        capabilities: OPENCODE_CUSTOM_MODEL_CAPABILITIES,      },    ]);  });   it("keeps an entry's own name and capabilities over the driver default", () => {    const capabilities = createModelCapabilities({      optionDescriptors: [{ id: "fastMode", label: "Fast Mode", type: "boolean" }],    });    const models = providerModelsFromSettings(      [],      ["bare", { slug: "named", name: "Named", capabilities }],      OPENCODE_CUSTOM_MODEL_CAPABILITIES,    );     expect(models).toEqual([      {        slug: "bare",        name: "bare",        isCustom: true,        capabilities: OPENCODE_CUSTOM_MODEL_CAPABILITIES,      },      { slug: "named", name: "Named", isCustom: true, capabilities },    ]);  });   it("preserves a custom slug that collides with a provider alias", () => {    const capabilities = createModelCapabilities({ optionDescriptors: [] });    const models = providerModelsFromSettings(      [        {          slug: "claude-opus-4-8",          name: "Claude Opus 4.8",          isCustom: false,          capabilities,        },      ],      [" opus "],      capabilities,    );     expect(models.map((model) => model.slug)).toEqual(["claude-opus-4-8", "opus"]);    expect(models[1]?.isCustom).toBe(true);  });}); describe("ProviderCommandNotFoundError", () => {  it("classifies normalized platform failures without parsing messages", () => {    expect(      isCommandMissingCause(        PlatformError.systemError({          _tag: "NotFound",          module: "ChildProcess",          method: "spawn",          description: "arbitrary host detail",        }),      ),    ).toBe(true);    expect(isCommandMissingCause(new Error("spawn provider ENOENT"))).toBe(false);  });   it.effect("retains safe failed-command diagnostics without process output", () => {    const stderr = "'codex' is not recognized: secret-token-value";    const spawner = ChildProcessSpawner.make(() =>      Effect.succeed(        ChildProcessSpawner.makeHandle({          pid: ChildProcessSpawner.ProcessId(1),          exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(9009)),          isRunning: Effect.succeed(false),          kill: () => Effect.void,          unref: Effect.succeed(Effect.void),          stdin: Sink.drain,          stdout: Stream.empty,          stderr: Stream.encodeText(Stream.make(stderr)),          all: Stream.empty,          getInputFd: () => Sink.drain,          getOutputFd: () => Stream.empty,        }),      ),    );    return Effect.gen(function* () {      const error = yield* spawnAndCollect(        "C:\\tools\\codex.cmd",        ChildProcess.make("codex", ["--version"]),      ).pipe(        Effect.provide(Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner)),        Effect.provideService(HostProcessPlatform, "win32"),        Effect.flip,      );       if (error._tag !== "ProviderCommandNotFoundError") {        throw new Error(`Unexpected error: ${error._tag}`);      }       expect(error.binaryPath).toBe("C:\\tools\\codex.cmd");      expect(error.exitCode).toBe(9009);      expect(error.stdoutLength).toBe(0);      expect(error.stderrLength).toBe(stderr.length);      expect(error.message).toBe(        "Provider command C:\\tools\\codex.cmd was not found (exit code 9009).",      );      expect(isCommandMissingCause(error)).toBe(true);      expect(error).not.toHaveProperty("stdout");      expect(error).not.toHaveProperty("stderr");      expect(error.message).not.toContain("secret-token-value");    });  });});