Files
rea/tests/boundary/cli/apktoolLocaleOption.test.ts
T
Ysopwnable 7c458f04b9 perf(apktool): avoid duplicated primary strings on request
Add opt-in string_tables_only and CLI --string-tables-only to keep selected
strings once in typed table facts. Preserve existing defaults and legacy replies;
charge aggregate output capacity for the copies actually requested.

Validate shared CLI/MCP behavior with independent XML oracles, three real APKs,
94 source tests, nine compiled boundary tests, 36 completed MCP tasks and three
CLI parity tasks. Preserve full task estimates, negative timings, resource
admission refusal and verification limitations in the measured report.

Fixes #4982

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-18 15:38:16 +08:00

165 lines
4.2 KiB
TypeScript

import { expect, it } from "vitest";
import { Cli } from "incur";
import pino from "pino";
import type { ApktoolResourceAnalysisPort } from "../../../src/application/apktool/ApktoolResourceAnalysisPort.js";
import { createAnalysisExecution } from "../../../src/application/AnalysisProvider.js";
import { registerApktoolCommands } from "../../../src/cli/apktoolCommands.js";
import { presentOmittableDefaults } from "../../../src/cli/omittableDefaults.js";
import type { ApktoolRequest } from "../../../src/domain/apktool/apktoolResourceAnalysis.js";
import { ok } from "../../../src/domain/result.js";
const invoke = async (argv: readonly string[]) => {
const requests: ApktoolRequest[] = [];
const provider: ApktoolResourceAnalysisPort = {
async inspectAvailability() {
throw new Error("unexpected provider probe");
},
async close() {},
async execute(request) {
requests.push(request);
return ok(
createAnalysisExecution(
{ strings: [] },
{ id: "apktool", name: "Apktool", version: null },
),
);
},
};
const cli = Cli.create("rea", { sync: false });
registerApktoolCommands(cli, pino({ level: "silent" }), {}, provider);
presentOmittableDefaults(cli);
let stdout = "";
let exitCode = 0;
await cli.serve([...argv], {
env: {},
exit: (code) => {
exitCode = code;
},
stdout: (text) => {
stdout += text;
},
});
return { exitCode, stdout, requests };
};
/**
* Locales the apktool input owner admits. The owner's own rejection message
* advertises `b+zh+Hans+CN`, so the CLI cannot be narrower than this list
* without refusing a caller-selected locale the decoding lane supports.
*/
const OWNER_ADMITTED_LOCALES = [
"de",
"pt-BR",
"pt-rBR",
"zh-rCN",
"en-419",
"b+zh+Hans+CN",
"b+sr+Latn+RS+REVISED",
"b+fil+Latn+419+ABCDEFGH",
] as const;
it("forwards the table-only string choice through the CLI", async () => {
const result = await invoke([
"decode-android-resources",
"/targets/app.apk",
"--string-tables-only",
"--locales",
"de",
"--json",
]);
expect(result.exitCode, result.stdout).toBe(0);
expect(result.requests).toEqual([
{
operation: "decode_android_resources",
input: {
path: "/targets/app.apk",
include_strings: true,
locales: ["de"],
string_tables_only: true,
},
},
]);
});
it("forwards every locale the apktool input owner admits", async () => {
for (const locale of OWNER_ADMITTED_LOCALES) {
const result = await invoke([
"decode-android-resources",
"/targets/app.apk",
"--locale",
locale,
"--json",
]);
expect(result.exitCode, `exit code for locale ${locale}`).toBe(0);
expect(result.requests, `request for locale ${locale}`).toEqual([
{
operation: "decode_android_resources",
input: { path: "/targets/app.apk", include_strings: true, locale },
},
]);
}
});
it("forwards repeated --locales to project multiple localized tables", async () => {
const result = await invoke([
"decode-android-resources",
"/targets/app.apk",
"--locales",
"de",
"--locales",
"pt-BR",
"--json",
]);
expect(result.exitCode).toBe(0);
expect(result.requests).toEqual([
{
operation: "decode_android_resources",
input: {
path: "/targets/app.apk",
include_strings: true,
locales: ["de", "pt-BR"],
},
},
]);
});
it("still refuses a locale the apktool input owner refuses", async () => {
const result = await invoke([
"decode-android-resources",
"/targets/app.apk",
"--locale",
"not a locale",
"--json",
]);
expect(result.exitCode).not.toBe(0);
expect(result.requests).toEqual([]);
});
it("forwards --no-strings with --locale preserving caller options", async () => {
const result = await invoke([
"decode-android-resources",
"/targets/app.apk",
"--no-strings",
"--locale",
"de",
"--json",
]);
expect(result.exitCode).toBe(0);
expect(result.requests).toEqual([
{
operation: "decode_android_resources",
input: {
path: "/targets/app.apk",
include_strings: false,
locale: "de",
},
},
]);
});