Files
reverse-engineering-anything/tests/acceptance/setup/hermesAnchoredEntrySetup.test.ts
T
morluto 23de10bb28 fix: streamline tests and preserve Darwin process ownership (#8275)
* test: strengthen suite guards and regression oracles

* test: share JSON client registration contract

* test: reuse fixtures and reduce repeated setup

* fix(process): preserve ambiguous Darwin token ownership

* test(process): bound child readiness waits
2026-10-31 16:43:04 +08:00

261 lines
9.5 KiB
TypeScript

import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { parse } from "yaml";
import { expect, onTestFinished } from "vitest";
import { currentClientRegistrationCommand } from "../../../src/application/ClientRegistrationIdentity.js";
import { readClientRegistrationStatuses } from "../../../src/application/ClientRegistrationStatus.js";
import { configureClientConfiguration } from "../../../src/application/SetupClientConfiguration.js";
import { supportedClients } from "../../../src/application/SupportedClients.js";
import { systemUninstallHost } from "../../../src/application/Uninstall.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
const cliLifecycle = (cli: TestCli, home: string, profile: string) => {
const client = supportedClients(home, process.platform, {
HERMES_HOME: profile,
}).find(({ name }) => name === "hermes");
if (client?.configPath !== join(profile, "config.yaml"))
throw new Error("Hermes profile did not resolve to the test config");
const run = (arguments_: readonly string[]) =>
cli.run({
arguments: arguments_,
cwd: home,
environment: { HOME: home, USERPROFILE: home, HERMES_HOME: profile },
timeoutMs: 20_000,
});
const setup = [
"setup",
"--client",
"hermes",
"--skill=false",
"--yes",
"--json",
];
const remove = async () => {
const result = await run(["uninstall", "--json"]);
expect(result.exitCode, JSON.stringify(result)).toBe(0);
};
return {
configure: async () => {
const result = await run(setup);
expect(result.exitCode, JSON.stringify(result)).toBe(0);
expect(result.json).toMatchObject({ status: "ready" });
},
verifyAndRepeat: async () => {
const doctor = await run(["doctor", "--client", "hermes", "--json"]);
expect(doctor.exitCode, JSON.stringify(doctor)).toBe(0);
expect(doctor.json).toMatchObject({
healthy: true,
identity: { registrations: [{ client: "hermes", state: "aligned" }] },
});
const repeated = await run(setup);
expect(repeated.exitCode, JSON.stringify(repeated)).toBe(0);
expect(repeated.json).toMatchObject({
plannedActions: [],
appliedActions: [],
});
},
remove,
removeAgain: remove,
};
};
const configurationLifecycle = (
home: string,
profile: string,
config: string,
command: readonly string[],
) => {
const environment = { HERMES_HOME: profile };
const client = supportedClients(home, process.platform, environment).find(
({ name }) => name === "hermes",
);
if (client === undefined || client.configPath !== config)
throw new Error("Hermes profile did not resolve to the test config");
const uninstallHost = systemUninstallHost(
home,
undefined,
environment,
process.platform,
);
onTestFinished(async () => {
expect(await uninstallHost.close?.()).toBeUndefined();
});
return {
configure: async () => {
expect(
await configureClientConfiguration(client, {}, command),
).toMatchObject({ status: "configured" });
},
verifyAndRepeat: async () => {
expect(
(
await readClientRegistrationStatuses(home, undefined, {
platform: process.platform,
environment,
selectedClientIds: ["hermes"],
})
).find(({ client: name }) => name === "hermes"),
).toMatchObject({ state: "aligned", config_path: config });
expect(await configureClientConfiguration(client, {}, command)).toEqual({
status: "unchanged",
});
},
remove: async () => {
expect((await uninstallHost.removeClient(client)).status).toBe("removed");
},
removeAgain: async () => {
expect((await uninstallHost.removeClient(client)).status).toBe("skipped");
},
};
};
type AnchorStyle = "entry" | "child" | "shadowed" | "key" | "external";
const anchoredConfiguration = (
version: "1.1" | "1.2",
style: AnchorStyle,
server: {
command: string;
args: string[];
env: { KEEP: string };
enabled: boolean;
},
) => {
const directive = version === "1.2" ? "%YAML 1.2\n---\n" : "";
const hasDefaults = style === "shadowed" || style === "external";
const entry =
` command: ${JSON.stringify(server.command)}\n` +
` args: ${JSON.stringify(server.args)}\n` +
" enabled: false\n" +
(hasDefaults
? " env: *settings # Keep nested reference comment\n"
: ` env: ${style === "child" ? "&settings " : ""}{KEEP: 'off'}\n`);
const original =
directive +
"# Keep this user setting\nmodel: custom\n" +
(hasDefaults ? "defaults: &settings {KEEP: 'off'}\n" : "") +
`mcp_servers:\n ${style === "key" ? "&server_name " : ""}rea:${style === "child" || style === "key" ? "" : " &server"}${style === "entry" ? " # Keep anchor definition comment" : ""}\n` +
entry +
(style === "child"
? " other:\n command: other-server\n env: *settings # Shared environment\n"
: style === "key"
? " other: {command: other-server}\nobserver: *server_name # Registration name\n"
: " other: *server # Independent server\n") +
(style === "shadowed"
? "later: &settings {KEEP: later}\nobserver: *server\n"
: "");
const other =
style === "child"
? { command: "other-server", env: { KEEP: "off" } }
: style === "key"
? { command: "other-server" }
: server;
return { original, other, hasDefaults };
};
for (const version of ["1.1", "1.2"] as const) {
for (const style of [
"entry",
"child",
"shadowed",
"key",
"external",
] as const) {
const publicCli = style === "entry" || style === "shadowed";
cliTest(
`Hermes ${version} preserves ${style} anchor consumers through ${publicCli ? "CLI" : "configuration boundary"} setup and uninstall`,
async ({ cli }) => {
// Exercise both YAML versions through the CLI, including the shadowed
// anchor case; the other styles retain the full registration lifecycle
// at the production configuration and uninstall boundary.
const home = await createTestTempDirectory(
"rea-hermes-anchored-entry-",
);
const profile = join(home, "hermes");
const config = join(profile, "config.yaml");
const command = publicCli
? [process.execPath, resolve("scripts/rea.mjs"), "mcp"]
: currentClientRegistrationCommand({ platform: process.platform });
if (command[0] === undefined) throw new Error("Missing REA entrypoint");
const server = {
command: command[0],
args: command.slice(1),
env: { KEEP: "off" },
enabled: false,
};
const { original, other, hasDefaults } = anchoredConfiguration(
version,
style,
server,
);
await mkdir(profile);
await writeFile(config, original);
const lifecycle = publicCli
? cliLifecycle(cli, home, profile)
: configurationLifecycle(home, profile, config, command);
await lifecycle.configure();
const configured = await readFile(config, "utf8");
const configuredValues = parse(configured, { version });
expect(configuredValues).toMatchObject({
model: "custom",
mcp_servers: { rea: { enabled: true, env: { KEEP: "off" } }, other },
...(style === "shadowed"
? { observer: server, later: { KEEP: "later" } }
: {}),
...(style === "key" ? { observer: "rea" } : {}),
});
if (hasDefaults)
expect(configuredValues.mcp_servers.other.env).toBe(
configuredValues.defaults,
);
expect(configured).toContain("# Keep this user setting");
if (style === "entry")
expect(configured).toContain("# Keep anchor definition comment");
expect(configured).toContain(
style === "child"
? "# Shared environment"
: style === "key"
? "# Registration name"
: "# Independent server",
);
if (hasDefaults)
expect(configured).toContain("# Keep nested reference comment");
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original);
await lifecycle.verifyAndRepeat();
expect(await readFile(config, "utf8")).toBe(configured);
await lifecycle.remove();
const removedValues = parse(await readFile(config, "utf8"), {
version,
});
expect(removedValues).toEqual({
model: "custom",
mcp_servers: { other },
...(hasDefaults ? { defaults: { KEEP: "off" } } : {}),
...(style === "shadowed"
? {
later: { KEEP: "later" },
observer: server,
}
: {}),
...(style === "key" ? { observer: "rea" } : {}),
});
if (hasDefaults)
expect(removedValues.mcp_servers.other.env).toBe(
removedValues.defaults,
);
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original);
const remaining = await readFile(config, "utf8");
if (style === "entry")
expect(remaining).toContain("# Keep anchor definition comment");
if (hasDefaults)
expect(remaining).toContain("# Keep nested reference comment");
await lifecycle.removeAgain();
expect(await readFile(config, "utf8")).toBe(remaining);
},
);
}
}