From aa9c62145c3988f51d34e0df1b03a20bd5f47f11 Mon Sep 17 00:00:00 2001 From: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:57:37 +0000 Subject: [PATCH] test(cli): cover remaining parser commands --- CHANGELOG.md | 2 + .../HeadlessProtocolTests/ProtocolTests.swift | 67 ++++++++++++++++++- docs/roadmap/improvements-backlog.md | 5 ++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0415a8e..6b042c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ Cutting that release is tracked in - An MCP stdio integration suite covering initialization, tool discovery, browser-command calls, malformed and oversized input, and rejection of local CLI commands. +- CLI parser coverage for the previously untested session, navigation, + diagnostics, flow, network emulation, reporting, and local command paths. - Progressive context pruning: `inspect --context summary|outline|text|actions|full` with `--task` ranking, `--within @rN` scoping, and `--limit` / `--budget` / `--depth` bounds. Every focused response reports `contextStats` and `omitted`. diff --git a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift index d3745da..a50f266 100644 --- a/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift +++ b/apps/headless/Tests/HeadlessProtocolTests/ProtocolTests.swift @@ -107,7 +107,11 @@ struct ProtocolTests { } static func rejectsUnexpectedRequestFields() throws { - let data = Data(#"{"id":"request-1","version":"0.1","command":"ping","parameters":{},"execute":"anything"}"#.utf8) + let valid = Data(#"{"id":"request-1","version":"0.4","command":"ping","parameters":{}}"#.utf8) + let decoded = try ProtocolCodec.decodeLine(CommandRequest.self, from: valid) + try expect(decoded.command == .ping, "the strict-field control request should decode") + + let data = Data(#"{"id":"request-1","version":"0.4","command":"ping","parameters":{},"execute":"anything"}"#.utf8) try expectThrows("unexpected top-level request fields should be rejected") { _ = try ProtocolCodec.decodeLine(CommandRequest.self, from: data) } @@ -502,6 +506,66 @@ struct ProtocolTests { } } + static func cliCommandMatrix() throws { + let remoteCommands: [([String], CommandName)] = [ + (["status"], .ping), + (["stop"], .shutdown), + (["session", "create", "qa"], .sessionCreate), + (["session", "list"], .sessionList), + (["session", "close", "qa"], .sessionClose), + (["back"], .back), + (["reload"], .reload), + (["tour", "--pace", "750"], .tour), + (["capture-info"], .captureInfo), + (["artifacts", "list"], .artifactList), + (["qa", "report"], .qaReport), + (["qa", "clear"], .qaClear), + (["performance", "get"], .performanceGet), + (["animations", "list"], .animationList), + (["report", "create", "--output", "report.json"], .reportCreate), + (["flow", "start"], .flowStart), + (["flow", "stop", "--output", "flow.json"], .flowStop), + (["network", "emulate", "--offline", "--latency", "100"], .networkEmulate), + (["network", "mock", "clear"], .networkMockClear), + ] + for (arguments, command) in remoteCommands { + let invocation = try CLIParser().parse(arguments) + try expect( + invocation.request?.command == command, + "\(arguments.joined(separator: " ")) should parse as \(command.rawValue)" + ) + } + + let localCommands: [([String], LocalCommand)] = [ + (["start"], .start), + (["help"], .help), + (["--help"], .help), + ] + for (arguments, command) in localCommands { + let invocation = try CLIParser().parse(arguments) + try expect( + invocation.local == command && invocation.request == nil, + "\(arguments.joined(separator: " ")) should stay local" + ) + } + + let sessionCreate = try CLIParser().parse(["session", "create", "qa"]) + try expect(sessionCreate.request?.parameters["name"] == .string("qa"), "session create name should parse") + let sessionClose = try CLIParser().parse(["session", "close", "qa"]) + try expect(sessionClose.request?.session == "qa", "session close target should parse") + let tour = try CLIParser().parse(["tour", "--pace", "750"]) + try expect(tour.request?.parameters["pace"] == .number(750), "tour pace should parse") + let emulation = try CLIParser().parse([ + "network", "emulate", "--offline", "--latency", "100", + "--download-kbps", "2000", "--upload-kbps", "500", + ]) + try expect(emulation.request?.parameters["offline"] == .bool(true), "offline emulation should parse") + try expect(emulation.request?.parameters["latencyMs"] == .number(100), "emulation latency should parse") + try expectThrows("unknown trailing arguments should not be ignored") { + _ = try CLIParser().parse(["performance", "get", "extra"]) + } + } + static func chromiumRuntimeSelection() throws { let runtimeInvocation = try CLIParser().parse(["runtime"]) try expect(runtimeInvocation.local == .runtime, "runtime diagnostics command should parse") @@ -924,6 +988,7 @@ struct ProtocolTests { ("client timeout parity", clientTimeoutsMatchCommandBounds), ("CLI P1 artifacts", cliP1Artifacts), ("CLI P2 commands and boundaries", cliP2CommandsAndBoundaries), + ("CLI command matrix", cliCommandMatrix), ("Chromium runtime selection", chromiumRuntimeSelection), ("artifact store round-trip", artifactStoreRoundTrip), ("screenshot series helpers", screenshotSeriesHelpers), diff --git a/docs/roadmap/improvements-backlog.md b/docs/roadmap/improvements-backlog.md index 1d9c983..73188b3 100644 --- a/docs/roadmap/improvements-backlog.md +++ b/docs/roadmap/improvements-backlog.md @@ -277,6 +277,11 @@ flow start-stop/network emulate/mock clear/session ops/status/stop/start/help). Also tighten `rejectsUnexpectedRequestFields` (`ProtocolTests.swift:109-114`) which currently passes for the wrong reason. +**Progress:** MCP stdio coverage now exercises C3 end to end. The audited CLI +command matrix is covered, and the unexpected-field test first proves its +current-version control request decodes before adding the forbidden field. +Recording, flow, artifact, runtime, transport, and host-specific gaps remain. + **D3. Web CI:** ~~`next build` + eslint on PR (site can break invisibly today).~~ **Done** — the `web` job in `ci.yml` runs `pnpm --filter @headless/web lint` and `build`.