Files
reverse-engineering-anything/tests/acceptance/setup/registrationRuntimeEnvironmentCli.test.ts
T
morluto 9abdad335c fix(setup): preserve configuration identity and inspection uncertainty
Refs #4378, #4384, #4432, #4496, #4501.

Keep inaccessible MSIX targets unknown, preserve literal AppData roots, and distinguish absent skill destinations from retained files. Isolate registration tests from host provider discovery.

Windows package behavior was exercised through filesystem fixtures; the installed Windows Claude application was not verified.
2026-10-17 00:44:02 +08:00

666 lines
21 KiB
TypeScript

import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { expect } from "vitest";
import { Scalar, stringify } from "yaml";
import {
effectiveClientServer,
parseClientConfiguration,
clientRegistrationEntry,
} from "../../../src/application/ClientConfigurationDocument.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const scenarios = [
{
client: "cursor",
format: "json",
path: ".cursor/mcp.json",
setting: "HOPPER_LOADER_ARGS_JSON",
invalid: { HOPPER_LOADER_ARGS_JSON: "{" },
repaired: { HOPPER_LOADER_ARGS_JSON: '["", "--loader", "Mach-O"]' },
},
{
client: "dsh",
format: "dsh",
path: ".dsh/cordis.patch.yml",
setting: "REA_GHIDRA_COMPILER_SPEC_ID",
invalid: { REA_GHIDRA_COMPILER_SPEC_ID: "borlandcpp" },
repaired: {
REA_GHIDRA_COMPILER_SPEC_ID: "borlandcpp",
REA_GHIDRA_LANGUAGE_ID: "x86:LE:32:default",
},
},
] as const;
cliTest.for([
{
client: "cursor",
format: "json",
path: ".cursor/mcp.json",
position: "value",
},
{
client: "cursor",
format: "json",
path: ".cursor/mcp.json",
position: "name",
},
{
client: "dsh",
format: "dsh",
path: ".dsh/cordis.patch.yml",
position: "value",
},
{
client: "dsh",
format: "dsh",
path: ".dsh/cordis.patch.yml",
position: "name",
},
{
client: "dsh",
format: "dsh",
path: ".dsh/cordis.patch.yml",
position: "expression name",
},
] as const)(
"refuses a NUL environment $position in $client before setup writes",
async (scenario, { cli }) => {
const home = await createTestTempDirectory("rea-registration-nul-");
const config = join(home, scenario.path);
await mkdir(dirname(config), { recursive: true });
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
DSH_HOME: join(home, ".dsh"),
HOPPER_LAUNCHER_PATH: join(home, "missing-hopper"),
};
const registration = (env: Readonly<Record<string, string>>) => {
const entry = clientRegistrationEntry(
scenario.format,
[process.execPath, resolve("scripts/rea.mjs"), "mcp"],
env,
);
if (scenario.position === "expression name")
entry.env = Object.fromEntries(
Object.entries(env).map(([key, value]) => {
if (!key.includes("\0")) return [key, value];
const expression = new Scalar("process.env.CALLER_SETTING");
expression.tag = "tag:yaml.org,2002:js";
return [key, expression];
}),
);
return scenario.format === "dsh"
? stringify([
{
insert: [
{
id: "rea-mcp",
name: "@deepseek-ai/dsh-mcp-client",
config: entry,
},
],
},
])
: JSON.stringify({ mcpServers: { rea: entry } });
};
const valueMarker = "reaNulFixtureValue_6c04e2";
const original = registration(
scenario.position === "value"
? { CALLER_SETTING: `${valueMarker}\0value` }
: { ["CALLER\0SETTING"]: `${valueMarker} value` },
);
await writeFile(config, original);
const doctor = await cli.run({
arguments: [
"doctor",
"--client",
scenario.client,
"--skill=false",
"--json",
],
cwd: home,
environment,
});
expect(doctor.exitCode).toBe(1);
expect(doctor.json).toMatchObject({
scope_checks: expect.arrayContaining([
expect.objectContaining({
name: `registration:${scenario.client}`,
ok: false,
classification: "config_drift",
remediation: expect.stringContaining("NUL"),
}),
]),
});
const setup = await cli.run({
arguments: [
"setup",
"--client",
scenario.client,
"--skill=false",
"--yes",
"--json",
],
cwd: home,
environment,
});
expect(setup.exitCode).toBe(1);
expect(setup.json).toMatchObject({
status: "needs_human",
plannedActions: [],
appliedActions: [],
remediation: expect.stringContaining("NUL"),
});
expect(setup.stdout + doctor.stdout).not.toContain(valueMarker);
expect(await readFile(config, "utf8")).toBe(original);
await expect(access(`${config}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
const repaired = registration({
CALLER_SETTING: "ordinary\nvalue=二\t",
EMPTY_SETTING: "",
});
await writeFile(config, repaired);
const healthy = await cli.run({
arguments: [
"doctor",
"--client",
scenario.client,
"--skill=false",
"--json",
],
cwd: home,
environment,
});
expect(healthy.exitCode, healthy.stdout).toBe(0);
expect(healthy.json).toMatchObject({ healthy: true });
expect(await readFile(config, "utf8")).toBe(repaired);
},
);
for (const scenario of scenarios)
cliTest(
`setup and doctor reject ${scenario.client} overrides rejected by MCP startup`,
async ({ cli }) => {
const home = await createTestTempDirectory("rea-registration-runtime-");
const config = join(home, scenario.path);
await mkdir(dirname(config), { recursive: true });
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
DSH_HOME: join(home, ".dsh"),
};
const registration = (env: Readonly<Record<string, string>>) => {
const server = {
command: "stale-launcher",
args: ["mcp"],
env: { CALLER_SETTING: "keep exactly", ...env },
};
return scenario.client === "dsh"
? stringify([
{
insert: [
{
id: "rea-mcp",
name: "@deepseek-ai/dsh-mcp-client",
config: {
serverName: "rea",
transport: "stdio",
...server,
},
},
],
},
])
: JSON.stringify({ mcpServers: { rea: server } });
};
const original = registration(scenario.invalid);
await writeFile(config, original);
const startup = await cli.run({
arguments: ["mcp"],
cwd: home,
environment: { ...environment, ...scenario.invalid },
});
expect(startup.exitCode).toBe(1);
expect(startup.stderr).toContain(scenario.setting);
expect(startup.stdout).toBe("");
const run = (args: readonly string[]) =>
cli.run({ arguments: [...args, "--json"], cwd: home, environment });
const setup = await run([
"setup",
"--client",
scenario.client,
"--skill=false",
"--yes",
]);
expect(setup.exitCode).toBe(1);
expect(setup.json).toMatchObject({
status: "needs_human",
plannedActions: [],
appliedActions: [],
remediation: expect.stringContaining(scenario.setting),
});
const doctor = await run(["doctor", "--client", scenario.client]);
expect(doctor.exitCode).toBe(1);
expect(doctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({
client: scenario.client,
state: "invalid",
remediation: expect.stringContaining(scenario.setting),
}),
]),
},
});
expect(await readFile(config, "utf8")).toBe(original);
await expect(access(`${config}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
// Model the caller repairing the named setting before rerunning setup.
const repaired = registration(scenario.repaired);
await writeFile(config, repaired);
const configured = await run([
"setup",
"--client",
scenario.client,
"--skill=false",
"--yes",
]);
expect(configured.exitCode, configured.stdout).toBe(0);
const text = await readFile(config, "utf8");
const parsed = parseClientConfiguration(text, scenario.format);
expect(effectiveClientServer(parsed, "rea")).toMatchObject({
env: { CALLER_SETTING: "keep exactly", ...scenario.repaired },
});
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(repaired);
const repeated = await run([
"setup",
"--client",
scenario.client,
"--skill=false",
"--yes",
]);
expect(repeated.json).toMatchObject({
status: "ready",
appliedActions: [],
plannedActions: [],
});
expect(await readFile(config, "utf8")).toBe(text);
},
);
cliTest(
"OpenCode CLI validates dependent settings after merging JSON and JSONC",
async ({ cli }) => {
const home = await createTestTempDirectory("rea-layered-runtime-cli-");
const directory = join(home, ".config/opencode");
await mkdir(directory, { recursive: true });
const lower = join(directory, "opencode.json");
const upper = join(directory, "opencode.jsonc");
const content = (env: Record<string, string>) =>
JSON.stringify({
mcp: {
rea: clientRegistrationEntry(
"opencode",
[resolve("scripts/rea.mjs"), "mcp"],
env,
),
},
});
const lowerContent = content({
REA_GHIDRA_LANGUAGE_ID: "x86:LE:32:default",
});
const upperContent = content({ REA_GHIDRA_COMPILER_SPEC_ID: "borlandcpp" });
await writeFile(lower, lowerContent);
await writeFile(upper, upperContent);
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
// Select absent provider locations so installed tools cannot make this
// configuration merge test plan an unrelated provider-environment edit.
HOPPER_LAUNCHER_PATH: join(home, "missing-hopper"),
GHIDRA_INSTALL_DIR: join(home, "missing-ghidra"),
JAVA_HOME: join(home, "missing-java"),
};
const run = (args: readonly string[]) =>
cli.run({ arguments: [...args, "--json"], cwd: home, environment });
const setup = await run([
"setup",
"--client",
"opencode",
"--skill=false",
"--yes",
]);
expect(setup.exitCode, setup.stdout).toBe(0);
expect(setup.json).toMatchObject({
status: "ready",
plannedActions: [],
appliedActions: [],
});
const doctor = await run(["doctor", "--client", "opencode"]);
expect(doctor.exitCode, doctor.stdout).toBe(0);
expect(doctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({ client: "opencode", state: "aligned" }),
]),
},
});
expect(await readFile(lower, "utf8")).toBe(lowerContent);
expect(await readFile(upper, "utf8")).toBe(upperContent);
await writeFile(lower, content({ CALLER_SETTING: "retained" }));
const missing = await run([
"setup",
"--client",
"opencode",
"--skill=false",
"--yes",
]);
expect(missing.exitCode).toBe(1);
expect(missing.json).toMatchObject({
status: "needs_human",
plannedActions: [],
appliedActions: [],
remediation: expect.stringContaining("REA_GHIDRA_COMPILER_SPEC_ID"),
});
const invalidDoctor = await run(["doctor", "--client", "opencode"]);
expect(invalidDoctor.exitCode).toBe(1);
expect(invalidDoctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({ client: "opencode", state: "invalid" }),
]),
},
});
expect(await readFile(upper, "utf8")).toBe(upperContent);
for (const path of [lower, upper])
await expect(access(`${path}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
},
);
cliTest(
"public setup propagates selected provider deadlines and input profile to Codex and Kimi",
async ({ cli }) => {
const home = await createTestTempDirectory(
"rea-selected-registration-env-",
);
const codexHome = join(home, "codex");
const kimiHome = join(home, "kimi");
await Promise.all(
[codexHome, kimiHome].map((path) => mkdir(path, { recursive: true })),
);
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
CODEX_HOME: codexHome,
KIMI_CODE_HOME: kimiHome,
HOPPER_LAUNCHER_PATH: join(home, "missing-hopper"),
REA_ANALYSIS_PROVIDER: "auto",
REA_GHIDRA_STARTUP_TIMEOUT_MS: "2700001",
REA_HOPPER_STARTUP_TIMEOUT_MS: "2800002",
REA_JEB_MCP_TIMEOUT_MS: "2900003",
REA_MCP_INPUT_SCHEMA_PROFILE: "full",
REA_UNRELATED_MARKER: "do-not-persist",
};
const run = (arguments_: readonly string[]) =>
cli.run({
arguments: [...arguments_, "--json"],
cwd: home,
environment,
});
for (const client of ["codex", "kimi"] as const) {
const setup = await run([
"setup",
"--client",
client,
"--skill=false",
"--yes",
]);
expect(setup.exitCode, setup.stderr).toBe(0);
expect(setup.json).toMatchObject({ status: "ready" });
}
const codexConfig = await readFile(join(codexHome, "config.toml"), "utf8");
const codexRegistration = effectiveClientServer(
parseClientConfiguration(codexConfig, "toml"),
"rea",
);
expect(codexRegistration).toMatchObject({ tool_timeout_sec: 3021 });
expect(codexConfig).toContain('REA_JEB_MCP_TIMEOUT_MS = "2900003"');
expect(codexConfig).not.toContain("REA_UNRELATED_MARKER");
const kimiPath = join(kimiHome, "mcp.json");
const kimiRegistration = effectiveClientServer(
parseClientConfiguration(await readFile(kimiPath, "utf8"), "kimi"),
"rea",
);
expect(kimiRegistration).toMatchObject({
toolTimeoutMs: 3_020_003,
env: {
REA_ANALYSIS_PROVIDER: "auto",
REA_GHIDRA_STARTUP_TIMEOUT_MS: "2700001",
REA_HOPPER_STARTUP_TIMEOUT_MS: "2800002",
REA_JEB_MCP_TIMEOUT_MS: "2900003",
REA_MCP_INPUT_SCHEMA_PROFILE: "full",
},
});
expect(await readFile(kimiPath, "utf8")).not.toContain(
"REA_UNRELATED_MARKER",
);
const doctor = await run(["doctor", "--client", "kimi"]);
expect(doctor.exitCode, doctor.stderr).toBe(0);
expect(doctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({ client: "kimi", state: "aligned" }),
]),
},
});
},
120_000,
);
for (const [client, provider] of [
["codex", "ida"],
["kimi", "ida"],
["kimi", "auto"],
] as const)
cliTest(
`public ${client} setup and doctor use the effective IDA registration request budget with ${provider} selection`,
async ({ cli }) => {
const home = await createTestTempDirectory(
"rea-ida-registration-budget-",
);
const clientHome = join(home, client);
await mkdir(clientHome, { recursive: true });
const idaConfig = join(home, "ida.json");
const writeRegistration = (timeoutMs: number) =>
writeFile(
idaConfig,
JSON.stringify({
url: "http://127.0.0.1:12345/mcp",
headers: { Authorization: "Bearer fixture-credential" },
timeoutMs,
}),
);
await writeRegistration(1_800_001);
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
CODEX_HOME: clientHome,
KIMI_CODE_HOME: clientHome,
HOPPER_LAUNCHER_PATH: join(home, "missing-hopper"),
};
const setupArguments = [
"setup",
"--client",
client,
"--skill=false",
"--yes",
"--json",
];
const configured = await cli.run({
arguments: setupArguments,
cwd: home,
environment: {
...environment,
REA_ANALYSIS_PROVIDER: provider,
REA_IDA_MCP_CONFIG: idaConfig,
},
});
expect(configured.exitCode, configured.stdout).toBe(0);
const config = join(
clientHome,
client === "codex" ? "config.toml" : "mcp.json",
);
const text = await readFile(config, "utf8");
expect(
effectiveClientServer(
parseClientConfiguration(text, client === "codex" ? "toml" : "kimi"),
"rea",
),
).toMatchObject({
...(client === "codex"
? { tool_timeout_sec: 1921 }
: { toolTimeoutMs: 1_920_001 }),
env: { REA_ANALYSIS_PROVIDER: provider, REA_IDA_MCP_CONFIG: idaConfig },
});
expect(text).not.toContain("fixture-credential");
const run = (arguments_: readonly string[]) =>
cli.run({ arguments: arguments_, cwd: home, environment });
const healthy = await run(["doctor", "--client", client, "--json"]);
expect(healthy.exitCode, healthy.stdout).toBe(0);
expect(JSON.stringify(healthy.json)).not.toContain('"advisory"');
// Only the stored client's provider path identifies this changed budget;
// the doctor/setup process has no IDA configuration in its environment.
await writeRegistration(2_400_001);
const doctor = await run(["doctor", "--client", client, "--json"]);
expect(doctor.exitCode, doctor.stdout).toBe(0);
expect(doctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({
client,
state: "aligned",
advisory: expect.stringContaining(
client === "codex"
? "tool_timeout_sec = 2521 seconds"
: "toolTimeoutMs = 2520001 ms",
),
}),
]),
},
});
const repeated = await run(setupArguments);
expect(repeated.exitCode, repeated.stdout).toBe(0);
expect(repeated.json).toMatchObject({
status: "ready",
plannedActions: [],
appliedActions: [],
});
expect(await readFile(config, "utf8")).toBe(text);
await expect(access(`${config}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
if (provider === "ida") {
await writeRegistration(0);
const invalidDoctor = await run([
"doctor",
"--client",
client,
"--json",
]);
expect(invalidDoctor.exitCode).toBe(1);
expect(invalidDoctor.json).toMatchObject({
identity: {
registrations: expect.arrayContaining([
expect.objectContaining({
client,
state: "invalid",
remediation: expect.stringContaining("REA_IDA_MCP_CONFIG"),
}),
]),
},
});
const invalidSetup = await run(setupArguments);
expect(invalidSetup.exitCode).toBe(1);
expect(invalidSetup.stdout).toContain("REA_IDA_MCP_CONFIG");
expect(await readFile(config, "utf8")).toBe(text);
await expect(access(`${config}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
}
},
120_000,
);
for (const invalidSetting of [
{ setting: "REA_MCP_INPUT_SCHEMA_PROFILE", value: "unknown" },
{ setting: "REA_GHIDRA_STARTUP_TIMEOUT_MS", value: "0" },
{ setting: "REA_JEB_MCP_TIMEOUT_MS", value: "1.5" },
] as const)
cliTest(
`setup rejects malformed selected ${invalidSetting.setting} before writing`,
async ({ cli }) => {
const home = await createTestTempDirectory(
"rea-invalid-selected-registration-env-",
);
const kimiHome = join(home, "kimi");
await mkdir(kimiHome, { recursive: true });
const config = join(kimiHome, "mcp.json");
const environment = {
HOME: home,
USERPROFILE: home,
XDG_CONFIG_HOME: join(home, ".config"),
KIMI_CODE_HOME: kimiHome,
HOPPER_LAUNCHER_PATH: join(home, "missing-hopper"),
[invalidSetting.setting]: invalidSetting.value,
};
const result = await cli.run({
arguments: [
"setup",
"--client",
"kimi",
"--skill=false",
"--yes",
"--json",
],
cwd: home,
environment,
});
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "configuration_invalid",
category: "invalid_input",
details: {
settings: [
expect.objectContaining({ setting: invalidSetting.setting }),
],
},
});
expect(`${result.stdout}\n${result.stderr}`).toContain(
invalidSetting.setting,
);
await expect(access(config)).rejects.toMatchObject({ code: "ENOENT" });
await expect(access(`${config}.rea.backup`)).rejects.toMatchObject({
code: "ENOENT",
});
},
120_000,
);