Files
rea/tests/boundary/cli/setupProviderReadiness.test.ts
Kaoru0822andClaude Opus 5.5 002e1b68c6 fix(ghidra): skip the Java probe without an installation (#6282)
Setup and doctor inspect every provider. With no GHIDRA_INSTALL_DIR and no
Ghidra launcher on PATH, the Ghidra inspection still ran the host `java` from
PATH. Its result was discarded because the configuration check already
rejects Ghidra, but its owned-process cleanup could still fail closed. In CI,
that cleanup reported cleanup_incomplete for an unrelated live process whose
/proc/<pid>/environ was unreadable, so `rea setup --client hermes` exited 1.

Run Java only for a selected installation, matching the existing rule for a
relative JAVA_HOME. Hermes setup tests now report the CLI JSON when an exit
code assertion fails.

Fixes #6200

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:20:12 -07:00

124 lines
4.0 KiB
TypeScript

import { access, mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { z } from "zod";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
import { writeFakeKuna } from "../providers/kuna/fakeKuna.js";
cliTest.skipIf(process.platform === "win32").for(["blocked", "ready"] as const)(
"setup dry-run reports Kuna readiness for a %s cache",
async (cache, { cli }) => {
const root = await createTestTempDirectory("rea-setup-provider-readiness-");
const home = join(root, "home");
await mkdir(home);
const command = await writeFakeKuna(root);
const cachePath = join(root, "cache");
if (cache === "blocked") await writeFile(cachePath, "not a directory");
else await mkdir(cachePath);
const result = await cli.run({
arguments: ["setup", "--client", "claude_code", "--dry-run", "--json"],
environment: {
HOME: home,
USERPROFILE: home,
HOPPER_LAUNCHER_PATH: join(root, "missing-hopper"),
GHIDRA_INSTALL_DIR: join(root, "missing-ghidra"),
REA_KUNA_COMMAND: command,
REA_KUNA_CACHE_DIR: cachePath,
},
timeoutMs: 20_000,
});
expect(result.exitCode, result.stderr).toBe(0);
expect(result.json).toMatchObject({
status: "planned",
doctor: {
providerInspections: expect.arrayContaining([
expect.objectContaining({
id: "kuna",
available: cache === "ready",
checks: expect.arrayContaining([
expect.objectContaining({
name: "cache",
ok: cache === "ready",
}),
]),
}),
]),
},
});
const summary = z
.object({
doctor: z.object({ availableProviders: z.array(z.string()) }),
})
.parse(result.json);
expect(summary.doctor.availableProviders.includes("kuna")).toBe(
cache === "ready",
);
await expect(access(join(home, ".claude.json"))).rejects.toMatchObject({
code: "ENOENT",
});
},
);
cliTest.skipIf(process.platform === "win32")(
"setup and doctor do not execute host Java while Ghidra is unconfigured",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-setup-unconfigured-java-");
const home = join(root, "home");
const bin = join(root, "bin");
const marker = join(root, "java-executed");
await mkdir(home);
await mkdir(bin);
// A host JVM is unrelated to an unconfigured Ghidra provider; running it
// exposes setup to that process's cleanup outcome.
await writeFile(
join(bin, "java"),
`#!/bin/sh\nprintf '%s\\n' "$*" >> '${marker}'\nexit 1\n`,
{ mode: 0o755 },
);
const environment = {
HOME: home,
USERPROFILE: home,
// The recording java shadows any host JVM in the system directories.
PATH: `${bin}:/usr/bin:/bin`,
HOPPER_LAUNCHER_PATH: join(root, "missing-hopper"),
};
const setup = await cli.run({
arguments: ["setup", "--client", "claude_code", "--dry-run", "--json"],
environment,
timeoutMs: 20_000,
});
expect(setup.exitCode, setup.stdout).toBe(0);
expect(setup.json).toMatchObject({
status: "planned",
doctor: {
providerInspections: expect.arrayContaining([
expect.objectContaining({
id: "ghidra",
configured: false,
checks: [
expect.objectContaining({
name: "configuration",
code: "not_configured",
}),
],
}),
]),
},
});
const doctor = await cli.run({
arguments: ["doctor", "--json"],
environment,
timeoutMs: 20_000,
});
expect(doctor.json).toMatchObject({
providerInspections: expect.arrayContaining([
expect.objectContaining({ id: "ghidra", configured: false }),
]),
});
await expect(access(marker)).rejects.toMatchObject({ code: "ENOENT" });
},
);