Files
rea/tests/boundary/cli/ghidraJavaDoctor.test.ts
T
morluto c75cf827e7 fix(ghidra): retain typed Java prerequisite outcomes
Replace ambiguous absent observations with an owning Result producer for
Java prerequisite probes. Preserve missing-command, runtime-failure and
unresolved-property outcomes, process error codes, both bounded streams,
raw byte counts and partial properties through provider and doctor adapters.
Reject malformed numeric-prefix versions while retaining their source facts.
Migrate all injected hosts to the named probe contract and remove the old
parser/observation ownership from installation discovery.

Validation: 17 focused files, 350 passed; npm run check passed. Public doctor
fixtures distinguish absent/nonzero/malformed commands. Real installed JDK
and Ghidra readiness facts matched across 20 baseline/candidate CLI tasks.
Warm repeat raw replies6392tokens each, elapsed .448/.444s, CPU .662/.660s;
sampled family memory varied and reversed direction on repeat. No causal
performance improvement claim, true-cold or full-agent/fault-path measurement.

Synchronous probe deadline/cancellation/family cleanup remains #4561 and needs
coordinated discovery/admission supervision; no claim that it is fixed here.
No real failing JVM or Windows runtime verification performed.

Refs #4560, #4561
2026-10-17 03:33:16 +08:00

118 lines
4.0 KiB
TypeScript

import { chmod, mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { elf, thinMach } from "../../../src/domain/binaryTarget.fixture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.skipIf(!["darwin", "linux"].includes(process.platform)).for([
["missing", "missing_dependency", "runtime_missing"],
["nonzero", "runtime_failure", "runtime_unavailable"],
["properties", "config_drift", "version_unresolved"],
] as const)(
"preserves the %s Java prerequisite result through doctor",
async ([failure, classification, code], { cli }) => {
const root = await createTestTempDirectory("rea-java-doctor-");
const jdk = join(root, "jdk");
const install = join(root, "ghidra");
const platform = process.platform === "darwin" ? "mac" : "linux";
const architecture = process.arch === "arm64" ? "arm_64" : "x86_64";
const native = join(
install,
"Ghidra/Features/Decompiler/os",
`${platform}_${architecture}`,
"decompile",
);
for (const path of [
join(jdk, "bin"),
join(install, "support"),
join(install, "Ghidra"),
join(native, ".."),
])
await mkdir(path, { recursive: true });
await writeFile(
join(install, "Ghidra/application.properties"),
"application.version=12.1.4\napplication.java.min=21\n",
);
await writeFile(
join(install, "support/analyzeHeadless"),
"#!/bin/sh\nexit 99\n",
);
await chmod(join(install, "support/analyzeHeadless"), 0o700);
await writeFile(
native,
process.platform === "darwin"
? thinMach(
0xfeedfacf,
process.arch === "arm64" ? 0x0100000c : 0x01000007,
)
: elf(2, 1, process.arch === "arm64" ? 183 : 62),
);
await chmod(native, 0o700);
if (failure !== "missing") {
const body =
failure === "nonzero"
? 'process.stderr.write("controlled runtime failure\\n"); process.exit(7);'
: `process.stdout.write(${JSON.stringify(`java.home = ${jdk}\nos.arch = aarch64\n`)});`;
await writeFile(
join(jdk, "bin/java"),
`#!${process.execPath}\n${body}\n`,
);
await chmod(join(jdk, "bin/java"), 0o700);
}
const result = await cli.run({
arguments: ["doctor", "--provider", "ghidra", "--json"],
environment: {
JAVA_HOME: jdk,
GHIDRA_INSTALL_DIR: install,
REA_LOG_LEVEL: "silent",
},
});
expect(result.exitCode, `${result.stdout}\n${result.stderr}`).toBe(1);
expect(result.json).toMatchObject({
checks: expect.arrayContaining([
expect.objectContaining({
name: "ghidra-java",
classification,
ok: false,
}),
]),
providerInspections: expect.arrayContaining([
expect.objectContaining({
id: "ghidra",
checks: expect.arrayContaining([
expect.objectContaining({ name: "java", code }),
]),
}),
]),
});
if (failure === "nonzero")
expect(result.json).toMatchObject({
checks: expect.arrayContaining([
expect.objectContaining({
name: "ghidra-java",
detail: expect.stringContaining("Java exited 7"),
details: expect.objectContaining({
output: expect.objectContaining({
stderr: "controlled runtime failure\n",
exit_code: 7,
stderr_bytes: 27,
}),
}),
}),
]),
});
if (failure === "properties")
expect(result.json).toMatchObject({
checks: expect.arrayContaining([
expect.objectContaining({
name: "ghidra-java",
details: expect.objectContaining({
properties: expect.objectContaining({ home: jdk, version: null }),
}),
}),
]),
});
},
);