import { expect, test, mock, spyOn, beforeEach, afterEach, describe } from "bun:test"; import * as fs from "node:fs"; import * as os from "node:os"; import multiModelTool from "./multi-model"; let originalBun$: typeof Bun.$; let mockCommandResponses: Record = {}; function setupMockBun$() { const mockFn = mock((strings: TemplateStringsArray, ...values: any[]) => { const parts = values[0] as string[]; const commandSignature = parts.join(" "); return { quiet: () => ({ nothrow: async () => { const response = mockCommandResponses[commandSignature] ?? { ok: true, stdout: "", stderr: "" }; return { exitCode: response.ok ? 0 : 1, stdout: { toString: () => response.stdout }, stderr: { toString: () => response.stderr }, }; }, }), }; }); (globalThis as any).Bun.$ = mockFn; return mockFn; } function restoreOriginalBun$() { (globalThis as any).Bun.$ = originalBun$; } describe("multi-model tool", () => { let existsSyncMock: ReturnType; let bunMock: ReturnType; beforeEach(() => { spyOn(os, "homedir").mockReturnValue("/mock/home"); existsSyncMock = spyOn(fs, "existsSync").mockReturnValue(false); bunMock = setupMockBun$(); mockCommandResponses = { "git rev-parse --is-inside-work-tree": { ok: true, stdout: "true", stderr: "" }, "command -v tmux": { ok: true, stdout: "/usr/bin/tmux", stderr: "" }, "command -v opencode": { ok: true, stdout: "/usr/bin/opencode", stderr: "" }, "opencode models": { ok: true, stdout: "openai/gpt-4o\nanthropic/claude-3-5-sonnet", stderr: "" }, "tmux has-session -t test-session": { ok: false, stdout: "", stderr: "session not found" }, "git show-ref --verify --quiet refs/heads/opencode/test-session/gpt-4o": { ok: false, stdout: "", stderr: "" }, }; }); afterEach(() => { restoreOriginalBun$(); }); test("fails if session name is empty", async () => { const result = await multiModelTool.execute( { sessionName: "", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Error: `sessionName` must not be empty."); }); test("fails if no models are provided", async () => { const result = await multiModelTool.execute( { sessionName: "test-session", models: [] }, { metadata: mock() } as any ); expect(result).toContain("Error: Model names must not be empty."); }); test("fails if duplicate models are provided", async () => { const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o", "openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Duplicate model names are not allowed"); }); test("fails if not inside a git repository", async () => { mockCommandResponses["git rev-parse --is-inside-work-tree"] = { ok: false, stdout: "", stderr: "not a git repo" }; const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Error: `multi-model` requires a git repository."); }); test("fails if tmux is not installed", async () => { mockCommandResponses["command -v tmux"] = { ok: false, stdout: "", stderr: "" }; const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Error: `tmux` is not installed or not on `PATH`."); }); test("fails if opencode is not installed", async () => { mockCommandResponses["command -v opencode"] = { ok: false, stdout: "", stderr: "" }; const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Error: `opencode` is not installed or not on `PATH`."); }); test("fails if model name is not in allowlist", async () => { const result = await multiModelTool.execute( { sessionName: "test-session", models: ["invalid/model"] }, { metadata: mock() } as any ); expect(result).toContain("Error: Model name 'invalid/model' not found"); }); test("fails if session already exists", async () => { mockCommandResponses["tmux has-session -t test-session"] = { ok: true, stdout: "", stderr: "" }; const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Error: Session already exists"); }); test("successfully plans and executes tmux launch with single model", async () => { const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Use `tmux attach -t test-session` to join session"); }); test("successfully launches multiple models", async () => { mockCommandResponses["git show-ref --verify --quiet refs/heads/opencode/test-session/claude-3-5-sonnet"] = { ok: false, stdout: "", stderr: "" }; const result = await multiModelTool.execute( { sessionName: "test-session", models: ["openai/gpt-4o", "anthropic/claude-3-5-sonnet"] }, { metadata: mock() } as any ); expect(result).toContain("Use `tmux attach -t test-session` to join session"); }); test("sanitizes session name", async () => { mockCommandResponses["tmux has-session -t test session!"] = { ok: false, stdout: "", stderr: "" }; const result = await multiModelTool.execute( { sessionName: "test session!", models: ["openai/gpt-4o"] }, { metadata: mock() } as any ); expect(result).toContain("Use `tmux attach -t test session!` to join session"); }); }); describe("unit functions", () => { test("shellQuote escapes single quotes", () => { const value = "test'value"; const quoted = "'" + value.replace(/'/g, `'"'"'`) + "'"; expect(quoted).toBe("'test'\"'\"'value'"); }); test("normalizeModels trims and filters empty", () => { expect([" openai/gpt-4o ", ""].map(m => m.trim()).filter(Boolean)).toEqual(["openai/gpt-4o"]); }); });