mirror of
https://github.com/morluto/rea.git
synced 2026-10-22 05:42:31 -05:00
Plan and approve Cursor setup in an isolated home, edit an unrelated server, then uninstall and verify both the historical backup and exact pre-uninstall recovery bytes. Related: #4581, PR #4580.
The new real CLI control fails on f9ccacca2 with a missing recovery file and passes with the recovery owner fix. Verified on macOS; native Windows/Linux runs remain unverified.
135 lines
4.2 KiB
TypeScript
135 lines
4.2 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
cliTest(
|
|
"repeated client flags plan and configure each selected client once",
|
|
async ({ cli }) => {
|
|
const directory = await createTestTempDirectory("rea-repeated-clients-");
|
|
const arguments_ = [
|
|
"setup",
|
|
"--client",
|
|
"cursor",
|
|
"--client",
|
|
"codex",
|
|
"--client",
|
|
"cursor",
|
|
"--skill=false",
|
|
"--json",
|
|
];
|
|
const run = (mode: string) =>
|
|
cli.run({
|
|
arguments: [...arguments_, mode],
|
|
cwd: directory,
|
|
environment: {
|
|
HOME: directory,
|
|
USERPROFILE: directory,
|
|
TMPDIR: directory,
|
|
},
|
|
});
|
|
const configurations = [
|
|
join(directory, ".cursor/mcp.json"),
|
|
join(directory, ".codex/config.toml"),
|
|
];
|
|
|
|
const preview = await run("--dry-run");
|
|
expect(preview.exitCode, preview.stdout).toBe(0);
|
|
expect(preview.json).toMatchObject({
|
|
status: "planned",
|
|
plannedActions: [
|
|
{ id: "configure_client:cursor" },
|
|
{ id: "configure_client:codex" },
|
|
],
|
|
appliedActions: [],
|
|
});
|
|
for (const path of configurations)
|
|
await expect(access(path)).rejects.toMatchObject({ code: "ENOENT" });
|
|
|
|
const applied = await run("--yes");
|
|
expect(applied.exitCode, applied.stdout).toBe(0);
|
|
expect(applied.json).toMatchObject({
|
|
status: "ready",
|
|
plannedActions: [
|
|
{ id: "configure_client:cursor" },
|
|
{ id: "configure_client:codex" },
|
|
],
|
|
appliedActions: ["configured_cursor", "configured_codex"],
|
|
clients: {
|
|
cursor: { status: "configured" },
|
|
codex: { status: "configured" },
|
|
},
|
|
});
|
|
const configured = await Promise.all(
|
|
configurations.map((path) => readFile(path, "utf8")),
|
|
);
|
|
|
|
const repeated = await run("--yes");
|
|
expect(repeated.exitCode, repeated.stdout).toBe(0);
|
|
expect(repeated.json).toMatchObject({
|
|
status: "ready",
|
|
plannedActions: [],
|
|
appliedActions: [],
|
|
});
|
|
expect(
|
|
await Promise.all(configurations.map((path) => readFile(path, "utf8"))),
|
|
).toEqual(configured);
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"uninstall preserves user edits made after an earlier setup",
|
|
async ({ cli }) => {
|
|
const home = await createTestTempDirectory("rea-current-uninstall-backup-");
|
|
const config = join(home, ".cursor", "mcp.json");
|
|
const original = '{"mcpServers":{"other":{"command":"other-server"}}}\n';
|
|
await mkdir(join(home, ".cursor"), { recursive: true });
|
|
await writeFile(config, original);
|
|
const run = (arguments_: readonly string[]) =>
|
|
cli.run({
|
|
arguments: [...arguments_, "--json"],
|
|
cwd: home,
|
|
environment: { HOME: home, USERPROFILE: home, TMPDIR: home },
|
|
});
|
|
const planned = await run([
|
|
"setup",
|
|
"--client",
|
|
"cursor",
|
|
"--skill=false",
|
|
"--dry-run",
|
|
]);
|
|
expect(planned.exitCode, planned.stderr).toBe(0);
|
|
expect(planned.json).toMatchObject({ status: "planned" });
|
|
expect(await readFile(config, "utf8")).toBe(original);
|
|
const installed = await run([
|
|
"setup",
|
|
"--client",
|
|
"cursor",
|
|
"--skill=false",
|
|
"--yes",
|
|
]);
|
|
expect(installed.exitCode, installed.stderr).toBe(0);
|
|
expect(installed.json).toMatchObject({ status: "ready" });
|
|
const current = (await readFile(config, "utf8")).replace(
|
|
"other-server",
|
|
"edited-server",
|
|
);
|
|
expect(current).toContain("edited-server");
|
|
await writeFile(config, current);
|
|
|
|
const removed = await run(["uninstall"]);
|
|
expect(removed.exitCode, removed.stderr).toBe(0);
|
|
expect(removed.json).toMatchObject({ status: "complete" });
|
|
const recovery = `${config}.rea.backup.${createHash("sha256").update(current).digest("hex")}.recovery`;
|
|
expect(await readFile(recovery, "utf8")).toBe(current);
|
|
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original);
|
|
expect(JSON.parse(await readFile(config, "utf8"))).toEqual({
|
|
mcpServers: { other: { command: "edited-server" } },
|
|
});
|
|
},
|
|
);
|