mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
fix(android): report a rejected relative JADX JAR instead of an unset one (#5862)
A supplied non-absolute REA_JADX_MCP_JAR was folded into the unset case, emitting not_configured with jar_configured: false and dropping the caller-selected value. The JAR parser now keeps the two outcomes distinct: unset stays not_configured, while a supplied non-absolute value (including an empty string) fails with open_options_invalid, jar_configured: true, the exact rejected jar_path and the absolute-path requirement. CLI, MCP and doctor derive their output from that one boundary result. Fixes #5844 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -544,13 +544,22 @@ const resolveJar = async (
|
||||
environment: Readonly<Record<string, string | undefined>>,
|
||||
): Promise<string> => {
|
||||
const jar = environment.REA_JADX_MCP_JAR;
|
||||
if (jar === undefined || !isAbsolute(jar))
|
||||
if (jar === undefined)
|
||||
throw configurationFailure(
|
||||
"not_configured",
|
||||
JADX_JAR_CONFIGURATION_REMEDIATION,
|
||||
{ jar_configured: false },
|
||||
"jar",
|
||||
);
|
||||
// A supplied selection stays configured even when rejected, so callers can
|
||||
// tell it from an omitted setting and see the exact value REA refused.
|
||||
if (!isAbsolute(jar))
|
||||
throw configurationFailure(
|
||||
"open_options_invalid",
|
||||
`REA_JADX_MCP_JAR must be an absolute path; received ${JSON.stringify(jar)}. ${JADX_JAR_CONFIGURATION_REMEDIATION}`,
|
||||
{ jar_configured: true, jar_path: jar, path_requirement: "absolute" },
|
||||
"jar",
|
||||
);
|
||||
try {
|
||||
await access(jar, constants.R_OK);
|
||||
if (!(await stat(jar)).isFile())
|
||||
|
||||
@@ -76,6 +76,61 @@ for (const { jarConfigured, setting } of [
|
||||
},
|
||||
);
|
||||
|
||||
cliTest.skipIf(process.platform === "win32")(
|
||||
"distinguishes a rejected relative REA_JADX_MCP_JAR from an unset one through CLI and MCP",
|
||||
async ({ cli }) => {
|
||||
const root = await createTestTempDirectory("rea-android-relative-jar-");
|
||||
const apk = join(root, "fixture.apk");
|
||||
await writeOrderedZip(apk, ["AndroidManifest.xml"]);
|
||||
const run = async (environment: Record<string, string>) => {
|
||||
const cliResult = await cli.run({
|
||||
arguments: ["inspect-android-package", apk, "--json"],
|
||||
environment: { REA_LOG_LEVEL: "silent", ...environment },
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
expect(cliResult.exitCode).toBe(1);
|
||||
const client = await connect({ REA_LOG_LEVEL: "silent", ...environment });
|
||||
const response = await client.callTool({
|
||||
name: "inspect_android_package",
|
||||
arguments: { path: apk },
|
||||
});
|
||||
expect(response.isError).toBe(true);
|
||||
expect(parseMcpToolError(response)).toEqual({ error: cliResult.json });
|
||||
return cliResult.json as {
|
||||
code: string;
|
||||
message: string;
|
||||
details: { diagnostics: Record<string, unknown> };
|
||||
};
|
||||
};
|
||||
|
||||
const relative = "./tools/jadx-headless-mcp.jar";
|
||||
const rejected = await run({ REA_JADX_MCP_JAR: relative });
|
||||
expect(rejected.code).toBe("capability_unavailable");
|
||||
expect(rejected.details.diagnostics).toEqual({
|
||||
jar_configured: true,
|
||||
jar_path: relative,
|
||||
path_requirement: "absolute",
|
||||
failure_kind: "jar",
|
||||
failure_code: "open_options_invalid",
|
||||
});
|
||||
expect(rejected.message).toContain(`received "${relative}"`);
|
||||
|
||||
const empty = await run({ REA_JADX_MCP_JAR: "" });
|
||||
expect(empty.details.diagnostics).toMatchObject({
|
||||
jar_configured: true,
|
||||
jar_path: "",
|
||||
failure_code: "open_options_invalid",
|
||||
});
|
||||
|
||||
const unset = await run({});
|
||||
expect(unset.details.diagnostics).toEqual({
|
||||
jar_configured: false,
|
||||
failure_kind: "jar",
|
||||
failure_code: "not_configured",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
cliTest.skipIf(process.platform !== "linux")(
|
||||
"preserves the missing firmware command reason through both caller paths",
|
||||
async ({ cli }) => {
|
||||
|
||||
@@ -133,6 +133,31 @@ it("publishes no registration environment when REA_JADX_MCP_JAR is unset", async
|
||||
});
|
||||
});
|
||||
|
||||
it("reports a relative REA_JADX_MCP_JAR as a rejected selection, not an unset one", async () => {
|
||||
const inspection = await inspectJadxProvider({
|
||||
REA_JADX_MCP_JAR: "tools/jadx-headless-mcp.jar",
|
||||
});
|
||||
|
||||
expect(inspection).toMatchObject({
|
||||
id: "jadx",
|
||||
configured: true,
|
||||
available: false,
|
||||
registrationEnvironment: {},
|
||||
checks: [
|
||||
{
|
||||
name: "configuration",
|
||||
ok: false,
|
||||
code: "open_options_invalid",
|
||||
classification: "config_drift",
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(inspection.checks[0]?.detail).toContain(
|
||||
'REA_JADX_MCP_JAR must be an absolute path; received "tools/jadx-headless-mcp.jar".',
|
||||
);
|
||||
expect(inspection.checks[0]?.remediation).toContain("REA_JADX_MCP_JAR");
|
||||
});
|
||||
|
||||
it("publishes no registration environment when the configured JAR fails validation", async () => {
|
||||
const root = await createTestTempDirectory("rea-jadx-doctor-invalid-");
|
||||
const jar = join(root, "engine.jar");
|
||||
|
||||
Reference in New Issue
Block a user