Files
stanp-opencode/tests/plugin.test.ts
T
StanPonomarev 04e80929f0
Release / release (push) Failing after 9s
initial
2026-09-09 09:56:21 +02:00

187 lines
7.0 KiB
TypeScript

import { afterAll, beforeAll, expect, test } from "bun:test";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createOpencodeClient } from "@opencode-ai/sdk";
import type { Config, PluginOptions } from "@opencode-ai/plugin";
import plugin from "../src/index.js";
import { z } from "zod";
import { gptAgents } from "../src/config.js";
let directory: string;
const originalModels = process.env.OPENCODE_MODELS_PATH;
const originalAuth = process.env.OPENCODE_AUTH_CONTENT;
beforeAll(async () => {
directory = await mkdtemp(join(tmpdir(), "stanp-tests-"));
const path = join(directory, "models.json");
await writeFile(
path,
JSON.stringify({
test: {
id: "test",
env: [],
npm: "@ai-sdk/openai-compatible",
models: Object.fromEntries(
["gpt-5.6-luna", "gpt-5.6-sol", "gpt-6-astra"].map((id) => [id, { id }]),
),
},
}),
);
process.env.OPENCODE_MODELS_PATH = path;
process.env.OPENCODE_AUTH_CONTENT = "{}";
});
afterAll(async () => {
if (originalModels === undefined) delete process.env.OPENCODE_MODELS_PATH;
else process.env.OPENCODE_MODELS_PATH = originalModels;
if (originalAuth === undefined) delete process.env.OPENCODE_AUTH_CONTENT;
else process.env.OPENCODE_AUTH_CONTENT = originalAuth;
await rm(directory, { recursive: true, force: true });
});
async function hooks(caller = "build", options: PluginOptions = { provider: "test" }) {
return plugin.server(
{
client: createOpencodeClient({
baseUrl: "http://localhost:1",
fetch: async (request) =>
Response.json(
request.url.endsWith("/user")
? { info: { role: "user", agent: caller }, parts: [] }
: [
{
info: {
role: "assistant",
mode: "general",
parentID: "user",
},
parts: [],
},
],
),
}),
project: { id: "test", worktree: directory, time: { created: 0 } },
directory,
worktree: directory,
serverUrl: new URL("http://localhost:1"),
experimental_workspace: { register() {} },
$: Bun.$,
},
options,
);
}
test("registration is atomic; only missing Astra degrades to four workers", async () => {
const full: Config = { provider: { test: {} } };
await (await hooks()).config(full);
// The host's /agent endpoint rejects undefined options rather than JSON-stringifying them.
z.json().parse(full.agent);
expect(Object.keys(full.agent ?? {})).toEqual([
"fast-lo",
"fast-hi",
"good-lo",
"good-hi",
"deep-lo",
"deep-hi",
"coordinate",
]);
const reduced: Config = { provider: { test: { blacklist: ["gpt-6-astra"] } } };
await (await hooks()).config(reduced);
expect(Object.keys(reduced.agent ?? {})).toEqual([
"fast-lo",
"fast-hi",
"good-lo",
"good-hi",
"coordinate",
]);
expect(reduced.agent?.coordinate?.model).toBe("test/gpt-5.6-sol");
for (const missing of ["gpt-5.6-luna", "gpt-5.6-sol"]) {
const config: Config = { provider: { test: { blacklist: [missing] } } };
await (await hooks()).config(config);
expect(config.agent).toBeUndefined();
}
});
test("disconnected and explicitly disabled providers register nothing", async () => {
const disconnected: Config = {};
await (await hooks()).config(disconnected);
expect(disconnected.agent).toBeUndefined();
const disabled: Config = { provider: { test: {} }, disabled_providers: ["test"] };
await (await hooks()).config(disabled);
expect(disabled.agent).toBeUndefined();
});
test("manual task targets cannot bypass coordinate's primary-only restriction", async () => {
const active = await hooks();
await active.config({ provider: { test: {} } });
await expect(
active["tool.execute.before"](
{ tool: "task", sessionID: "test", callID: "test" },
{ args: { subagent_type: "coordinate", prompt: "implement" } },
),
).rejects.toThrow("cannot be delegated to");
});
test("subtask bypass uses the parent user agent, not the synthetic assistant target", async () => {
const coordinate = await hooks("coordinate");
await coordinate.config({ provider: { test: { blacklist: ["gpt-6-astra"] } } });
for (const target of ["general", "deep-lo"]) {
await expect(
coordinate["tool.execute.before"](
{ tool: "task", sessionID: "test", callID: "test" },
{ args: { subagent_type: target } },
),
).rejects.toThrow("only to its available workers");
}
const worker = await hooks("fast-hi");
await worker.config({ provider: { test: {} } });
await expect(
worker["tool.execute.before"](
{ tool: "task", sessionID: "test", callID: "test" },
{ args: { subagent_type: "good-lo" } },
),
).rejects.toThrow("Workers cannot delegate");
});
test("custom assignments do not silently take the GPT fallback", async () => {
const custom = await hooks("build", {
mode: "custom",
provider: "test",
agents: { ...gptAgents, coordinate: { model: "local", effort: "low" } },
});
const config: Config = { provider: { test: { models: { local: { name: "Local" } } } } };
await custom.config(config);
expect(config.agent?.coordinate?.model).toBe("test/local");
expect(config.agent?.coordinate?.options).toEqual({ reasoningEffort: "low" });
const missing: Config = {
provider: { test: { blacklist: ["gpt-6-astra"], models: { local: {} } } },
};
await custom.config(missing);
expect(missing.agent).toBeUndefined();
});
test("tier selection requires evidence and preserves the advertised fast tier", async () => {
const namedFast = { name: "Luna", variants: { fast: { reasoningEffort: "low" } } };
const ordinary: Config = { provider: { test: { models: { "gpt-5.6-luna": namedFast } } } };
await (await hooks()).config(ordinary);
expect(ordinary.agent?.["fast-lo"]?.options).toEqual({ reasoningEffort: "low" });
const advertised: Config = {
provider: {
test: {
models: { "gpt-5.6-luna": { options: { serviceTier: "fast" } } },
},
},
};
await (await hooks()).config(advertised);
expect(advertised.agent?.["fast-lo"]?.options).toEqual({
reasoningEffort: "low",
serviceTier: "fast",
});
});