97 lines
4.3 KiB
JavaScript
97 lines
4.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFile } from "node:child_process";
|
|
import { createServer } from "node:http";
|
|
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { promisify } from "node:util";
|
|
import test from "node:test";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const installer = new URL("../server/assets/foundry-agent-npm/bin/nodedc-foundry-codex-agent.mjs", import.meta.url);
|
|
|
|
async function listen(server) {
|
|
await new Promise((resolve, reject) => {
|
|
server.once("error", reject);
|
|
server.listen(0, "127.0.0.1", resolve);
|
|
});
|
|
return `http://127.0.0.1:${server.address().port}`;
|
|
}
|
|
|
|
test("Foundry installer preserves unrelated MCP entries and installs two independent servers", async () => {
|
|
let baseUrl = "";
|
|
const server = createServer(async (request, response) => {
|
|
if (request.url === "/api/foundry-agent/v1/setup/redeem") {
|
|
response.writeHead(200, { "content-type": "application/json" });
|
|
response.end(JSON.stringify({
|
|
ok: true,
|
|
foundry: { serverName: "nodedc_module_foundry", mcpUrl: `${baseUrl}/foundry-mcp`, token: "foundry-device-token" },
|
|
ontology: { serverName: "nodedc_ontology", mcpUrl: `${baseUrl}/ontology-mcp`, token: "ontology-device-token", readOnly: true },
|
|
}));
|
|
return;
|
|
}
|
|
if (request.url === "/foundry-mcp" || request.url === "/ontology-mcp") {
|
|
const chunks = [];
|
|
for await (const chunk of request) chunks.push(chunk);
|
|
const message = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
const tools = request.url === "/foundry-mcp"
|
|
? [{ name: "foundry_status" }, { name: "foundry_create_application" }]
|
|
: [{ name: "ontology_status" }, { name: "ontology_search" }];
|
|
response.writeHead(200, { "content-type": "application/json" });
|
|
response.end(JSON.stringify({
|
|
jsonrpc: "2.0",
|
|
id: message.id,
|
|
result: message.method === "tools/list"
|
|
? { tools }
|
|
: { protocolVersion: "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "test", version: "1" } },
|
|
}));
|
|
return;
|
|
}
|
|
response.writeHead(404);
|
|
response.end();
|
|
});
|
|
baseUrl = await listen(server);
|
|
const codexHome = await mkdtemp(path.join(os.tmpdir(), "nodedc-foundry-installer-"));
|
|
await mkdir(codexHome, { recursive: true });
|
|
const original = [
|
|
"model = \"gpt-5\"",
|
|
"",
|
|
"[mcp_servers.nodedc_engine_agent]",
|
|
"url = \"https://engine.example/mcp\"",
|
|
"",
|
|
"[mcp_servers.nodedc_module_foundry]",
|
|
"url = \"https://stale.example/foundry\"",
|
|
"",
|
|
"[mcp_servers.nodedc_ontology]",
|
|
"url = \"https://stale.example/ontology\"",
|
|
"",
|
|
].join("\n");
|
|
await writeFile(path.join(codexHome, "config.toml"), original, "utf8");
|
|
try {
|
|
const result = await execFileAsync(process.execPath, [installer.pathname, "--server", baseUrl, "--code", "test-code", "--codex-home", codexHome]);
|
|
assert.match(result.stdout, /Foundry MCP smoke tools: 2/);
|
|
assert.match(result.stdout, /Ontology MCP smoke tools: 2/);
|
|
|
|
const config = await readFile(path.join(codexHome, "config.toml"), "utf8");
|
|
assert.match(config, /\[mcp_servers\.nodedc_engine_agent\]/);
|
|
assert.equal((config.match(/\[mcp_servers\.nodedc_module_foundry\]/g) || []).length, 1);
|
|
assert.equal((config.match(/\[mcp_servers\.nodedc_ontology\]/g) || []).length, 1);
|
|
assert.match(config, new RegExp(`${baseUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\/foundry-mcp`));
|
|
assert.match(config, /Bearer foundry-device-token/);
|
|
assert.match(config, /Bearer ontology-device-token/);
|
|
assert.equal(config.includes("stale.example"), false);
|
|
|
|
const backup = await readFile(path.join(codexHome, "config.toml.nodedc-foundry-agent.bak"), "utf8");
|
|
assert.equal(backup, original);
|
|
const skill = await readFile(path.join(codexHome, "skills", "foundry-context", "SKILL.md"), "utf8");
|
|
assert.match(skill, /nodedc_module_foundry/);
|
|
assert.match(skill, /nodedc_ontology/);
|
|
|
|
const doctor = await execFileAsync(process.execPath, [installer.pathname, "doctor", "--codex-home", codexHome]);
|
|
assert.match(doctor.stdout, /install is present/);
|
|
} finally {
|
|
await new Promise((resolve) => server.close(resolve));
|
|
await rm(codexHome, { recursive: true, force: true });
|
|
}
|
|
});
|