Files
rea/src/application/ClientConfigurationDocument.test.ts
T
Iwakurakaoru0822andClaude Opus 5.5 69d8afefc1 fix(setup): register OpenCode V2 configurations in mcp.servers
OpenCode V2 reads native server entries from mcp.servers, and a native
entry takes precedence over a V1 mcp.<name> entry. After OpenCode
migrated a configuration, setup wrote an ignored V1 entry, status
reported REA as missing, and uninstall left the native entry in place.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 21:58:20 -07:00

116 lines
3.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
clientConfigurationServersKey,
clientRegistrationEntry,
parseClientConfiguration,
serializeClientConfiguration,
} from "./ClientConfigurationDocument.js";
describe.each([
"json",
"vscode",
"copilot_cli",
"opencode",
"commandcode",
] as const)("%s client configuration BOM handling", (format) => {
const serversKey = clientConfigurationServersKey(format);
it.each(["", "\uFEFF"])("parses a document with prefix %j", (bom) => {
const registration = clientRegistrationEntry(format, ["rea", "mcp"], {});
const document = { [serversKey]: { rea: registration } };
expect(
parseClientConfiguration(`${bom}${JSON.stringify(document)}`, format),
).toEqual({
document,
servers: document[serversKey],
serversPath: [serversKey],
dialect: format,
legacyServers: {},
});
});
it.each(["", "\uFEFF"])(
"reports malformed JSON at the original offset with prefix %j",
(bom) => {
const text = `${bom}{\r\n "${serversKey}": {"rea": @}\r\n}`;
expect(() => parseClientConfiguration(text, format)).toThrow(
new SyntaxError(
`Invalid JSON/JSONC at offset ${text.indexOf("@")}: InvalidSymbol`,
),
);
},
);
it.each([
["\uFEFF\uFEFF{}", 1],
[" \uFEFF{}", 1],
['{"setting":\uFEFFtrue}', 11],
['\uFEFF{"setting":\uFEFFtrue}', 12],
] as const)("rejects a nonleading BOM in %j", (text, offset) => {
expect(() => parseClientConfiguration(text, format)).toThrow(
new SyntaxError(`Invalid JSON/JSONC at offset ${offset}: InvalidSymbol`),
);
});
it.each(["", "\uFEFF"])(
"preserves prefix %j and unrelated bytes across add, update, and removal",
(bom) => {
const prefix = `${bom}{\r\n // Keep \uFEFF in comments.\r\n "label": "left\uFEFFright",\r\n "${serversKey}": {\r\n // Other server.\r\n "other": {\r\n "command": "other\uFEFFtool"\r\n },\r\n`;
const suffix = ' },\r\n "theme": "dark",\r\n}\r\n';
const original = `${prefix}${suffix}`;
let text = original;
for (const registration of [
clientRegistrationEntry(format, ["rea", "mcp"], {}),
clientRegistrationEntry(format, ["rea-next", "mcp"], {
LABEL: "left\uFEFFright",
}),
undefined,
]) {
const parsed = parseClientConfiguration(text, format);
const servers = { ...parsed.servers };
if (registration === undefined) delete servers.rea;
else servers.rea = registration;
const document = { ...parsed.document, [serversKey]: servers };
text = serializeClientConfiguration(document, format, text, [
[serversKey, "rea"],
]);
expect(parseClientConfiguration(text, format)).toEqual({
document,
servers,
serversPath: [serversKey],
dialect: format,
legacyServers: {},
});
expect(Buffer.from(text.slice(0, prefix.length))).toEqual(
Buffer.from(prefix),
);
expect(Buffer.from(text.slice(-suffix.length))).toEqual(
Buffer.from(suffix),
);
expect(text.replaceAll("\r\n", "")).not.toContain("\n");
}
expect(text).toBe(original);
},
);
});
describe("TOML client configuration BOM handling", () => {
it("retains the TOML parser's existing leading-BOM rejection", () => {
const text = '[mcp_servers.rea]\ncommand = "rea"\n';
expect(parseClientConfiguration(text, "toml").servers).toEqual({
rea: { command: "rea" },
});
expect(() => parseClientConfiguration(`\uFEFF${text}`, "toml")).toThrow(
"Invalid TOML document",
);
});
});