test: add coverage for listK8sModels CLI fetch and fallback
chore: bump version to 0.1.32 Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "paperclip-adapter-opencode-k8s",
|
"name": "paperclip-adapter-opencode-k8s",
|
||||||
"version": "0.1.31",
|
"version": "0.1.32",
|
||||||
"description": "Paperclip adapter plugin that runs OpenCode agents as Kubernetes Jobs",
|
"description": "Paperclip adapter plugin that runs OpenCode agents as Kubernetes Jobs",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
const execMock = vi.fn();
|
||||||
|
|
||||||
|
vi.mock("child_process", () => ({
|
||||||
|
exec: (cmd: string, opts: unknown, cb: (err: Error | null, result: { stdout: string; stderr: string }) => void) => {
|
||||||
|
execMock(cmd, opts, cb);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { listK8sModels } = await import("./models.js");
|
||||||
|
|
||||||
|
function mockExecResult(stdout: string) {
|
||||||
|
execMock.mockImplementation((_cmd, _opts, cb) => {
|
||||||
|
cb(null, { stdout, stderr: "" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockExecError(err: Error) {
|
||||||
|
execMock.mockImplementation((_cmd, _opts, cb) => {
|
||||||
|
cb(err, { stdout: "", stderr: "" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("listK8sModels", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
execMock.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses opencode models output into AdapterModel entries", async () => {
|
||||||
|
mockExecResult(
|
||||||
|
[
|
||||||
|
"anthropic/claude-opus-4-7",
|
||||||
|
"openai/gpt-4o",
|
||||||
|
"google/gemini-2.5-pro",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const models = await listK8sModels();
|
||||||
|
|
||||||
|
expect(models).toHaveLength(3);
|
||||||
|
expect(models[0]).toEqual({ id: "anthropic/claude-opus-4-7", label: "claude opus 4 7" });
|
||||||
|
expect(models[1]).toEqual({ id: "openai/gpt-4o", label: "gpt 4o" });
|
||||||
|
expect(models[2]).toEqual({ id: "google/gemini-2.5-pro", label: "gemini 2.5 pro" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores blank lines and trims whitespace", async () => {
|
||||||
|
mockExecResult("\nanthropic/claude-opus-4-7\n\n openai/gpt-4o \n\n");
|
||||||
|
|
||||||
|
const models = await listK8sModels();
|
||||||
|
|
||||||
|
expect(models.map((m) => m.id)).toEqual([
|
||||||
|
"anthropic/claude-opus-4-7",
|
||||||
|
"openai/gpt-4o",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invokes opencode models with a timeout", async () => {
|
||||||
|
mockExecResult("anthropic/claude-opus-4-7");
|
||||||
|
|
||||||
|
await listK8sModels();
|
||||||
|
|
||||||
|
expect(execMock).toHaveBeenCalledTimes(1);
|
||||||
|
const [cmd, opts] = execMock.mock.calls[0];
|
||||||
|
expect(cmd).toBe("opencode models");
|
||||||
|
expect(opts).toMatchObject({ timeout: 30_000 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to the static list when the CLI fails", async () => {
|
||||||
|
mockExecError(new Error("ENOENT: opencode not found"));
|
||||||
|
|
||||||
|
const models = await listK8sModels();
|
||||||
|
|
||||||
|
expect(models.length).toBeGreaterThan(0);
|
||||||
|
expect(models.map((m) => m.id)).toContain("anthropic/claude-opus-4-7");
|
||||||
|
expect(models.map((m) => m.id)).toContain("openai/gpt-4o");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user