Files
rea/tests/acceptance/setup/opencodeV2ClientSetup.test.ts
Haoran Xu c592ea6bac fix(opencode): override inherited disabled flags (#3469)
Set disabled to false in generated OpenCode V2 registrations so a
lower-priority disabled entry cannot keep approved setup stale.

Cover layered JSON/JSONC refreshes, idempotent setup and doctor readiness,
exact backups, preserved lower settings, malformed-config refusal, and
the exclusion of disabled registrations from automatic maintenance.
Update the native V2 registration expectations.

Fixes #3318
2026-10-23 02:33:38 +08:00

155 lines
4.9 KiB
TypeScript

import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { parse } from "jsonc-parser";
import { expect } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
const setupArguments = [
"setup",
"--client",
"opencode",
"--skill=false",
"--yes",
"--json",
];
const layeredOpenCode = async (
cli: TestCli,
disabled: boolean,
jsonc: boolean,
) => {
const home = await createTestTempDirectory("rea-opencode-v2-activation-");
const configurationHome = join(home, "config");
const directory = join(configurationHome, "opencode");
await mkdir(directory, { recursive: true });
const lower = join(directory, "config.json");
const upper = join(directory, jsonc ? "opencode.jsonc" : "opencode.json");
const other = { type: "local", command: ["other-server"], disabled: true };
const originalLower = JSON.stringify({
mcp: {
servers: {
rea: {
type: "local",
command: ["rea", "mcp"],
disabled,
environment: { REA_MCP_OUTPUT_SCHEMA_PROFILE: "off" },
},
other,
},
},
});
const originalUpper = `${jsonc ? "// Keep the user configuration comment.\n" : ""}${JSON.stringify(
{
mcp: { servers: {} },
theme: "system",
},
)}\n`;
await writeFile(lower, originalLower);
await writeFile(upper, originalUpper);
const run = (arguments_: readonly string[]) =>
cli.run({
arguments: arguments_,
cwd: home,
environment: {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: configurationHome,
APPDATA: join(home, "appdata"),
LOCALAPPDATA: join(home, "localappdata"),
HOPPER_LAUNCHER_PATH: join(home, "absent-hopper"),
},
});
return { lower, upper, originalLower, originalUpper, other, run };
};
for (const disabled of [true, false]) {
for (const jsonc of [false, true]) {
cliTest(
`OpenCode V2 setup activates ${jsonc ? "JSONC" : "JSON"} over a lower disabled=${disabled} registration`,
async ({ cli }) => {
const fixture = await layeredOpenCode(cli, disabled, jsonc);
const setup = await fixture.run(setupArguments);
expect(setup.exitCode, setup.stdout).toBe(0);
expect(setup.json).toMatchObject({ status: "ready" });
const configured = await readFile(fixture.upper, "utf8");
expect(parse(configured)).toMatchObject({
mcp: {
servers: {
rea: {
type: "local",
},
},
},
theme: "system",
});
if (disabled)
expect(parse(configured).mcp.servers.rea.disabled).toBe(false);
if (jsonc)
expect(configured).toContain(
"// Keep the user configuration comment.",
);
expect(await readFile(fixture.lower, "utf8")).toBe(
fixture.originalLower,
);
expect(await readFile(`${fixture.upper}.rea.backup`, "utf8")).toBe(
fixture.originalUpper,
);
const repeated = await fixture.run(setupArguments);
expect(repeated.exitCode, repeated.stdout).toBe(0);
expect(repeated.json).toMatchObject({
status: "ready",
plannedActions: [],
appliedActions: [],
});
const doctor = await fixture.run([
"doctor",
"--client",
"opencode",
"--json",
]);
expect(doctor.exitCode, doctor.stdout).toBe(0);
expect(doctor.json).toMatchObject({
healthy: true,
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({ client: "opencode", state: "aligned" }),
]),
},
});
expect(await readFile(fixture.upper, "utf8")).toBe(configured);
expect(await readFile(fixture.lower, "utf8")).toBe(
fixture.originalLower,
);
expect(
parse(await readFile(fixture.lower, "utf8")).mcp.servers.other,
).toEqual(fixture.other);
},
);
}
}
cliTest(
"OpenCode V2 activation does not bypass a malformed lower configuration",
async ({ cli }) => {
const fixture = await layeredOpenCode(cli, true, true);
const malformed = "{ not json";
await writeFile(fixture.lower, malformed);
const setup = await fixture.run(setupArguments);
expect(setup.exitCode, setup.stdout).toBe(1);
expect(setup.json).toMatchObject({
status: "needs_human",
plannedActions: [],
appliedActions: [],
remediation: expect.stringContaining(fixture.lower),
});
expect(await readFile(fixture.lower, "utf8")).toBe(malformed);
expect(await readFile(fixture.upper, "utf8")).toBe(fixture.originalUpper);
await expect(access(`${fixture.upper}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
},
);