Read shadowed registrations from the effective JSONC syntax tree before
any client is changed. Share the same typed ownership decision with the
reread before removal, including foreign OpenCode V1 fallbacks. Remove
all admitted duplicates using one member/separator planner and grant
serializer authority only for exact admitted paths.
Setup may remove its explicitly superseded OpenCode V1 path. Preserve
inactive repeated parents, unrelated entries, comments and source backups.
Use the pinned JSONC scanner instead of handwritten string decoding.
Validation: final 86 focused cases and 9 setup acceptance cases passed;
104 additional filesystem consumer cases passed. Static/architecture,
docs and discovery checks passed. A broader 186-case run had one OMP
timeout; its isolated 9-case rerun passed, with timeout cause unverified.
Controlled compiled CLI comparisons verify removal/refusal and backups;
real developer-client execution remains unverified.
Adapted from #4977 at 4cd22d4ec2.
Closes #4976
Co-authored-by: Nikhil Bhagat <knewboy.nykhil@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
parseClientConfiguration,
|
|
shadowedClientServers,
|
|
} from "./ClientConfigurationDocument.js";
|
|
|
|
describe("parseClientConfiguration shape diagnostics", () => {
|
|
it.each([
|
|
["null", "null"],
|
|
["array", "[]"],
|
|
["string", '"x"'],
|
|
] as const)("names a non-object JSON root (%s)", (_label, text) => {
|
|
expect(() => parseClientConfiguration(text, "json")).toThrow(
|
|
new TypeError("The configuration root must be an object"),
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
["null", "null"],
|
|
["array", "[]"],
|
|
["string", '"x"'],
|
|
] as const)(
|
|
'names a non-object "mcpServers" value (%s)',
|
|
(_label, servers) => {
|
|
expect(() =>
|
|
parseClientConfiguration(`{"mcpServers":${servers},"keep":1}`, "json"),
|
|
).toThrow(new TypeError('"mcpServers" must be an object'));
|
|
},
|
|
);
|
|
|
|
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: [],
|
|
});
|
|
});
|
|
});
|