fix(cli): preserve APK locale-list schema bounds

Reuse the named domain locales field instead of reconstructing an optional
array from its element type. Preserve min/max selection bounds through the
CLI parser and public JSON Schema while retaining the repeat-flag guidance.
The application service already validates the input; provider authority is
unchanged.

Add a public schema regression and compare advertised Ajv2020 acceptance
and CLI parser acceptance with the owner for omission, empty, valid,
maximum, oversized and invalid-element selections.

Validation: regression fails unchanged d37c34647; 21 focused checks across
4 files, check:fast and docs:check pass. Source review found no blocker.
No real-provider or performance qualification claim. Heavy tests remain
stopped.

Fixes #4876
This commit is contained in:
morluto committed 2026-10-18 04:21:19 +08:00
1 parent d37c346477
commit f1edf0da0c
2 files changed
+40 -6

No files matched your search

+2 -6
View File
@@ -54,12 +54,8 @@ export const registerApktoolCommands = (
// `b+zh+Hans+CN` form at the CLI boundary.
options: z.object({
locale: apktoolInputSchemas.decode_android_resources.shape.locale,
locales: z
.array(
apktoolInputSchemas.decode_android_resources.shape.locale.unwrap(),
)
.optional()
.describe(
locales:
apktoolInputSchemas.decode_android_resources.shape.locales.describe(
"Repeat to project multiple localized string tables, e.g. --locales de --locales pt-BR",
),
strings: z
@@ -4,6 +4,10 @@ import { describe, expect, it } from "vitest";
import { createCli } from "../../../src/cli.js";
import { createCliInventory } from "../../../scripts/lib/catalog-cli.mjs";
import {
apktoolInputSchemas,
MAX_SELECTED_LOCALES,
} from "../../../src/domain/apktool/apktoolResourceAnalysis.js";
type CliInstance = ReturnType<typeof createCli>;
type CommandMap = NonNullable<ReturnType<typeof Cli.toCommands.get>>;
@@ -201,6 +205,40 @@ describe("web module CLI advertised input schema", () => {
});
});
describe("Apktool locale selection input schema", () => {
it("preserves the owner's array bounds through CLI advertisement and parsing", async () => {
const cli = createCli({});
const advertised = await advertisedSchema(cli, "decode-android-resources");
expect(advertised.options?.properties?.locales).toMatchObject({
type: "array",
minItems: 1,
maxItems: MAX_SELECTED_LOCALES,
});
const parser = parserSchema(cli, "decode-android-resources", "options");
const ajv = new Ajv2020({ strict: false });
const validate = ajv.compile({
...advertised.options,
$schema: "https://json-schema.org/draft/2020-12/schema",
});
for (const locales of [
undefined,
[],
["de"],
Array<string>(MAX_SELECTED_LOCALES).fill("de"),
Array<string>(MAX_SELECTED_LOCALES + 1).fill("de"),
["not a locale"],
]) {
const input = locales === undefined ? {} : { locales };
const expected = apktoolInputSchemas.decode_android_resources.safeParse({
path: "/targets/app.apk",
...input,
}).success;
expect(parser?.safeParse(input).success).toBe(expected);
expect(validate(input)).toBe(expected);
}
});
});
describe("reference-source outline-depth input schema", () => {
it("aligns advertised bounds with the parser's accepted values", async () => {
const cli = createCli({});