reduce testing
CI / verify (pull_request) Successful in 8s

This commit is contained in:
2026-10-08 09:34:13 +02:00
parent 6f86d1af28
commit 59da616bc5
13 changed files with 299 additions and 2318 deletions
+39 -200
View File
@@ -1,15 +1,5 @@
import { afterEach, expect, test } from "bun:test";
import {
chmod,
mkdir,
mkdtemp,
readFile,
readdir,
rm,
stat,
symlink,
writeFile,
} from "node:fs/promises";
import { chmod, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { createHash } from "node:crypto";
@@ -24,7 +14,11 @@ const unrelated = {
permissions: { shell: "ask" },
mcp: {
servers: {
codegraph: { type: "local", command: ["user-codegraph"], codemode: true },
codegraph: {
type: "local",
command: ["user-codegraph"],
codemode: true,
},
browser: { type: "local", command: ["user-browser"] },
},
},
@@ -83,13 +77,20 @@ async function fixture() {
join(source, "dist", "tui.js"),
'export default { id: "stanp-opencode.quota-tui", setup() {} };',
);
const built = await Bun.build({ entrypoints: [helper.pathname], target: "bun", format: "esm" });
const built = await Bun.build({
entrypoints: [helper.pathname],
target: "bun",
format: "esm",
});
expect(built.success).toBe(true);
await Bun.write(join(source, "dist", "install.js"), built.outputs[0]!);
await writeFile(join(source, "package.json"), '{"type":"module"}');
await writeFile(
join(config, "opencode.json"),
JSON.stringify({ ...unrelated, plugins: [{ package: "before", options: { keep: true } }] }),
JSON.stringify({
...unrelated,
plugins: [{ package: "before", options: { keep: true } }],
}),
);
await writeFile(join(config, "opencode.jsonc"), "// untouched user config\n{}\n");
return { directory, source, config };
@@ -98,20 +99,13 @@ async function contents(config: string) {
return JSON.parse(await readFile(join(config, "opencode.json"), "utf8"));
}
for (const selection of [
"agents",
"quota",
"agents,quota",
"agents,multi-work",
"agents,multi-work,quota",
"optchat",
"agents,multi-work,quota,optchat",
]) {
for (const selection of ["quota", "agents,multi-work,quota,optchat"]) {
test(`installs valid subset ${selection}, preserving unrelated config and MCP`, async () => {
const f = await fixture();
await install(f.source, f.config, selection);
const result = await contents(f.config);
expect(result).toMatchObject(unrelated);
expect(result.experimental === undefined).toBe(selection === "quota");
expect(result.plugins.map((entry: { package: string }) => entry.package)).toEqual([
"before",
...selection.split(",").map((name) => `./plugins/stanp-opencode/${name}`),
@@ -148,96 +142,7 @@ test("upgrades retain selection and options; explicit selection replaces only ow
]);
});
test("mixed string/object registrations upgrade without flags and preserve unrelated entries verbatim", async () => {
const f = await fixture();
const before = "unrelated-plugin";
const after = { package: "unrelated-object", options: { keep: [1, true, "value"] } };
await writeFile(
join(f.config, "opencode.json"),
JSON.stringify({
...unrelated,
plugins: [before, { package: "./plugins/stanp-opencode/quota" }, after],
}),
);
await install(f.source, f.config);
expect((await contents(f.config)).plugins).toEqual([
before,
{ package: "./plugins/stanp-opencode/quota" },
after,
]);
await install(f.source, f.config, "agents,quota");
const updated = await contents(f.config);
expect(updated.plugins).toEqual([
before,
{
package: "./plugins/stanp-opencode/agents",
options: empty,
},
{ package: "./plugins/stanp-opencode/quota" },
after,
]);
expect(updated).toMatchObject(unrelated);
});
test("owned string registrations retain their selection and explicit selection removes mixed duplicates", async () => {
const f = await fixture();
await writeFile(
join(f.config, "opencode.json"),
JSON.stringify({
plugins: [
"before",
"./plugins/stanp-opencode/agents",
"./plugins/stanp-opencode/multi-work",
"./plugins/stanp-opencode/quota",
{ package: "stanp-opencode/quota" },
"after",
],
}),
);
await install(f.source, f.config);
const retained = await contents(f.config);
expect(retained.plugins).toEqual([
"before",
{
package: "./plugins/stanp-opencode/agents",
options: empty,
},
{ package: "./plugins/stanp-opencode/multi-work" },
{ package: "./plugins/stanp-opencode/quota" },
"after",
]);
await install(f.source, f.config, "quota");
expect((await contents(f.config)).plugins).toEqual([
"before",
{ package: "./plugins/stanp-opencode/quota" },
"after",
]);
});
for (const target of ["./plugins/stanp-opencode", "stanp-opencode@1.0.0", "absolute"]) {
test(`owned monolith string ${target} upgrades without a selection`, async () => {
const f = await fixture();
const packageName =
target === "absolute" ? join(f.config, "plugins", "stanp-opencode") : target;
await writeFile(
join(f.config, "opencode.json"),
JSON.stringify({ plugins: ["before", packageName, "after"] }),
);
await install(f.source, f.config);
expect((await contents(f.config)).plugins).toEqual([
"before",
{
package: "./plugins/stanp-opencode/agents",
options: empty,
},
{ package: "./plugins/stanp-opencode/multi-work" },
{ package: "./plugins/stanp-opencode/quota" },
"after",
]);
});
}
test("installer writes empty option skeletons, fills only missing keys, and is idempotent", async () => {
test("installer writes empty option skeletons and subagent_depth, fills only missing keys, and is idempotent", async () => {
const f = await fixture();
await install(f.source, f.config, "agents,multi-work,quota,optchat");
const fresh = await contents(f.config);
@@ -250,7 +155,11 @@ test("installer writes empty option skeletons, fills only missing keys, and is i
options: { compactionModel: "", dataDir: "" },
},
]);
fresh.plugins[1].options = { agents: { "fast-lo": "openai/gpt-6-luna#low" }, extra: 1 };
expect(fresh.experimental).toEqual({ subagent_depth: 2 });
fresh.plugins[1].options = {
agents: { "fast-lo": "openai/gpt-6-luna#low" },
extra: 1,
};
fresh.plugins[4].options = { compactionModel: "acme/cheap" };
await writeFile(join(f.config, "opencode.json"), JSON.stringify(fresh));
await install(f.source, f.config);
@@ -259,40 +168,35 @@ test("installer writes empty option skeletons, fills only missing keys, and is i
agents: { ...empty.agents, "fast-lo": "openai/gpt-6-luna#low" },
extra: 1,
});
expect(filled.plugins[4].options).toEqual({ compactionModel: "acme/cheap", dataDir: "" });
expect(filled.plugins[4].options).toEqual({
compactionModel: "acme/cheap",
dataDir: "",
});
expect(filled).toMatchObject(unrelated);
await install(f.source, f.config);
expect(await contents(f.config)).toEqual(filled);
});
test("agents installs set experimental.subagent_depth to at least 2", async () => {
const f = await fixture();
await install(f.source, f.config, "quota");
expect((await contents(f.config)).experimental).toBeUndefined();
await install(f.source, f.config, "agents");
const fresh = await contents(f.config);
expect(fresh.experimental).toEqual({ subagent_depth: 2 });
expect(fresh).toMatchObject(unrelated);
for (const [depth, expected] of [
[1, 2],
[3, 3],
]) {
await writeFile(
join(f.config, "opencode.json"),
JSON.stringify({ ...fresh, experimental: { subagent_depth: depth, other: true } }),
JSON.stringify({
...filled,
experimental: { subagent_depth: depth, other: true },
}),
);
await install(f.source, f.config);
const result = await contents(f.config);
expect(result.experimental).toEqual({ subagent_depth: expected, other: true });
await install(f.source, f.config);
expect(await contents(f.config)).toEqual(result);
expect((await contents(f.config)).experimental).toEqual({
subagent_depth: expected,
other: true,
});
}
});
test("local installer retains executable permissions and supports direct invocation", async () => {
test("local installer supports direct invocation", async () => {
const f = await fixture();
const path = new URL("../install-local.sh", import.meta.url).pathname;
expect((await stat(path)).mode & 0o777).toBe(0o755);
const result = await run([path, "--plugins", "multi-work"], f.directory);
expect(result.exitCode).toBe(2);
expect(result.stderr).toContain("Multi-work requires Agents");
@@ -348,43 +252,6 @@ test("noninteractive fresh installs require a selection; invalid selections leav
expect(await readFile(join(f.config, "opencode.json"), "utf8")).toBe(original);
});
test("interactive Multi-work-only selection offers Agents", async () => {
const f = await fixture();
const command = `${process.execPath} '${helper.pathname}' '${f.source}' '${f.config}'`;
const child = Bun.spawn(["script", "-qec", command, "/dev/null"], {
cwd: f.directory,
stdin: "pipe",
stdout: "pipe",
stderr: "pipe",
timeout: 10_000,
});
let output = "";
let selected = false;
let confirmed = false;
const reader = child.stdout.getReader();
while (true) {
const chunk = await reader.read();
if (chunk.done) break;
output += new TextDecoder().decode(chunk.value);
if (!selected && output.includes("Plugins (")) {
child.stdin.write("multi-work\n");
selected = true;
}
if (!confirmed && output.includes("Add Agents?")) {
child.stdin.write("y\n");
confirmed = true;
}
}
expect(await child.exited, output).toBe(0);
child.stdin.end();
expect(output).toContain("Add Agents?");
expect(
(await contents(f.config)).plugins
.slice(1)
.map((entry: { package: string }) => entry.package),
).toEqual(["./plugins/stanp-opencode/agents", "./plugins/stanp-opencode/multi-work"]);
});
test("artifact validation failure leaves prior installation and config intact", async () => {
const f = await fixture();
await install(f.source, f.config, "quota");
@@ -406,36 +273,6 @@ test("artifact validation failure leaves prior installation and config intact",
expect(await readdir(join(f.config, "plugins"))).toEqual(["stanp-opencode"]);
});
test("replaces an owned installation symlink without touching its target or unrelated plugin directories", async () => {
const f = await fixture();
const external = join(f.directory, "old installation");
const sibling = join(f.config, "plugins", "unrelated");
await mkdir(external);
await mkdir(sibling, { recursive: true });
await writeFile(join(external, "keep.txt"), "old work is untouched");
await writeFile(join(sibling, "index.js"), "unrelated plugin");
await symlink(external, join(f.config, "plugins", "stanp-opencode"));
await install(f.source, f.config, "quota");
expect(await readFile(join(external, "keep.txt"), "utf8")).toBe("old work is untouched");
expect(await readFile(join(sibling, "index.js"), "utf8")).toBe("unrelated plugin");
expect(
await Bun.file(join(f.config, "plugins", "stanp-opencode", "quota", "index.js")).exists(),
).toBe(true);
});
test("both shell installers reject explicit Multi-work-only selection before doing work", async () => {
const f = await fixture();
for (const file of ["install.sh", "install-local.sh"]) {
const result = await run(
["bash", new URL(`../${file}`, import.meta.url).pathname, "--plugins", "multi-work"],
f.directory,
);
expect(result.exitCode).toBe(2);
expect(result.stderr).toContain("Multi-work requires Agents");
}
expect((await contents(f.config)).plugins).toHaveLength(1);
});
test("local shell installer builds its checkout and upgrades mixed entries without flags", async () => {
const f = await fixture();
const after = { package: "unrelated-object", options: { keep: true } };
@@ -481,7 +318,9 @@ for (const validChecksum of [true, false])
if (validChecksum)
await writeFile(
join(f.config, "opencode.json"),
JSON.stringify({ plugins: ["unrelated-plugin", "./plugins/stanp-opencode/quota"] }),
JSON.stringify({
plugins: ["unrelated-plugin", "./plugins/stanp-opencode/quota"],
}),
);
const archiveRoot = join(f.directory, "archive");
await mkdir(archiveRoot);