mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 11:13:21 +00:00
* fix(setup): name client configuration syntax from one reader table Each client format now maps to one reader description (syntax, strict or JSONC, BOM and empty-file handling, strict-JSON repair). The document parser, the strict-JSON rewrite decision and uninstall's diagnostic all read it, so uninstall no longer re-derives a syntax name that called Goose's YAML and JSONC-admitting clients "JSON". * test(hopper): retry cleanup after confirmed application exit
96 lines
3.2 KiB
TypeScript
96 lines
3.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
ClientConfigurationShapeError,
|
|
clientConfigurationSyntaxName,
|
|
parseClientConfiguration,
|
|
shadowedClientServers,
|
|
} from "./ClientConfigurationDocument.js";
|
|
|
|
describe("parseClientConfiguration shape diagnostics", () => {
|
|
it.each([
|
|
["null root", "null", "root"],
|
|
["array root", "[]", "root"],
|
|
["string root", '"x"', "root"],
|
|
["null server table", '{"mcpServers":null,"keep":1}', "mcpServers"],
|
|
["array server table", '{"mcpServers":[],"keep":1}', "mcpServers"],
|
|
["string server table", '{"mcpServers":"x","keep":1}', "mcpServers"],
|
|
])("reports a non-object %s", (_label, text, location) => {
|
|
let error: unknown;
|
|
try {
|
|
parseClientConfiguration(text, "json");
|
|
} catch (cause: unknown) {
|
|
error = cause;
|
|
}
|
|
expect(error).toBeInstanceOf(ClientConfigurationShapeError);
|
|
if (!(error instanceof Error)) throw new Error("Expected shape error");
|
|
expect(error.message).toContain(location);
|
|
expect(error.message).toContain("object");
|
|
});
|
|
|
|
// The diagnostic syntax name must describe what the reader actually admits.
|
|
it.each([
|
|
["json", "JSONC"],
|
|
["vscode", "JSONC"],
|
|
["opencode", "JSONC"],
|
|
["kilo", "JSONC"],
|
|
["zed", "JSONC"],
|
|
["amp", "JSONC"],
|
|
["copilot_cli", "JSONC"],
|
|
["fx", "JSONC"],
|
|
["claude_code", "JSON"],
|
|
["pi", "JSON"],
|
|
["kimi", "JSON"],
|
|
["lm_studio", "JSON"],
|
|
["zcode", "JSON"],
|
|
["toml", "TOML"],
|
|
["grok", "TOML"],
|
|
["goose", "YAML"],
|
|
["hermes", "YAML"],
|
|
] as const)("names %s configuration syntax %s", (format, name) => {
|
|
expect(clientConfigurationSyntaxName(format)).toBe(name);
|
|
const accepts = (text: string): boolean => {
|
|
try {
|
|
parseClientConfiguration(text, format);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
if (name === "TOML") expect(accepts("keep = 1\n")).toBe(true);
|
|
if (name === "YAML") expect(accepts("keep: 1\n")).toBe(true);
|
|
if (name === "JSON" || name === "JSONC") {
|
|
expect(accepts('{"keep":1}')).toBe(true);
|
|
expect(accepts('{"keep":1,}')).toBe(name === "JSONC");
|
|
expect(accepts('{"keep":1 /* comment */}')).toBe(name === "JSONC");
|
|
}
|
|
});
|
|
|
|
it("still rejects unparsable JSON with SyntaxError", () => {
|
|
expect(() => parseClientConfiguration("not-json", "json")).toThrow(
|
|
SyntaxError,
|
|
);
|
|
});
|
|
|
|
it("reports only duplicates in the effective server object", () => {
|
|
const owned = '{"command":"rea","args":["mcp"]}';
|
|
const activeTable = `{"mcpServers":{"rea":null,"rea":${owned}}}`;
|
|
const active = parseClientConfiguration(activeTable, "json");
|
|
expect(shadowedClientServers(activeTable, active, "rea")).toEqual({
|
|
servers: [null],
|
|
legacyServers: [],
|
|
});
|
|
|
|
// Removing from the effective parent leaves an earlier repeated parent
|
|
// inactive. Its members are not registrations the client would reveal.
|
|
const inactiveParent =
|
|
`{"mcpServers":{"rea":{"command":"/usr/bin/false"}},` +
|
|
`"mcpServers":{"rea":${owned}}}`;
|
|
const parsed = parseClientConfiguration(inactiveParent, "json");
|
|
expect(shadowedClientServers(inactiveParent, parsed, "rea")).toEqual({
|
|
servers: [],
|
|
legacyServers: [],
|
|
});
|
|
});
|
|
});
|