Files
reverse-engineering-anything/tests/boundary/cli/browserInputDiagnostics.test.ts
T
morlutoandCursor Agent b31fc5d5fb fix(cli): advertise domain constraints for selector and path options (#9181)
* fix(cli): advertise domain constraints for selector and path options

Android class, method, and mapping fields, ADB setting keys, and browser
endpoints used bare strings in --schema. They now reuse the domain fields,
so empty selectors, relative mapping paths, and malformed setting keys
fail in the advertised schema.

* fix(cli): keep selector limits when argument parsing rejects them

Empty browser and Electron selectors now fail in the CLI parser, so their
diagnostics are VALIDATION_ERROR with the one-character minimum. ADB
setting keys stay on the selector repair path, which restores maximum 256
and the identifier pattern.

* test(cli): assert empty-selector codes without frozen prose

Empty browser and Electron selectors still fail as VALIDATION_ERROR
too_small on the named field. The tests no longer copy Zod's sentence.

* test(cli): compare endpoint diagnostics with the domain schema

Browser and Inspector tests take custom issue text from the schema that
owns it, and empty capture comparison must name both missing inspection
fields.

* fix(cli): remove duplicate merged schema import

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-11-07 16:17:09 +08:00

358 lines
10 KiB
TypeScript

import { describe, expect } from "vitest";
import type { z } from "zod";
import {
inspectWebPageInputSchema,
listBrowserTargetsInputSchema,
} from "../../../src/domain/browserObservation.js";
import { listJavaScriptRuntimeTargetsInputSchema } from "../../../src/domain/javascript/javascriptRuntimeObservation.js";
import { analyzeWebBundleInputSchema } from "../../../src/domain/webBundleAnalysis.js";
import { discoverWebMcpToolsInputSchema } from "../../../src/domain/webMcpDiscovery.js";
import { workspaceCliTest as cliTest } from "../../support/cli/workspaceCliFixture.js";
const malformedEndpoint = "not a url";
const browserEndpoint = "http://127.0.0.1:9222";
const inspectorEndpoint = "http://127.0.0.1:9229";
/** The domain schema owns custom issue text. Tests compare the CLI to that issue. */
const schemaIssue = (
schema: z.ZodType,
value: unknown,
path: readonly (string | number)[],
) => {
const parsed = schema.safeParse(value);
if (parsed.success)
throw new Error(`domain schema accepted ${path.join(".")}`);
const issue = parsed.error.issues.find(
(item) => item.path.join(".") === path.join("."),
);
if (issue === undefined)
throw new Error(
`domain schema omitted ${path.join(".")}: ${JSON.stringify(parsed.error.issues)}`,
);
return issue;
};
const malformedEndpointInput = (operation: string) =>
operation === "list_browser_targets"
? { cdp_endpoint: malformedEndpoint }
: {
cdp_endpoint: malformedEndpoint,
target_id: "target",
...(operation === "analyze_web_bundle"
? { include_script_sources: true }
: {}),
};
const invalidInputs = [
{
command: "list-browser-targets",
arguments: [malformedEndpoint],
operation: "list_browser_targets",
error: "Browser observation failed",
issue: {
path: ["cdp_endpoint"],
reason: "invalid_value",
},
schema: listBrowserTargetsInputSchema,
},
{
command: "analyze-web-bundle",
arguments: [malformedEndpoint, "target"],
operation: "analyze_web_bundle",
error: "Browser observation failed",
issue: {
path: ["cdp_endpoint"],
reason: "invalid_value",
},
schema: analyzeWebBundleInputSchema,
},
{
command: "discover-webmcp-tools",
arguments: [malformedEndpoint, "target"],
operation: "discover_webmcp_tools",
error: "Browser observation failed",
issue: {
path: ["cdp_endpoint"],
reason: "invalid_value",
},
schema: discoverWebMcpToolsInputSchema,
},
];
const parserRejections = [
{
command: "inspect-web-page",
arguments: [browserEndpoint, ""],
path: "targetId",
},
{
command: "observe-web-session",
arguments: [browserEndpoint, ""],
path: "targetId",
},
{
command: "list-javascript-runtime-targets",
arguments: [""],
path: "endpoint",
},
{
command: "observe-javascript-runtime",
arguments: [inspectorEndpoint, ""],
path: "targetId",
},
{
command: "capture-web-screenshot",
arguments: [browserEndpoint, ""],
path: "targetId",
},
] as const;
describe("inspection branch diagnostics", () => {
cliTest.for([[], ["--include-script-sources"]])(
"reports only the selected inspection branch's endpoint error (%j)",
async (flags, { cli, workspace }) => {
const result = await cli.run({
arguments: [
"inspect-web-page",
malformedEndpoint,
"target",
...flags,
"--json",
],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
expect(result.exitCode).toBe(1);
const owner = schemaIssue(
inspectWebPageInputSchema,
{
cdp_endpoint: malformedEndpoint,
target_id: "target",
include_script_sources: flags.includes("--include-script-sources"),
},
["cdp_endpoint"],
);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
operation: "inspect_web_page",
issues: [
{
path: ["cdp_endpoint"],
reason: "invalid_value",
message: owner.message,
},
],
},
});
},
);
});
describe("browser and Inspector CLI input diagnostics", () => {
cliTest.for(invalidInputs)(
"$command retains the named contract's correction details",
async (input, { cli, workspace }) => {
const result = await cli.run({
arguments: [input.command, ...input.arguments, "--json"],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
const owner = schemaIssue(
input.schema,
malformedEndpointInput(input.operation),
input.issue.path,
);
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
error: input.error,
code: "invalid_request",
category: "invalid_input",
message:
"Analysis input is invalid. Check the arguments and try again.",
retryable: false,
remediation: {
action: `Read the declared input schema or CLI usage for ${input.operation}; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call.`,
},
recovery: {
kind: "inspect_input_contract",
operation: input.operation,
},
details: {
operation: input.operation,
issues: expect.arrayContaining([
{ ...input.issue, message: owner.message },
]),
},
});
expect(result.stdout).not.toContain(malformedEndpoint);
expect(result.stderr).not.toContain(malformedEndpoint);
},
);
cliTest.for([
["list-browser-targets", "cdp_endpoint"],
["list-javascript-runtime-targets", "inspector_endpoint"],
] as const)(
"%s does not echo rejected endpoint credentials",
async ([command, field], { cli, workspace }) => {
const endpoint =
"http://rea-diagnostic-user:rea-diagnostic-secret@127.0.0.1:9222/";
const result = await cli.run({
arguments: [command, endpoint, "--json"],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "debug" },
});
const owner = schemaIssue(
command === "list-browser-targets"
? listBrowserTargetsInputSchema
: listJavaScriptRuntimeTargetsInputSchema,
command === "list-browser-targets"
? { cdp_endpoint: endpoint }
: { inspector_endpoint: endpoint },
[field],
);
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
issues: [
{
path: [field],
reason: "invalid_value",
message: owner.message,
},
],
},
});
const output = result.stdout + result.stderr;
expect(output).not.toContain(endpoint);
expect(output).not.toContain("rea-diagnostic-user");
expect(output).not.toContain("rea-diagnostic-secret");
},
);
});
describe("browser correction facts", () => {
cliTest(
"retains authored cross-field correction guidance",
async ({ cli, workspace }) => {
const result = await cli.run({
arguments: [
"inspect-web-page",
browserEndpoint,
"target",
"--include-storage-fingerprints",
"--json",
],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
const owner = schemaIssue(
inspectWebPageInputSchema,
{
cdp_endpoint: browserEndpoint,
target_id: "target",
include_script_sources: false,
include_storage_fingerprints: true,
},
["include_storage_fingerprints"],
);
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
operation: "inspect_web_page",
issues: expect.arrayContaining([
{
path: ["include_storage_fingerprints"],
reason: "invalid_value",
message: owner.message,
},
]),
},
});
},
);
cliTest(
"retains correction details for invalid capture comparison inputs",
async ({ cli, workspace }) => {
const result = await cli.run({
arguments: ["compare-web-captures", "{}", "{}", "--json"],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
operation: "compare_web_captures",
issues: expect.arrayContaining([
expect.objectContaining({
path: ["before", "inspection"],
reason: "missing_argument",
}),
expect.objectContaining({
path: ["after", "inspection"],
reason: "missing_argument",
}),
]),
},
});
},
);
cliTest(
"preserves the parser diagnostic for an omitted endpoint",
async ({ cli, workspace }) => {
const result = await cli.run({
arguments: ["list-browser-targets", "--json"],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: [
{
path: "endpoint",
code: "invalid_type",
missing: true,
expected: "string",
},
],
});
},
);
});
describe("empty browser and Inspector selectors", () => {
cliTest.for(parserRejections)(
"$command rejects an empty selector in argument parsing",
async (input, { cli, workspace }) => {
const result = await cli.run({
arguments: [input.command, ...input.arguments, "--json"],
cwd: workspace.root,
environment: { ...workspace.environment, REA_LOG_LEVEL: "silent" },
});
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: [
{
code: "too_small",
path: input.path,
},
],
});
},
);
});