build: multi-model test setup

This commit is contained in:
2026-03-13 00:08:36 +05:30
parent 5459a6d614
commit 41c00d75b5
2 changed files with 181 additions and 0 deletions
@@ -0,0 +1,111 @@
---
model: google/gemini-3.1-pro-preview
---
# Plan: Lightweight Testing for `multi-model.ts`
## Overview
To test `.opencode/tools/multi-model.ts` without executing actual commands on your filesystem or Git repository, we will use **`bun:test`**.
Since your project is already utilizing Bun (evident from `Bun.$`), `bun:test` is the perfect built-in, zero-dependency, and lightweight testing framework. It comes with Jest-compatible mocking out of the box.
## Mocking Strategy
The tool interacts with the system via three main avenues, all of which we will mock:
1. **Filesystem checks (`node:fs`)**: The tool only uses `fs.existsSync`. We will mock this using `spyOn(fs, 'existsSync')`.
2. **OS details (`node:os`)**: We will mock `os.homedir()` using `spyOn(os, 'homedir')` to return a safe, fake path.
3. **Shell commands (`Bun.$`)**: The tool extensively executes `git`, `tmux`, and `opencode` commands via `Bun.$`${parts}`.quiet().nothrow()`. We can override `globalThis.Bun.$` with a custom mock function that inspects the command parts and returns simulated deterministic outputs (exit code, stdout, stderr).
## Implementation Details
Create a new file `.opencode/tools/multi-model.test.ts` and set up the mocks as follows:
```typescript
import { expect, test, mock, spyOn, beforeEach, afterEach } from "bun:test";
import * as fs from "node:fs";
import * as os from "node:os";
import multiModelTool from "./multi-model";
// 1. Mock OS and FS
spyOn(os, "homedir").mockReturnValue("/mock/home");
const existsSyncMock = spyOn(fs, "existsSync").mockReturnValue(false);
// 2. Mock Bun.$
const originalBun$ = Bun.$;
let mockCommandResponses: Record<string, any> = {};
function setupMockBun$() {
(globalThis as any).Bun.$ = mock((strings: TemplateStringsArray, ...values: any[]) => {
// In Bun.$`${parts}`, parts is an array passed as the first value
const parts = values[0] as string[];
const commandSignature = parts.join(" ");
return {
quiet: () => ({
nothrow: async () => {
// You can define specific responses for specific commands
if (mockCommandResponses[commandSignature]) {
return mockCommandResponses[commandSignature];
}
// Default successful response
return {
exitCode: 0,
stdout: { toString: () => "" },
stderr: { toString: () => "" },
};
},
}),
};
});
}
beforeEach(() => {
setupMockBun$();
existsSyncMock.mockClear();
mockCommandResponses = {
// Provide a default opencode models list for tests
"opencode models": {
exitCode: 0,
stdout: { toString: () => "openai/gpt-4\nanthropic/claude-3" },
stderr: { toString: () => "" }
},
// Mock that session does not exist by default
"tmux has-session -t test-session": {
exitCode: 1, // 1 means session does not exist in tmux
stdout: { toString: () => "" },
stderr: { toString: () => "session not found" }
}
};
});
afterEach(() => {
// Restore original Bun.$
(globalThis as any).Bun.$ = originalBun$;
});
// 3. Write Tests
test("fails if duplicate models are provided", async () => {
const result = await multiModelTool.execute(
{ sessionName: "test-session", models: ["openai/gpt-4", "openai/gpt-4"] },
{ metadata: mock() } as any
);
expect(result).toContain("Duplicate model names are not allowed");
});
test("successfully plans and executes tmux launch", async () => {
const result = await multiModelTool.execute(
{ sessionName: "test-session", models: ["openai/gpt-4", "anthropic/claude-3"] },
{ metadata: mock() } as any
);
expect(result).toContain("Use `tmux attach -t test-session` to join session");
// You can also assert that Bun.$ was called with specific commands
// by inspecting (globalThis as any).Bun$.mock.calls
});
```
## Verification
You can verify and run these tests safely without any system side-effects by running:
```bash
bun test .opencode/tools/multi-model.test.ts
```