fix(adb): share host configuration refusals with apktool (#1954)

The ADB provider, which the apktool provider mirrors, had the same two
defects: a relative REA_ADB_PATH escaped as UNKNOWN because selection
ran before the try that maps configuration failures, and a missing adb
binary projected generic target advice instead of its reason.

Move the configuration-failure-to-refusal mapping into one
providerConfigurationRefusal helper used by both providers, resolve the
ADB selection inside the mapped try, and share the CLI refusal assertion.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
Kaoru0822
2026-10-11 14:09:13 -07:00
committed by GitHub
parent 7d92d93a55
commit 39418ea173
3 changed files with 45 additions and 29 deletions
+23 -19
View File
@@ -1,6 +1,9 @@
import { expect, it } from "vitest";
import { serveCli as serve } from "./cliServeHarness.js";
import {
expectConfigurationRefusal,
serveCli as serve,
} from "./cliServeHarness.js";
it.each([
["inspect-adb-client"],
@@ -35,25 +38,26 @@ it.each([
});
it.each([
["inspect-adb-client", "/nonexistent/adb", "not found"],
["list-adb-devices", "relative/adb", "absolute path"],
[
["inspect-adb-client", "--json"],
"/nonexistent/adb",
"The selected adb binary was not found: /nonexistent/adb. Set REA_ADB_PATH",
],
[
["list-adb-devices", "--json"],
"/nonexistent/adb",
"The selected adb binary was not found: /nonexistent/adb. Set REA_ADB_PATH",
],
[
["list-adb-devices", "--json"],
"relative/adb",
"REA_ADB_PATH must be an absolute path to the adb binary.",
],
])(
"%s names the adb configuration requirement",
async (command, binary, cause) => {
const result = await serve([command, "--json"], {
REA_ADB_PATH: binary,
});
const output = JSON.parse(result.stdout) as {
code?: unknown;
message?: unknown;
remediation?: { action?: unknown };
};
expect(output.code).toBe("capability_unavailable");
expect(output.message).toEqual(expect.stringContaining(cause));
expect(output.message).toEqual(expect.stringContaining("REA_ADB_PATH"));
expect(output.remediation?.action).toEqual(
expect.stringContaining("REA_ADB_PATH"),
);
"%j with REA_ADB_PATH=%s names the host requirement",
async (argv, binary, reason) => {
const result = await serve(argv, { REA_ADB_PATH: binary });
expectConfigurationRefusal(result, reason);
},
);
+5 -10
View File
@@ -1,6 +1,9 @@
import { expect, it } from "vitest";
import { serveCli as serve } from "./cliServeHarness.js";
import {
expectConfigurationRefusal,
serveCli as serve,
} from "./cliServeHarness.js";
it.each(["inspect-apktool-client", "decode-android-resources"])(
"%s advertises a schema through the public CLI",
@@ -32,15 +35,7 @@ it.each([
"%j with REA_APKTOOL_COMMAND=%s names the host requirement",
async (argv, command, reason) => {
const result = await serve(argv, { REA_APKTOOL_COMMAND: command });
const output = JSON.parse(result.stdout) as {
code?: unknown;
message?: unknown;
remediation?: { action?: unknown };
};
expect(output.code).toBe("capability_unavailable");
// The target is not the problem; the refusal must say what to configure.
expect(output.message).toEqual(expect.stringContaining(reason));
expect(output.remediation?.action).toEqual(expect.stringContaining(reason));
expectConfigurationRefusal(result, reason);
},
);
+17
View File
@@ -1,3 +1,5 @@
import { expect } from "vitest";
import { createCli } from "../../../src/cli.js";
/** Captured CLI invocation for boundary tests. */
@@ -25,3 +27,18 @@ export const serveCli = async (
});
return { stdout, exitCode };
};
/** A host tool configuration refusal names what to configure, not the target. */
export const expectConfigurationRefusal = (
result: ServedCli,
reason: string,
): void => {
const output = JSON.parse(result.stdout) as {
code?: unknown;
message?: unknown;
remediation?: { action?: unknown };
};
expect(output.code).toBe("capability_unavailable");
expect(output.message).toEqual(expect.stringContaining(reason));
expect(output.remediation?.action).toEqual(expect.stringContaining(reason));
};