Files
rea/tests/acceptance/applications/adbPackageInventoryCoverage.test.ts
T
Kaoru0822 5b014a739e fix(adb): report unrecognized package rows from resolve-adb-packages (#5874)
resolve_adb_packages marked coverage partial when pm list packages printed
unrecognized rows, but dropped the rows themselves and could still emit a
limitation describing an unparsed_lines field the result never carried.

ADB parsers now own unrecognized-line facts as a bounded sample (first ten)
plus the exact total, and a single provider projection derives
unparsed_lines, coverage, and the truncation limitation from those facts for
every parsed listing and the package search. The resolver result schema now
carries unparsed_lines, matching list_adb_packages through CLI and MCP.

Fixes #5843

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 05:06:11 -07:00

105 lines
3.6 KiB
TypeScript

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { toolContract } from "../../../src/contracts/toolContracts.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const PACKAGE_LINE = "package:/data/app/example/base.apk=com.example.app";
const unrecognized = (count: number) =>
Array.from(
{ length: count },
(_, index) => `unrecognized package row ${String(index + 1)}`,
);
cliTest.skipIf(process.platform === "win32")(
"reports unrecognized package rows through package search and listing via CLI and MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-adb-package-coverage-");
const binary = join(root, "adb");
const inventory = join(root, "packages.txt");
await writeFile(
binary,
`#!${process.execPath}
const args = process.argv.slice(2);
if (args[0] === 'version') {
console.log('Android Debug Bridge version 1.0.41\\nVersion 34.0.5-fixture\\nInstalled as ' + process.argv[1]);
process.exit(0);
}
if (args.join(' ') === '-s emulator-5554 shell pm list packages -f') {
process.stdout.write(require('node:fs').readFileSync(${JSON.stringify(inventory)}));
process.exit(0);
}
process.exit(91);
`,
{ mode: 0o700 },
);
const environment = {
HOME: root,
USERPROFILE: root,
REA_ADB_PATH: binary,
REA_LOG_LEVEL: "silent",
};
const { call } = await connectLocalToolsMcp(environment);
for (const count of [0, 1, 11]) {
const rows = unrecognized(count);
await writeFile(inventory, [PACKAGE_LINE, ...rows, ""].join("\n"));
const expected = {
unparsed_lines: rows.slice(0, 10),
coverage: count === 0 ? "complete" : "partial",
};
const truncation =
"unparsed_lines reports the first 10 of 11 unrecognized lines";
for (const [command, operation, tail] of [
["resolve-adb-packages", "resolve_adb_packages", ["example"]],
["list-adb-packages", "list_adb_packages", []],
] as const) {
const output = await cli.run({
arguments: [command, "emulator-5554", ...tail, "--json"],
environment,
});
expect(output.exitCode, output.stderr).toBe(0);
const evidence = parseEvidence(output.json);
expect(
evidence.normalized_result,
`${command} ${String(count)}`,
).toMatchObject(expected);
if (count === 11) expect(evidence.limitations).toContain(truncation);
else
expect(evidence.limitations).not.toContain(
expect.stringContaining("unparsed_lines"),
);
if (operation !== "resolve_adb_packages") continue;
expect(evidence.normalized_result).toMatchObject({
matches: [
{
package_name: "com.example.app",
base_apk_device_path: "/data/app/example/base.apk",
},
],
exact_match: false,
});
const reply = await call(operation, {
serial: "emulator-5554",
query: "example",
});
expect(reply.isError).not.toBe(true);
const structured = toolContract(operation).outputSchema.parse(
reply.structuredContent,
);
expect(structured.normalized_result).toEqual(
evidence.normalized_result,
);
expect(structured.limitations).toEqual(evidence.limitations);
}
}
},
);