139 lines
5.0 KiB
JavaScript
139 lines
5.0 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import {
|
|
addMcpServerToConfigFile,
|
|
installMcpServerConfigToFile,
|
|
launcherPath,
|
|
mcpConfigJson,
|
|
mcpServerConfig,
|
|
readMcpInstallRecord,
|
|
removeLauncher,
|
|
removeMcpServerFromConfigFile,
|
|
uninstallMcpServerConfigs,
|
|
} from "../out/agent/setup.js";
|
|
|
|
test("mcpServerConfig and mcpConfigJson use the stable launcher", () => {
|
|
const config = mcpServerConfig("C:/Users/me/.ra3modxml/ra3-mod-xml-mcp.cmd", "D:/Mods/Example");
|
|
assert.deepEqual(config.mcpServers["ra3-mod-xml"].args, ["--project", "D:/Mods/Example"]);
|
|
const json = mcpConfigJson("C:/launcher.cmd", "D:/Proj");
|
|
assert.ok(json.includes("C:/launcher.cmd"));
|
|
assert.ok(json.includes("D:/Proj"));
|
|
});
|
|
|
|
test("addMcpServerToConfigFile creates and merges config", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "ra3-setup-test-"));
|
|
try {
|
|
const file = join(dir, "mcp.json");
|
|
await addMcpServerToConfigFile(file, "C:/launcher.cmd", "D:/Proj");
|
|
const first = JSON.parse(readFileSync(file, "utf8"));
|
|
assert.ok(first.mcpServers["ra3-mod-xml"]);
|
|
|
|
writeFileSync(file, JSON.stringify({ mcpServers: { other: { command: "x" } } }, null, 2));
|
|
await addMcpServerToConfigFile(file, "C:/launcher.cmd", "D:/Proj");
|
|
const merged = JSON.parse(readFileSync(file, "utf8"));
|
|
assert.ok(merged.mcpServers.other);
|
|
assert.ok(merged.mcpServers["ra3-mod-xml"]);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("removeMcpServerFromConfigFile removes only our entry and preserves the rest", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "ra3-setup-remove-"));
|
|
try {
|
|
const file = join(dir, "mcp.json");
|
|
writeFileSync(
|
|
file,
|
|
JSON.stringify(
|
|
{
|
|
mcpServers: {
|
|
other: { command: "x" },
|
|
"ra3-mod-xml": { command: "launcher", args: ["--project", "D:/Mods/A"] },
|
|
},
|
|
someOtherKey: 1,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
assert.equal(await removeMcpServerFromConfigFile(file), true);
|
|
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
assert.ok(parsed.mcpServers.other);
|
|
assert.equal(parsed.mcpServers["ra3-mod-xml"], undefined);
|
|
assert.equal(parsed.someOtherKey, 1);
|
|
|
|
// Nothing left to remove: second call is a no-op.
|
|
assert.equal(await removeMcpServerFromConfigFile(file), false);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("removeMcpServerFromConfigFile handles VS Code's servers key and drops empty containers", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "ra3-setup-vscode-"));
|
|
try {
|
|
const file = join(dir, "mcp.json");
|
|
writeFileSync(
|
|
file,
|
|
JSON.stringify({ servers: { "ra3-mod-xml": { command: "x" } }, inputs: [] }, null, 2),
|
|
);
|
|
assert.equal(await removeMcpServerFromConfigFile(file), true);
|
|
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
assert.equal(parsed.servers, undefined);
|
|
assert.deepEqual(parsed.inputs, []);
|
|
|
|
// A missing file is "nothing to remove", not an error.
|
|
assert.equal(await removeMcpServerFromConfigFile(join(dir, "nope.json")), false);
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("installed MCP configs can be uninstalled through the install record", async () => {
|
|
const home = mkdtempSync(join(tmpdir(), "ra3-setup-home-"));
|
|
const dir = mkdtempSync(join(tmpdir(), "ra3-setup-record-"));
|
|
try {
|
|
const file = join(dir, "mcp.json");
|
|
await installMcpServerConfigToFile({
|
|
filePath: file,
|
|
launcher: "C:/launcher.cmd",
|
|
projectDir: "D:/Mods/A",
|
|
label: "Test client",
|
|
sourceVersion: "1.2.3",
|
|
agentHome: home,
|
|
});
|
|
|
|
const record = await readMcpInstallRecord(home);
|
|
assert.equal(record.length, 1);
|
|
assert.equal(record[0].label, "Test client");
|
|
assert.equal(record[0].serverKey, "ra3-mod-xml");
|
|
assert.equal(record[0].sourceVersion, "1.2.3");
|
|
|
|
const removed = await uninstallMcpServerConfigs({ agentHome: home });
|
|
assert.deepEqual(removed, [file]);
|
|
assert.equal((await readMcpInstallRecord(home)).length, 0);
|
|
// Our entry was the only server: the empty container is dropped.
|
|
assert.deepEqual(JSON.parse(readFileSync(file, "utf8")), {});
|
|
} finally {
|
|
rmSync(home, { recursive: true, force: true });
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("removeLauncher deletes the stable launcher and tolerates its absence", async () => {
|
|
const home = mkdtempSync(join(tmpdir(), "ra3-setup-launcher-"));
|
|
try {
|
|
writeFileSync(launcherPath(home), "dummy", "utf8");
|
|
assert.equal(existsSync(launcherPath(home)), true);
|
|
assert.equal(await removeLauncher(home), true);
|
|
assert.equal(existsSync(launcherPath(home)), false);
|
|
// force: true, so removing it again is still a success.
|
|
assert.equal(await removeLauncher(home), true);
|
|
} finally {
|
|
rmSync(home, { recursive: true, force: true });
|
|
}
|
|
});
|