test(cli): consolidate schema coverage and assert input refusals

Remove 30 redundant truthy-JSON schema checks. Validate advertised groups and property parity across every primary command, retain required-field checks, and assert structured malformed-input errors. Exercise JEB positional validation rather than an unknown flag.

Validation: 13 public CLI adapter tests, combined checks, and temporary-directory hygiene. Host configuration refusal coverage from main is preserved.
This commit is contained in:
morluto
2026-10-12 05:24:33 +08:00
parent 108b5f9425
commit beb80a07ba
4 changed files with 47 additions and 68 deletions
+4 -33
View File
@@ -5,38 +5,6 @@ import {
serveCli as serve,
} from "./cliServeHarness.js";
it.each([
["inspect-adb-client"],
["list-adb-devices"],
["inspect-adb-device"],
["list-adb-packages"],
["pull-adb-package"],
["read-adb-logcat"],
["inspect-adb-package"],
["pull-adb-file"],
["push-adb-file"],
["capture-adb-screen"],
["list-adb-processes"],
["list-adb-directory"],
["list-adb-features"],
["list-adb-services"],
["inspect-adb-display"],
["inspect-adb-window"],
["read-adb-setting"],
["collect-adb-bugreport"],
["resolve-adb-packages"],
["install-adb-package"],
["uninstall-adb-package"],
["start-adb-app"],
["start-adb-activity"],
["stop-adb-app"],
])("%s advertises a schema through the public CLI", async (command) => {
const result = await serve([command, "--schema", "--json"]);
expect(result.exitCode, command).toBe(0);
const schema = JSON.parse(result.stdout) as { args?: unknown };
expect(schema).toBeTruthy();
});
it.each([
[
["inspect-adb-client", "--json"],
@@ -63,7 +31,10 @@ it.each([
it("validates device serials before contacting any device", async () => {
const result = await serve(["inspect-adb-device", "bad serial", "--json"]);
expect(result.stdout).toContain("invalid_input");
expect(JSON.parse(result.stdout)).toMatchObject({
code: "invalid_request",
category: "invalid_input",
});
});
it("requires the output directory option on pull", async () => {
+10 -11
View File
@@ -5,16 +5,6 @@ import {
serveCli as serve,
} from "./cliServeHarness.js";
it.each(["inspect-apktool-client", "decode-android-resources"])(
"%s advertises a schema through the public CLI",
async (command) => {
const result = await serve([command, "--schema", "--json"]);
expect(result.exitCode, command).toBe(0);
const schema = JSON.parse(result.stdout) as { args?: unknown };
expect(schema).toBeTruthy();
},
);
it.each([
[
["inspect-apktool-client", "--json"],
@@ -47,5 +37,14 @@ it("validates the locale option before launching apktool", async () => {
"not a locale",
"--json",
]);
expect(result.stdout).toContain("invalid");
expect(result.exitCode).toBe(1);
expect(JSON.parse(result.stdout)).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: [
{
code: "invalid_format",
path: "locale",
},
],
});
});
@@ -13,7 +13,7 @@ const registeredCommands: WeakMap<object, CommandMap> = Cli.toCommands;
const advertisedGroupSchema = z
.object({
properties: z.record(z.string(), z.unknown()).optional(),
properties: z.record(z.string(), z.unknown()),
required: z.array(z.string()).optional(),
})
.loose();
@@ -70,8 +70,27 @@ describe("CLI advertised input schemas", () => {
for (const command of createCliInventory(cli).primary) {
const advertised = await advertisedSchema(cli, command);
for (const group of INPUT_GROUPS) {
const required = (advertised[group]?.required ?? []).toSorted();
const rejected = omissionRejected(parserSchema(cli, command, group));
const parser = parserSchema(cli, command, group);
const advertisedGroup = advertised[group];
if (parser === undefined) {
if (advertisedGroup !== undefined)
mismatches.push(`${command} ${group}: advertised without parser`);
continue;
}
if (advertisedGroup === undefined) {
mismatches.push(`${command} ${group}: parser group not advertised`);
continue;
}
const parserProperties = Object.keys(parser.shape).toSorted();
const advertisedProperties = Object.keys(
advertisedGroup.properties,
).toSorted();
if (advertisedProperties.join() !== parserProperties.join())
mismatches.push(
`${command} ${group}: advertised properties [${advertisedProperties.join()}], parser properties [${parserProperties.join()}]`,
);
const required = (advertisedGroup.required ?? []).toSorted();
const rejected = omissionRejected(parser);
if (required.join() !== rejected.join())
mismatches.push(
`${command} ${group}: advertised [${required.join()}], parser rejects [${rejected.join()}]`,
+11 -21
View File
@@ -11,18 +11,6 @@ const serve = async (
): Promise<{ readonly stdout: string; readonly exitCode: number }> =>
serveCli(argv, jebEnvironment);
it.each([
["inspect-jeb-client"],
["open-jeb-project"],
["list-jeb-units"],
["decompile-jeb-item"],
])("%s advertises a schema through the public CLI", async (command) => {
const result = await serve([command, "--schema", "--json"]);
expect(result.exitCode, command).toBe(0);
const schema = JSON.parse(result.stdout) as { args?: unknown };
expect(schema).toBeTruthy();
});
it("reports an unreachable engine endpoint with recovery guidance", async () => {
const result = await serve(["inspect-jeb-client", "--json"]);
expect(result.stdout).toContain("capability_unavailable");
@@ -30,13 +18,15 @@ it("reports an unreachable engine endpoint with recovery guidance", async () =>
});
it("validates decompile item input before contacting the engine", async () => {
const result = await serve([
"decompile-jeb-item",
"not-an-address",
"--item-kind",
"method",
"--json",
]);
expect(result.stdout).toBeTruthy();
expect(result.stdout).not.toContain("capability_unavailable");
const result = await serve(["decompile-jeb-item", "", "method", "--json"]);
expect(result.exitCode).toBe(1);
expect(JSON.parse(result.stdout)).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: [
{
code: "too_small",
path: "item-address",
},
],
});
});