From f1edf0da0c6c897e68664a6ab2637f8f33368bf8 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 18 Oct 2026 04:21:19 +0800 Subject: [PATCH] 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 --- src/cli/apktoolCommands.ts | 8 +--- .../cli/cliInputSchemaRequired.test.ts | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/cli/apktoolCommands.ts b/src/cli/apktoolCommands.ts index b3a58d878..d870d918d 100644 --- a/src/cli/apktoolCommands.ts +++ b/src/cli/apktoolCommands.ts @@ -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 diff --git a/tests/boundary/cli/cliInputSchemaRequired.test.ts b/tests/boundary/cli/cliInputSchemaRequired.test.ts index efd72f10d..1bf778b7a 100644 --- a/tests/boundary/cli/cliInputSchemaRequired.test.ts +++ b/tests/boundary/cli/cliInputSchemaRequired.test.ts @@ -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; type CommandMap = NonNullable>; @@ -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(MAX_SELECTED_LOCALES).fill("de"), + Array(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({});