From 852dfcd282f605bdc44c52452fc67cb088ec6d40 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Sun, 25 Oct 2026 20:46:39 -0700 Subject: [PATCH] test: remove redundant browser and transport coverage (#33757) * test: remove redundant browser and transport coverage * test: retire deleted suites from reliability manifest --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 4 - config/reliability-gates.jsonc | 32 +- ...use-mobile-native-chat-input-lease.test.ts | 23 -- .../agent-browser-process-environment.test.ts | 83 ----- .../agent-browser-session-reset.test.ts | 50 --- .../browser/agent-input-focus-guard.test.ts | 14 +- .../browser-client-download-relay.test.ts | 65 ---- ...er-client-file-channel-negotiation.test.ts | 101 ------ ...browser-client-host-attach-request.test.ts | 71 ---- ...-client-host-authority-replacement.test.ts | 67 ---- ...r-client-host-published-url-wiring.test.ts | 136 -------- ...owser-client-page-command-executor.test.ts | 135 -------- ...ser-client-page-metadata-transport.test.ts | 53 --- .../browser-client-page-published-url.test.ts | 111 ------ ...lient-page-reconciliation-adapters.test.ts | 26 -- ...browser-client-page-unavailability.test.ts | 10 - ...browser-client-page-upload-routing.test.ts | 42 +-- ...browser-client-route-cookie-import.test.ts | 35 -- .../browser-client-upload-command.test.ts | 78 +---- .../browser-client-upload-staging.test.ts | 36 +- .../browser-client-upload-transfer.test.ts | 56 +--- ...er-execution-host-storage-identity.test.ts | 216 ------------ .../browser-external-app-launch.test.ts | 5 - .../browser/browser-google-auth-ua.test.ts | 65 ---- .../browser-guest-context-menu.test.ts | 317 ------------------ .../browser-guest-shortcut-forwarding.test.ts | 194 +---------- .../browser-host-client-identity.test.ts | 13 +- ...browser-host-lease-reconnect-delay.test.ts | 47 --- .../browser-identity-mode-record.test.ts | 91 ----- ...wser-link-materialization.electron.test.ts | 35 -- .../browser-manager-annotation-bridge.test.ts | 22 -- .../browser-manager-auth-user-agent.test.ts | 38 --- ...er-manager-client-hosted-downloads.test.ts | 28 -- .../browser-manager-destroyed-guest.test.ts | 8 - .../project-host-setup-projection.test.ts | 67 ---- .../pty-owner-color-query-colors.test.ts | 10 - ...ty-startup-ingress-lifetime-colors.test.ts | 10 - ...y-startup-ingress-live-query-reply.test.ts | 8 - src/shared/pty-startup-ingress.test.ts | 38 --- .../pty-startup-reply-echo-shapes.test.ts | 27 -- .../published-pane-agent-identity.test.ts | 5 - src/shared/pull-request-generation.test.ts | 43 --- src/shared/quick-open-filter.test.ts | 48 --- .../quick-open-transport-budget.test.ts | 8 +- .../raster-image-base64-preview.test.ts | 245 +------------- .../react-update-depth-attribution.test.ts | 10 - src/shared/release-channel.test.ts | 31 -- src/shared/remote-runtime-client.test.ts | 105 +----- ...remote-runtime-socket-resume-probe.test.ts | 43 --- ...-runtime-transport-error-agreement.test.ts | 34 -- ...-runtime-shared-control-connection.test.ts | 126 ------- ...e-shared-control-keepalive-refresh.test.ts | 9 - ...ime-shared-control-standing-intent.test.ts | 25 -- ...untime-client-error-classification.test.ts | 42 --- .../renderer-restart-preparation.test.ts | 21 -- src/shared/repo-display-labels.test.ts | 21 -- src/shared/repo-host-identity.test.ts | 19 +- src/shared/repo-ref-maintenance.test.ts | 14 - src/shared/repo-routing-connection-id.test.ts | 12 - ...9-claude-quarter-circle-busy-title.test.ts | 20 -- src/shared/resolved-worktree-lineage.test.ts | 10 - tests/e2e/browser-settings-preview.spec.ts | 46 --- tests/e2e/browser-tab.spec.ts | 50 --- ...ent-hosted-browser-tooltip-preview.spec.ts | 136 -------- 64 files changed, 23 insertions(+), 3567 deletions(-) delete mode 100644 src/main/browser/agent-browser-process-environment.test.ts delete mode 100644 src/main/browser/agent-browser-session-reset.test.ts delete mode 100644 src/main/browser/browser-client-host-attach-request.test.ts delete mode 100644 src/main/browser/browser-client-host-published-url-wiring.test.ts delete mode 100644 src/main/browser/browser-client-page-published-url.test.ts delete mode 100644 src/main/browser/browser-execution-host-storage-identity.test.ts delete mode 100644 src/main/browser/browser-google-auth-ua.test.ts delete mode 100644 src/main/browser/browser-guest-context-menu.test.ts delete mode 100644 src/main/browser/browser-host-lease-reconnect-delay.test.ts delete mode 100644 src/main/browser/browser-identity-mode-record.test.ts delete mode 100644 tests/e2e/browser-settings-preview.spec.ts delete mode 100644 tests/e2e/client-hosted-browser-tooltip-preview.spec.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 0d169f0b99c..c4313787f22 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -497,11 +497,9 @@ 1 test-no-real-time-wait:tests/e2e/artificial-opencode-revisit-pressure-scenario.ts 1 test-no-real-time-wait:tests/e2e/artificial-opencode-scroll-scenario.ts 1 test-no-real-time-wait:tests/e2e/browser-guest-crash-recovery.spec.ts -1 test-no-real-time-wait:tests/e2e/browser-settings-preview.spec.ts 1 test-no-real-time-wait:tests/e2e/browser-tab.spec.ts 9 test-no-real-time-wait:tests/e2e/chinese-ime-chat-input-repro.spec.ts 2 test-no-real-time-wait:tests/e2e/cli-floating-file-open-position.spec.ts -1 test-no-real-time-wait:tests/e2e/client-hosted-browser-tooltip-preview.spec.ts 1 test-no-real-time-wait:tests/e2e/codex-ctrl-c-status.spec.ts 4 test-no-real-time-wait:tests/e2e/combined-diff-scroll-restore.spec.ts 1 test-no-real-time-wait:tests/e2e/computer-windows.e2e.ts @@ -1366,9 +1364,7 @@ 1 test-timeout-needs-reason:tests/e2e/agent-session-log-tail-stability.spec.ts 1 test-timeout-needs-reason:tests/e2e/browser-agent-input-focus-isolation-native.spec.ts 5 test-timeout-needs-reason:tests/e2e/browser-agent-input-focus-isolation.spec.ts -1 test-timeout-needs-reason:tests/e2e/browser-settings-preview.spec.ts 1 test-timeout-needs-reason:tests/e2e/cli-agent-unopened-workspace-resume.spec.ts -1 test-timeout-needs-reason:tests/e2e/client-hosted-browser-tooltip-preview.spec.ts 1 test-timeout-needs-reason:tests/e2e/combined-diff-invalidation-freeze-repro.spec.ts 1 test-timeout-needs-reason:tests/e2e/completed-worker-retirement-resume.spec.ts 2 test-timeout-needs-reason:tests/e2e/cross-version-wire/agent-session-clear-old-client.unit.test.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 97126e2ccfb..e113f45b615 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -6790,7 +6790,7 @@ "providers": ["local", "remote-runtime", "ssh", "wsl"], "coveredPlatforms": ["macos", "linux"], "coveredProviders": ["remote-runtime", "ssh"], - "coverageNotes": "Deterministic protocol, registry, injected-socket, and loopback-listener tests cover strict framing, remote-DNS targets, exact destination-write and source-consumption credit, at most 16 pending opens, 128 admitted opens per 10-second monotonic window, an 8 MiB per-route application-buffer ledger, shared 32 MiB browser-host and 128 MiB process ledgers across application copies, encrypted client queues, and native WebSocket bufferedAmount, bounded byte/claim/socket-source counts, a four-frame queued-drain quantum, authority epochs, exact host selection, one host per authenticated connection, four hosts per paired device, eight global browser-host polls with four per authenticated paired device, a shared ask/host ceiling that retains one quarter for waits, bounded initial and reconnect runtime_busy recovery, long-poll metering and disconnect abort, monotonic host/page/route generations without page tombstones, two-phase exact page retirement with cancellation, connection-owned cleanup, exact client revocation, stale and replaced fences, retired stream IDs, half-close and close ordering, SOCKS CONNECT, bind/close races, listener wildcard normalization, unsupported commands, unavailable routes, and raw/terminal binary-handler isolation. Page commands use a separately echoed v1 attach negotiation, exact authority/host/page generations, bounded command IDs and sequences, and bounded create/navigate payloads; legacy attaches still receive only the unchanged ready/revoked event shapes. A second optional reconciliation subprotocol gates bounded reclaim, close, and restore payloads behind exact attach/ready echo, complete inventory, command negotiation, reconnect authority, and command-result authority; the production client advertises it only with the matching command and inventory capabilities. Exact guest or app-renderer loss marks one page generation outcome-unknown, coalesces a bounded negotiated inventory reattach, closes or retires the dead generation, and allocates a fresh generation before URL restore; explicit close is not misclassified as a crash. Mixed-version mutation tests project hidden client pages before activate, close, split, reorder, and move-to-group admission, preserve hidden raw order slots, translate visible insertion indices, and project mutation snapshots. A production server orchestrator consumes each immutable inventory once, reserves target generations without exposing placement, emits only negotiated ledger commands, commits after exact completed proof, preserves unrelated and server placements, aborts an attempt when connection authority enters reconnect grace, and requires fresh inventory after failure or abort. The production client dispatcher additionally proves per-page FIFO execution, exact payload-matched duplicate replay, frozen command/result snapshots, a global retired-generation floor, transactional admission, bounded pages/active commands/queues/per-page and global result cache/concurrency, create dependency failure, cancellation, deduplicated retirement joining, and bounded close without late-result overwrite. The server ledger owns issue order and immutable command/result snapshots, bounds outstanding commands, active pages, and per-page/global replay caches, releases active-page capacity after an exact completed close while retaining bounded result replay, validates the shared wire payload before admission, requires live delivery and exact placement, authenticates results to the negotiated connection and paired lease, rejects gaps and conflicting replay, and fences outstanding outcomes at exact retirement. Negotiated command results reuse the authenticated attach socket through bounded nested JSON requests; exact ID routing, reverse-order replies, unknown and duplicate IDs, timeout teardown, serialization failure, aggregate queue accounting, acknowledgement validation, and the unchanged non-v1 path are deterministic. A stable local listener rejects CONNECT while offline or reconnecting, retains its address across replacement, requires a strictly increasing tunnel generation, ignores late superseded callbacks, propagates tunnel protocol failure to the route owner, and recycles exhausted stream IDs only after generation replacement. Reconnect uses the unchanged native v1 attach payload and capability pair; SSH descriptors alone add an execution-host capability and require a runtime-minted grant bound to the exact browser-host lease. Exact SSH provider epoch and connection generation fence ssh2 forwardOut and one non-interactive standalone system-SSH dynamic forward per route. Unit tests preserve domain-form SOCKS requests, sanitize remote errors, bound stderr, cancel startup, release timed-out and synchronously failed sockets, and release routes once. An ephemeral Docker sshd resolves a container-only domain and returns a unique HTTP marker through both ssh2 and the actual system-OpenSSH dynamic-forward adapter without touching the user's SSH files; authority loss fences the ssh2 route. The execution runtime charges route application bytes to the same per-host/process policy; its existing E2EE owner separately caps native outbound buffers process-wide. Production-registered browser-host and paired-runtime methods lease one exact host, prove attach, command delivery, and result settlement share one exact connection identity, then carry SOCKS and HTTP bytes over a dedicated E2EE socket to the fenced execution-host revision and prove route close destroys the destination socket. The production desktop adapter now composes one exact host per environment pairing revision with the page executor, current renderer selector, route Session/WebContents registries, and one reference-counted route per canonical execution-host key. Negotiated same-client control reconnect retains exact authority, placements, grants, dispatcher dedupe, executor guests, and listener addresses; it fences tunnels immediately, blocks route admission, reattaches command delivery only after ready, and replays unsettled commands without repeating completed mutations. Terminal release, replacement, legacy disconnect, and reconnect-grace expiry make only the exact host generation's client placements non-cancellable retirement-pending while retaining capacity until exact cleanup; reconnect grace preserves them. Environment replacement and app shutdown still serialize transport closure before page cleanup and force-close every remaining route. The production placement preparation starts the exact desktop adapter and advertises host/tunnel capabilities only when the paired Electron client is eligible. Node stream-internal high-water bytes, strict cross-route scheduling, and physical cross-platform evidence remain uncovered. The two Docker remote-only SSH browser routing journeys now run in the dedicated Linux ssh-browser-network-route CI job on full runs and their mapped source/test changes; 2 baseline and 6 repeated cases passed with no skips/retries on 2026-09-06.", + "coverageNotes": "Deterministic protocol, registry, injected-socket, and loopback-listener tests cover strict framing, remote-DNS targets, exact destination-write and source-consumption credit, at most 16 pending opens, 128 admitted opens per 10-second monotonic window, an 8 MiB per-route application-buffer ledger, shared 32 MiB browser-host and 128 MiB process ledgers across application copies, encrypted client queues, and native WebSocket bufferedAmount, bounded byte/claim/socket-source counts, a four-frame queued-drain quantum, authority epochs, exact host selection, one host per authenticated connection, four hosts per paired device, eight global browser-host polls with four per authenticated paired device, a shared ask/host ceiling that retains one quarter for waits, bounded initial and reconnect runtime_busy recovery, long-poll metering and disconnect abort, monotonic host/page/route generations without page tombstones, two-phase exact page retirement with cancellation, connection-owned cleanup, exact client revocation, stale and replaced fences, retired stream IDs, half-close and close ordering, SOCKS CONNECT, bind/close races, listener wildcard normalization, unsupported commands, unavailable routes, and raw/terminal binary-handler isolation. Page commands use a separately echoed v1 attach negotiation, exact authority/host/page generations, bounded command IDs and sequences, and bounded create/navigate payloads; legacy attaches still receive only the unchanged ready/revoked event shapes. A second optional reconciliation subprotocol gates bounded reclaim, close, and restore payloads behind exact attach/ready echo, complete inventory, command negotiation, reconnect authority, and command-result authority; the production client advertises it only with the matching command and inventory capabilities. Exact guest or app-renderer loss marks one page generation outcome-unknown, coalesces a bounded negotiated inventory reattach, closes or retires the dead generation, and allocates a fresh generation before URL restore; explicit close is not misclassified as a crash. Mixed-version mutation tests project hidden client pages before activate, close, split, reorder, and move-to-group admission, preserve hidden raw order slots, translate visible insertion indices, and project mutation snapshots. A production server orchestrator consumes each immutable inventory once, reserves target generations without exposing placement, emits only negotiated ledger commands, commits after exact completed proof, preserves unrelated and server placements, aborts an attempt when connection authority enters reconnect grace, and requires fresh inventory after failure or abort. The production client dispatcher additionally proves per-page FIFO execution, exact payload-matched duplicate replay, frozen command/result snapshots, a global retired-generation floor, transactional admission, bounded pages/active commands/queues/per-page and global result cache/concurrency, create dependency failure, cancellation, deduplicated retirement joining, and bounded close without late-result overwrite. The server ledger owns issue order and immutable command/result snapshots, bounds outstanding commands, active pages, and per-page/global replay caches, releases active-page capacity after an exact completed close while retaining bounded result replay, validates the shared wire payload before admission, requires live delivery and exact placement, authenticates results to the negotiated connection and paired lease, rejects gaps and conflicting replay, and fences outstanding outcomes at exact retirement. Negotiated command results reuse the authenticated attach socket through bounded nested JSON requests; exact ID routing, reverse-order replies, unknown and duplicate IDs, timeout teardown, serialization failure, aggregate queue accounting, acknowledgement validation, and the unchanged non-v1 path are deterministic. A stable local listener rejects CONNECT while offline or reconnecting, retains its address across replacement, requires a strictly increasing tunnel generation, ignores late superseded callbacks, propagates tunnel protocol failure to the route owner, and recycles exhausted stream IDs only after generation replacement. Reconnect uses the unchanged native v1 attach payload and capability pair; SSH descriptors alone add an execution-host capability and require a runtime-minted grant bound to the exact browser-host lease. Exact SSH provider epoch and connection generation fence ssh2 forwardOut and one non-interactive standalone system-SSH dynamic forward per route. Unit tests preserve domain-form SOCKS requests, sanitize remote errors, bound stderr, cancel startup, release timed-out and synchronously failed sockets, and release routes once. An ephemeral Docker sshd resolves a container-only domain and returns a unique HTTP marker through both ssh2 and the actual system-OpenSSH dynamic-forward adapter without touching the user's SSH files; authority loss fences the ssh2 route. The execution runtime charges route application bytes to the same per-host/process policy; its existing E2EE owner separately caps native outbound buffers process-wide. Production-registered browser-host and paired-runtime methods lease one exact host, prove attach, command delivery, and result settlement share one exact connection identity, then carry SOCKS and HTTP bytes over a dedicated E2EE socket to the fenced execution-host revision and prove route close destroys the destination socket. The production desktop adapter now composes one exact host per environment pairing revision with the page executor, current renderer selector, route Session/WebContents registries, and one reference-counted route per canonical execution-host key. Negotiated same-client control reconnect retains exact authority, placements, grants, dispatcher dedupe, executor guests, and listener addresses; it fences tunnels immediately, blocks route admission, reattaches command delivery only after ready, and replays unsettled commands without repeating completed mutations. Terminal release, replacement, legacy disconnect, and reconnect-grace expiry make only the exact host generation's client placements non-cancellable retirement-pending while retaining capacity until exact cleanup; reconnect grace preserves them. Environment replacement and app shutdown still serialize transport closure before page cleanup and force-close every remaining route. The production placement preparation starts the exact desktop adapter and advertises host/tunnel capabilities only when the paired Electron client is eligible. Node stream-internal high-water bytes, strict cross-route scheduling, and physical cross-platform evidence remain uncovered. The two Docker remote-only SSH browser routing journeys now run in the dedicated Linux ssh-browser-network-route CI job on full runs and their mapped source/test changes; 2 baseline and 6 repeated cases passed with no skips/retries on 2026-09-06. The removed retry-arithmetic and attach-request inventory suites are not current coverage; retained lease and composition suites exercise admission retry, cleanup, inventory negotiation, and reconnect behavior.", "motivatingLinks": [ "https://linear.app/stably/issue/STA-4150/refactor-remote-browser-to-client-hosted-electron-webviews" ], @@ -6803,7 +6803,7 @@ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/browser-host/browser-host-page-reconciliation-plan.test.ts src/main/runtime/browser-host/browser-host-page-reconciliation-executor.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/browser-host/browser-host-page-reconciliation-orchestration.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-page-placement.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-reconciliation-protocol.test.ts src/main/browser/paired-runtime-browser-host-reconciliation-negotiation.test.ts src/main/runtime/rpc/methods/browser-client-host-reconciliation.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/browser-client-page-command-integration.test.ts src/main/browser/browser-client-page-inventory.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts src/main/runtime/browser-host/browser-host-page-reconciliation-plan.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/browser-client-page-command-integration.test.ts src/main/browser/browser-client-page-inventory.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts src/main/runtime/browser-host/browser-host-page-reconciliation-plan.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-reconciliation-command-order.test.ts src/main/browser/browser-client-page-reconciliation-adapters.test.ts src/main/browser/browser-route-prepared-page-rekey.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-network-tunnel-protocol.test.ts src/main/browser/browser-network-tunnel-session.test.ts src/main/browser/remote-browser-socks-server.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-network-tunnel-client.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/runtime-binary-message-router.test.ts src/main/runtime/rpc/methods/browser-network-tunnel.test.ts src/main/runtime/browser-network-tunnel-paired-runtime.integration.test.ts", @@ -6814,11 +6814,11 @@ "pnpm exec vitest run --config config/vitest.config.ts src/shared/remote-runtime/requests/remote-runtime-subscription-request.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-host-command-dispatcher.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-client-host-command-dispatcher.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", - "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-placement-retirement.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-placement-retirement.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host-registry.test.ts src/main/browser/paired-runtime-browser-client-host-runtime.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/browser-session-startup.test.ts src/main/ipc/runtime-environments/runtime-environments-subscription-teardown.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/browser/browser-network-deferred-socket.test.ts src/main/browser/browser-network-execution-route.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/rpc/methods/browser-network-tunnel.test.ts src/main/browser/ssh-browser-network-execution-route.test.ts src/main/browser/system-ssh-socks-client-socket.test.ts src/main/ssh/system-transport/system-ssh-dynamic-forward-process.test.ts src/shared/browser-client-host-protocol.test.ts src/main/ssh/system-transport/system-ssh-forward-process.test.ts src/main/ssh/system-transport/system-ssh-operations.test.ts src/main/ssh/port-forwarding/ssh-port-forward.test.ts", @@ -6850,14 +6850,12 @@ "src/main/browser/paired-runtime-browser-host-lease.test.ts", "src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts", "src/main/browser/paired-runtime-browser-host-reconciliation-negotiation.test.ts", - "src/main/browser/browser-host-lease-reconnect-delay.test.ts", "src/main/browser/paired-runtime-browser-client-host.test.ts", "src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts", "src/main/browser/browser-client-network-route-registry.test.ts", "src/main/browser/paired-runtime-browser-client-host-composition.test.ts", "src/main/browser/paired-runtime-browser-client-host-registry.test.ts", "src/main/browser/paired-runtime-browser-client-host-runtime.test.ts", - "src/main/browser/browser-client-host-attach-request.test.ts", "src/main/browser/browser-client-host-reconciliation-command-order.test.ts", "src/main/browser/browser-client-page-command-executor.test.ts", "src/main/browser/browser-client-page-command-integration.test.ts", @@ -7029,13 +7027,6 @@ "exact reconnect rejects reconciliation downgrade" ] }, - { - "file": "src/main/browser/browser-host-lease-reconnect-delay.test.ts", - "assertions": [ - "client-specific retry jitter is deterministic and remains inside exponential, two-second, and grace ceilings", - "invalid timer delays fail before starting a subscription" - ] - }, { "file": "src/main/browser/paired-runtime-browser-client-host.test.ts", "assertions": [ @@ -7074,13 +7065,6 @@ "cleanly absent failed creates are forgotten while unresolved cleanup remains fenced" ] }, - { - "file": "src/main/browser/browser-client-host-attach-request.test.ts", - "assertions": [ - "an unencodable optional inventory is omitted without changing legacy page-command negotiation", - "reconnect negotiation is emitted only beside an encoded complete inventory" - ] - }, { "file": "src/main/browser/browser-client-host-reconciliation-command-order.test.ts", "assertions": [ @@ -7424,6 +7408,7 @@ "runner": "local", "platform": "macos", "command": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-placement-retirement.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-placement-retirement.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", "result": "passed", "durationSeconds": 3.61, "summary": "On the #14753 tip, sixteen files passed 167 exact terminal navigation/route fencing, retained reconnect grace, placement retirement, attach, command, result, route recovery, admission, and mixed-version protocol assertions. The new composition oracle was 1 failed / 10 passed before implementation because terminal host loss started asynchronous close without first suspending routes or revoking navigation." @@ -7451,6 +7436,7 @@ "runner": "local", "platform": "macos", "command": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/runtime-rpc-browser-host-admission.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", "result": "passed", "durationSeconds": 7.18, "summary": "On origin/main@5b7f44278a, fifteen files passed 159 mixed-version attach, exact authority, bounded admission and recovery, reconnect, route suspension, page-command replay, result-ledger, and authenticated lease tests." @@ -7460,6 +7446,7 @@ "runner": "local", "platform": "macos", "command": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/browser-host-lease-reconnect-delay.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-network-route-registry.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-client-host-reconnect.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/browser/paired-runtime-browser-host-lease-reconnect.test.ts src/main/browser/paired-runtime-browser-network-route.test.ts src/main/runtime/browser-host/browser-host-command-ledger.test.ts src/main/runtime/browser-host/browser-host-command-ledger-capacity.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts", "result": "passed", "durationSeconds": 2.23, "summary": "Fourteen files passed 150 negotiated same-client reconnect, legacy skew, active takeover, exact authority, immediate route suspension, independently bounded route retry, stable fail-closed SOCKS, ready-before-replay, completed-mutation dedupe, partial-replay delivery recovery, repeated-loss, stale-callback, explicit-revocation, grace cleanup, and deterministic jittered-backoff tests." @@ -7469,6 +7456,7 @@ "runner": "local", "platform": "macos", "command": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-host-attach-request.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/browser-client-page-command-integration.test.ts src/main/browser/browser-client-page-inventory.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts src/main/runtime/browser-host/browser-host-page-reconciliation-plan.test.ts", + "currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/shared/browser-client-host-protocol.test.ts src/main/browser/browser-client-page-command-executor.test.ts src/main/browser/browser-client-page-command-integration.test.ts src/main/browser/browser-client-page-inventory.test.ts src/main/browser/paired-runtime-browser-client-host-composition.test.ts src/main/browser/paired-runtime-browser-client-host.test.ts src/main/browser/paired-runtime-browser-host-lease.test.ts src/main/runtime/browser-host/browser-host-lease-registry.test.ts src/main/runtime/rpc/methods/browser-client-host.test.ts src/main/runtime/browser-host/browser-host-page-reconciliation-plan.test.ts", "result": "passed", "durationSeconds": 7.3, "summary": "Eleven files passed 145 exact inventory negotiation, 256-page and 768 KiB atomic bounds, worst-case JSON-escaped inventory identities, deterministic optional-URL compaction, legacy-command preservation when inventory is unencodable, legacy strip, immutable authenticated lease snapshot, strict v1/duplicate/foreign-client rejection, paired-device-bound previous-runtime restart admission, missing and unsolicited echo, create-to-active deduplication, renderer/guest-stale outcome-unknown classification, normalized URL bounds, composition sampling, RPC retention, and reconciliation tests." diff --git a/mobile/src/session/native-chat/use-mobile-native-chat-input-lease.test.ts b/mobile/src/session/native-chat/use-mobile-native-chat-input-lease.test.ts index 70fc71c77e9..8b99f5736b4 100644 --- a/mobile/src/session/native-chat/use-mobile-native-chat-input-lease.test.ts +++ b/mobile/src/session/native-chat/use-mobile-native-chat-input-lease.test.ts @@ -2,7 +2,6 @@ import { createElement } from 'react' import { act, create, type ReactTestRenderer } from 'react-test-renderer' import { afterEach, describe, expect, it, vi } from 'vitest' import { - mobileNativeChatComposerPlaceholder, useMobileNativeChatInputLease, useSettledMobileNativeChatInputLock } from './use-mobile-native-chat-input-lease' @@ -110,25 +109,3 @@ describe('useSettledMobileNativeChatInputLock', () => { expect(settled).toBeNull() }) }) - -describe("the phone chat composer's placeholder", () => { - it('says a message runs after the stop while the chat reads Stopping', () => { - expect(mobileNativeChatComposerPlaceholder(null, 'queue')).toBe( - 'Queue a message to run after the stop' - ) - expect(mobileNativeChatComposerPlaceholder(null, 'send')).toBe( - 'Send a message to run after the stop' - ) - }) - - it('reads as usual otherwise', () => { - expect(mobileNativeChatComposerPlaceholder(null, undefined)).toBe( - 'Ask anything, @ to mention files, / for commands' - ) - }) - - it('says why the composer is locked first', () => { - expect(mobileNativeChatComposerPlaceholder('disconnected', 'queue')).toBe('Reconnecting…') - expect(mobileNativeChatComposerPlaceholder('waiting', 'queue')).toBe('Waiting for terminal…') - }) -}) diff --git a/src/main/browser/agent-browser-process-environment.test.ts b/src/main/browser/agent-browser-process-environment.test.ts deleted file mode 100644 index 0dc1605f22b..00000000000 --- a/src/main/browser/agent-browser-process-environment.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - AGENT_BROWSER_IDLE_TIMEOUT_MS, - createAgentBrowserProcessEnvironment -} from './agent-browser-process-environment' - -vi.mock('node:fs', () => ({ mkdirSync: vi.fn(), chmodSync: vi.fn() })) - -describe('agent-browser process environment', () => { - it('bounds Unix socket paths independently of a long profile path', () => { - const { env, ownsSocketDirectory } = createAgentBrowserProcessEnvironment({ - inheritedEnv: { PATH: '/bin' }, - platform: 'darwin', - userDataPath: `/private/var/folders/${'long-profile-segment/'.repeat(12)}` - }) - const socketDirectory = env.AGENT_BROWSER_SOCKET_DIR - - expect(ownsSocketDirectory).toBe(true) - expect(socketDirectory).toMatch(/^\/tmp\/orca-ab-[0-9a-f]{16}$/) - expect( - `${socketDirectory}/orca-tab-00000000-0000-4000-8000-000000000000.sock`.length - ).toBeLessThan(104) - }) - - it('preserves explicit overrides and leaves Windows socket routing unchanged', () => { - const configured = createAgentBrowserProcessEnvironment({ - inheritedEnv: { AGENT_BROWSER_SOCKET_DIR: '/custom/socket-dir' }, - platform: 'linux', - userDataPath: '/profile' - }) - expect(configured.env.AGENT_BROWSER_SOCKET_DIR).toBe('/custom/socket-dir') - expect(configured.ownsSocketDirectory).toBe(false) - - const windows = createAgentBrowserProcessEnvironment({ - inheritedEnv: { PATH: 'C:\\Windows' }, - platform: 'win32', - userDataPath: 'C:\\Users\\Orca' - }) - expect(windows.env.AGENT_BROWSER_SOCKET_DIR).toBeUndefined() - expect(windows.env.PATH).toBe('C:\\Windows') - expect(windows.ownsSocketDirectory).toBe(false) - }) - - it.each(['darwin', 'linux', 'win32'])( - 'bounds daemon idle lifetime on %s', - (platform) => { - const { env } = createAgentBrowserProcessEnvironment({ - inheritedEnv: { PATH: '/bin' }, - platform, - userDataPath: '/profile' - }) - expect(env.AGENT_BROWSER_IDLE_TIMEOUT_MS).toBe(String(AGENT_BROWSER_IDLE_TIMEOUT_MS)) - } - ) - - it('never cuts a command short: idle timeout exceeds the bridge exec timeout', () => { - expect(AGENT_BROWSER_IDLE_TIMEOUT_MS).toBeGreaterThan(90_000) - }) - - it('honors an explicit idle timeout from the environment', () => { - const { env } = createAgentBrowserProcessEnvironment({ - inheritedEnv: { AGENT_BROWSER_IDLE_TIMEOUT_MS: '5000' }, - platform: 'darwin', - userDataPath: '/profile' - }) - expect(env.AGENT_BROWSER_IDLE_TIMEOUT_MS).toBe('5000') - }) - - it('still bounds the daemon when the socket directory cannot be created', async () => { - const fs = await import('node:fs') - vi.mocked(fs.mkdirSync).mockImplementationOnce(() => { - throw new Error('EACCES') - }) - const { env, ownsSocketDirectory } = createAgentBrowserProcessEnvironment({ - inheritedEnv: { PATH: '/bin' }, - platform: 'linux', - userDataPath: '/profile' - }) - expect(env.AGENT_BROWSER_SOCKET_DIR).toBeUndefined() - expect(ownsSocketDirectory).toBe(false) - expect(env.AGENT_BROWSER_IDLE_TIMEOUT_MS).toBe(String(AGENT_BROWSER_IDLE_TIMEOUT_MS)) - }) -}) diff --git a/src/main/browser/agent-browser-session-reset.test.ts b/src/main/browser/agent-browser-session-reset.test.ts deleted file mode 100644 index 4b16906d73f..00000000000 --- a/src/main/browser/agent-browser-session-reset.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { beforeEach, expect, it, vi } from 'vitest' -import { join } from 'node:path' - -const { lstatSync } = vi.hoisted(() => ({ lstatSync: vi.fn() })) -vi.mock('node:fs', () => ({ lstatSync })) -import { canSkipAgentBrowserSessionReset } from './agent-browser-session-reset' - -const owned = { - ownsSocketDirectory: true, - socketDirectory: '/tmp/orca-ab-profile', - sessionName: 'orca-tab-page' -} -const socketPath = join(owned.socketDirectory, 'orca-tab-page.sock') - -beforeEach(() => { - lstatSync.mockReset() -}) - -it('skips an absent owned socket', () => { - lstatSync.mockImplementation(() => { - throw Object.assign(new Error('No socket'), { code: 'ENOENT' }) - }) - expect(canSkipAgentBrowserSessionReset(owned)).toBe(true) - expect(lstatSync).toHaveBeenCalledWith(socketPath) -}) - -it('requires reset when a socket or symlink exists', () => { - lstatSync.mockReturnValue({}) - expect(canSkipAgentBrowserSessionReset(owned)).toBe(false) - expect(lstatSync).toHaveBeenCalledWith(socketPath) -}) - -it.each(['EACCES', 'EIO', 'ENOTDIR'])('requires reset for %s', (code) => { - lstatSync.mockImplementation(() => { - throw Object.assign(new Error('Socket inspection failed'), { code }) - }) - expect(canSkipAgentBrowserSessionReset(owned)).toBe(false) - expect(lstatSync).toHaveBeenCalledWith(socketPath) -}) - -it.each([ - { ownsSocketDirectory: false }, - { socketDirectory: undefined }, - { sessionName: '../other' }, - { sessionName: 'has space' }, - { sessionName: '' } -])('requires reset without an owned Unix socket address: %j', (override) => { - expect(canSkipAgentBrowserSessionReset({ ...owned, ...override })).toBe(false) - expect(lstatSync).not.toHaveBeenCalled() -}) diff --git a/src/main/browser/agent-input-focus-guard.test.ts b/src/main/browser/agent-input-focus-guard.test.ts index 1dece0273b8..c67ab7550fe 100644 --- a/src/main/browser/agent-input-focus-guard.test.ts +++ b/src/main/browser/agent-input-focus-guard.test.ts @@ -21,8 +21,7 @@ vi.mock('electron', () => ({ import { AGENT_INPUT_FOCUS_GUARD_CHANNEL, AGENT_INPUT_FOCUS_GUARD_READY_CHANNEL, - guardPaneFocusForAgentInput, - isFocusTakingCdpInput + guardPaneFocusForAgentInput } from './agent-input-focus-guard' function fakeRenderer(id: number, answer: boolean) { @@ -113,14 +112,3 @@ describe('guardPaneFocusForAgentInput', () => { expect(ipcListeners.get(AGENT_INPUT_FOCUS_GUARD_READY_CHANNEL)).toEqual([]) }) }) - -describe('isFocusTakingCdpInput', () => { - it('matches presses only', () => { - expect(isFocusTakingCdpInput('Input.dispatchMouseEvent', { type: 'mousePressed' })).toBe(true) - expect(isFocusTakingCdpInput('Input.dispatchMouseEvent', { type: 'mouseReleased' })).toBe(false) - expect(isFocusTakingCdpInput('Input.dispatchMouseEvent', { type: 'mouseMoved' })).toBe(false) - expect(isFocusTakingCdpInput('Input.dispatchTouchEvent', { type: 'touchStart' })).toBe(true) - expect(isFocusTakingCdpInput('Input.synthesizeTapGesture', {})).toBe(true) - expect(isFocusTakingCdpInput('Input.dispatchKeyEvent', { type: 'keyDown' })).toBe(false) - }) -}) diff --git a/src/main/browser/browser-client-download-relay.test.ts b/src/main/browser/browser-client-download-relay.test.ts index 0b2de836704..da011d4b345 100644 --- a/src/main/browser/browser-client-download-relay.test.ts +++ b/src/main/browser/browser-client-download-relay.test.ts @@ -36,18 +36,6 @@ function negotiatedTransport( return transport } -function boundTransport( - availability: 'unsupported' | 'unavailable' -): BrowserClientFileChannelTransport { - const transport = new BrowserClientFileChannelTransport() - transport.bind({ - fileChannelNegotiated: false, - fileChannelAvailability: availability, - sendFileChannelRequest: async () => ({ ok: true, result: {}, _meta: {} }) as never - }) - return transport -} - function remoteRoute(outcome: ReturnType) { expect(outcome.kind).toBe('remote') return (outcome as { kind: 'remote'; route: BrowserClientDownloadRoute }).route @@ -117,23 +105,6 @@ describe('BrowserClientDownloadRelay', () => { expect(removed).toContain(path.dirname(route.stagingPath)) }) - it('removes the per-transfer staging directory when the transfer is aborted', async () => { - const { filesystem, removed } = memoryFilesystem(Buffer.from('abc')) - const relay = new BrowserClientDownloadRelay({ - stagingRoot: '/tmp/staging', - hostLabel: 'build-box', - transport: negotiatedTransport(() => ({ released: true })), - resolvePage: () => page, - filesystem - }) - - const route = remoteRoute(relay.route({ guestWebContentsId: 7 })) - await route.abort() - - expect(removed).toEqual([path.dirname(route.stagingPath)]) - expect(path.dirname(route.stagingPath)).not.toBe('/tmp/staging') - }) - it('leaves nothing on disk under the real staging root once transfers settle', async () => { const root = await realpath(await mkdtemp(path.join(tmpdir(), 'orca-download-relay-'))) const stagingRoot = path.join(root, 'downloads') @@ -165,42 +136,6 @@ describe('BrowserClientDownloadRelay', () => { } }) - it('keeps the local fallback when the host never offered the file channel', () => { - const relay = new BrowserClientDownloadRelay({ - stagingRoot: '/tmp/staging', - hostLabel: 'build-box', - transport: boundTransport('unsupported'), - resolvePage: () => page, - filesystem: memoryFilesystem(Buffer.alloc(0)).filesystem - }) - - expect(relay.route({ guestWebContentsId: 7 })).toEqual({ kind: 'local-fallback' }) - }) - - it('reports an owned page whose channel is momentarily gone as unavailable', () => { - const relay = new BrowserClientDownloadRelay({ - stagingRoot: '/tmp/staging', - hostLabel: 'build-box', - transport: boundTransport('unavailable'), - resolvePage: () => page, - filesystem: memoryFilesystem(Buffer.alloc(0)).filesystem - }) - - expect(relay.route({ guestWebContentsId: 7 })).toEqual({ kind: 'unavailable' }) - }) - - it('claims no ownership of a WebContents that is not one of its pages', () => { - const relay = new BrowserClientDownloadRelay({ - stagingRoot: '/tmp/staging', - hostLabel: 'build-box', - transport: negotiatedTransport(() => ({ accepted: true })), - resolvePage: () => undefined, - filesystem: memoryFilesystem(Buffer.alloc(0)).filesystem - }) - - expect(relay.route({ guestWebContentsId: 7 })).toEqual({ kind: 'unowned' }) - }) - it('stops the commit and never sends the final chunk once the transfer is aborted', async () => { const writes: { final: boolean }[] = [] const aborts: unknown[] = [] diff --git a/src/main/browser/browser-client-file-channel-negotiation.test.ts b/src/main/browser/browser-client-file-channel-negotiation.test.ts index 9ef1a49891a..8da654744a1 100644 --- a/src/main/browser/browser-client-file-channel-negotiation.test.ts +++ b/src/main/browser/browser-client-file-channel-negotiation.test.ts @@ -1,45 +1,9 @@ -import { pairedRuntimeConnectionFromOffer } from '../../shared/paired-runtime-connection' import { describe, expect, it } from 'vitest' import { BrowserClientHostAttachParams } from '../../shared/browser-client-host-protocol' -import { createBrowserClientHostAttachRequest } from './browser-client-host-attach-request' -import { sameBrowserClientHostLeaseAuthority } from './browser-client-host-command-authority' import { BrowserClientFileChannelTransport } from './browser-client-file-channel-transport' -import { PairedRuntimeBrowserClientHost } from './paired-runtime-browser-client-host' - -const TEST_CONNECTION = pairedRuntimeConnectionFromOffer({ - endpoint: 'ws://127.0.0.1:1', - deviceToken: 'token', - publicKeyB64: 'key' -}) - -const leaseOptions = { - connection: TEST_CONNECTION, - authorityRuntimeId: 'runtime-1', - browserHostClientId: 'host-1', - hostCapabilities: ['webview'], - pageCommandProtocolVersion: 1 as const, - onPageCommand: () => ({ status: 'completed' }) as never -} describe('browser file channel negotiation', () => { - it('requests the file channel only alongside the command protocol', () => { - expect( - createBrowserClientHostAttachRequest({ - ...leaseOptions, - fileChannelProtocolVersion: 1 - }).params.fileChannelProtocolVersion - ).toBe(1) - - expect( - createBrowserClientHostAttachRequest({ - ...leaseOptions, - onPageCommand: undefined, - fileChannelProtocolVersion: 1 - }).params.fileChannelProtocolVersion - ).toBeUndefined() - }) - it('rejects an attach that asks for the file channel without command negotiation', () => { expect( BrowserClientHostAttachParams.safeParse({ @@ -51,37 +15,6 @@ describe('browser file channel negotiation', () => { ).toBe(false) }) - it('keeps the same authority when a reconnect renegotiates the file channel', () => { - const negotiated = { - authorityRuntimeId: 'runtime-1', - authorityEpoch: 'epoch-1', - browserHostClientId: 'host-1', - browserHostGeneration: 1, - pageCommandProtocolVersion: 1 as const, - fileChannelProtocolVersion: 1 as const - } - - expect(sameBrowserClientHostLeaseAuthority(negotiated, negotiated)).toBe(true) - expect( - sameBrowserClientHostLeaseAuthority(negotiated, { - ...negotiated, - fileChannelProtocolVersion: undefined - }) - ).toBe(true) - expect( - sameBrowserClientHostLeaseAuthority(negotiated, { - ...negotiated, - browserHostGeneration: 2 - }) - ).toBe(false) - expect( - sameBrowserClientHostLeaseAuthority(negotiated, { - ...negotiated, - pageCommandProtocolVersion: undefined - }) - ).toBe(false) - }) - it('reports the channel unavailable until a negotiated lease binds it', async () => { const transport = new BrowserClientFileChannelTransport() expect(transport.available).toBe(false) @@ -116,38 +49,4 @@ describe('browser file channel negotiation', () => { expect(transport.available).toBe(false) expect(transport.availability).toBe('unavailable') }) - - it('separates a host that never offered the channel from one whose lease is gone', () => { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the test reads the host's private channel state; a renamed field fails the test. - const host = new PairedRuntimeBrowserClientHost({ - connection: TEST_CONNECTION, - authorityRuntimeId: 'runtime-1', - browserHostClientId: 'host-1', - hostCapabilities: ['webview'], - fileChannelProtocolVersion: 1, - handler: () => Promise.reject(new Error('unused')) - }) as unknown as { - closed: boolean - authority: { fileChannelProtocolVersion?: 1 } | null - lease: { - authority: { fileChannelProtocolVersion?: 1 } | null - fileChannelActive: boolean - } - fileChannelAvailability: string - } - - expect(host.fileChannelAvailability).toBe('unavailable') - - host.authority = { fileChannelProtocolVersion: undefined } - expect(host.fileChannelAvailability).toBe('unsupported') - - host.authority = { fileChannelProtocolVersion: 1 } - host.lease.authority = { fileChannelProtocolVersion: 1 } - // Match acceptAuthority’s lease update. - host.lease.fileChannelActive = true - expect(host.fileChannelAvailability).toBe('negotiated') - - host.closed = true - expect(host.fileChannelAvailability).toBe('unavailable') - }) }) diff --git a/src/main/browser/browser-client-host-attach-request.test.ts b/src/main/browser/browser-client-host-attach-request.test.ts deleted file mode 100644 index c9733f1073b..00000000000 --- a/src/main/browser/browser-client-host-attach-request.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { pairedRuntimeConnectionFromOffer } from '../../shared/paired-runtime-connection' -import { describe, expect, it, vi } from 'vitest' -import type { PairingOffer } from '../../shared/pairing' -import { createBrowserClientHostAttachRequest } from './browser-client-host-attach-request' - -const pairing = { - v: 2, - endpoint: 'ws://127.0.0.1:6768', - deviceToken: 'device-token', - publicKeyB64: 'public-key', - pairedDeviceId: 'device-a', - scope: 'runtime' -} as PairingOffer - -describe('browser client host attach request', () => { - it('omits unencodable inventory without changing legacy page-command negotiation', () => { - const onPageCommand = vi.fn(() => ({ status: 'completed' as const })) - const attach = createBrowserClientHostAttachRequest({ - connection: pairedRuntimeConnectionFromOffer(pairing), - authorityRuntimeId: 'runtime-a', - browserHostClientId: 'host-a', - hostCapabilities: ['webview'], - pageCommandProtocolVersion: 1, - onPageCommand, - pageInventoryProtocolVersion: 1, - leaseReconnectProtocolVersion: 1, - getPageInventory: () => [ - { - authorityRuntimeId: 'runtime-old', - authorityEpoch: 'epoch-old', - browserHostClientId: 'host-a', - browserHostGeneration: 2, - browserPageId: 'page-a', - pageHostGeneration: 3, - browserProfileId: '\0'.repeat(256), - executionHostKey: 'native:runtime-old:1', - state: 'active' - } - ] - }) - - expect(attach.pageCommandProtocolVersion).toBe(1) - expect(attach.pageInventoryProtocolVersion).toBeUndefined() - expect(attach.params).toMatchObject({ pageCommandProtocolVersion: 1 }) - expect(attach.params).not.toHaveProperty('pageInventoryProtocolVersion') - expect(attach.params).not.toHaveProperty('pageInventory') - expect(attach.params).not.toHaveProperty('leaseReconnectProtocolVersion') - }) - - it('includes reconnect negotiation only beside an encoded inventory snapshot', () => { - const attach = createBrowserClientHostAttachRequest({ - connection: pairedRuntimeConnectionFromOffer(pairing), - authorityRuntimeId: 'runtime-a', - browserHostClientId: 'host-a', - hostCapabilities: ['webview'], - pageInventoryProtocolVersion: 1, - leaseReconnectProtocolVersion: 1, - getPageInventory: () => [] - }) - - expect(attach).toMatchObject({ - pageInventoryProtocolVersion: 1, - leaseReconnectProtocolVersion: 1, - params: { - pageInventoryProtocolVersion: 1, - pageInventory: [], - leaseReconnectProtocolVersion: 1 - } - }) - }) -}) diff --git a/src/main/browser/browser-client-host-authority-replacement.test.ts b/src/main/browser/browser-client-host-authority-replacement.test.ts index cee55a42b73..1dfbba0e9a3 100644 --- a/src/main/browser/browser-client-host-authority-replacement.test.ts +++ b/src/main/browser/browser-client-host-authority-replacement.test.ts @@ -1,73 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE } from '../../shared/browser-client-host-protocol' -import { isBrowserClientHostAuthorityReplaced } from './browser-client-host-authority-replacement' import { BrowserClientHostAuthorityReplacementWait } from './browser-client-host-authority-replacement-wait' -function errorWithCode(message: string, code: unknown): Error { - return Object.assign(new Error(message), { code }) -} - -describe('browser client host authority replacement', () => { - it('recognizes the structured mismatch code a current runtime sends', () => { - const rejected = errorWithCode( - 'lease attach named a retired runtime', - BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE - ) - - expect(isBrowserClientHostAuthorityReplaced(rejected)).toBe(true) - }) - - it('recognizes the mismatch when a new client talks to a runtime older than the typed code', () => { - const rejected = new Error(BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE) - - expect('code' in rejected).toBe(false) - expect(isBrowserClientHostAuthorityReplaced(rejected)).toBe(true) - }) - - it('rejects a host error that carries an unrelated code and an unrelated message', () => { - const unrelated = errorWithCode('browser host process exited', 'runtime_error') - - expect(isBrowserClientHostAuthorityReplaced(unrelated)).toBe(false) - }) - - it('rejects non-Error rejections, including a bare object carrying the mismatch code', () => { - expect(isBrowserClientHostAuthorityReplaced(undefined)).toBe(false) - expect(isBrowserClientHostAuthorityReplaced(null)).toBe(false) - expect(isBrowserClientHostAuthorityReplaced(BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE)).toBe( - false - ) - expect( - isBrowserClientHostAuthorityReplaced({ code: BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE }) - ).toBe(false) - }) - - it('rejects a non-string code that a loose comparison could coerce', () => { - expect(isBrowserClientHostAuthorityReplaced(errorWithCode('host rejected attach', 503))).toBe( - false - ) - expect( - isBrowserClientHostAuthorityReplaced( - errorWithCode('host rejected attach', { - toString: () => BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE - }) - ) - ).toBe(false) - }) - - it('rejects a message that merely contains the mismatch code inside a longer sentence', () => { - const quoted = new Error(`failed: ${BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE} occurred`) - - expect(isBrowserClientHostAuthorityReplaced(quoted)).toBe(false) - }) - - it('pins the mismatch code string', () => { - // Older clients compare their fallback error message to this exact wire value. - expect(BROWSER_CLIENT_HOST_AUTHORITY_MISMATCH_CODE).toBe( - 'browser_client_host_authority_mismatch' - ) - }) -}) - describe('BrowserClientHostAuthorityReplacementWait', () => { beforeEach(() => { vi.useFakeTimers() diff --git a/src/main/browser/browser-client-host-published-url-wiring.test.ts b/src/main/browser/browser-client-host-published-url-wiring.test.ts deleted file mode 100644 index 8ded2cfbb2d..00000000000 --- a/src/main/browser/browser-client-host-published-url-wiring.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { tmpdir } from 'node:os' -import { afterEach, describe, expect, it, vi } from 'vitest' -import type { KnownRuntimeEnvironment } from '../../shared/runtime-environments' - -vi.mock('electron', () => ({ - app: { getPath: () => tmpdir() }, - ipcMain: { on: vi.fn(), removeListener: vi.fn() } -})) - -type CompositionOptions = { - createExecutor: ( - input: unknown, - options: { - retainNetworkRoute: () => Promise - onPageUnavailable: (browserPageId: string, pageHostGeneration: number) => void - } - ) => unknown -} - -const compositionOptions: CompositionOptions[] = [] -const recordedUrlParams: unknown[] = [] - -vi.mock('./paired-runtime-browser-client-host', () => ({ - PairedRuntimeBrowserClientHost: class {} -})) - -vi.mock('./browser-client-file-channel-transport', () => ({ - BrowserClientFileChannelTransport: class { - bind(): void {} - } -})) - -vi.mock('./browser-client-page-command-executor', () => ({ - BrowserClientPageCommandExecutor: class { - readonly recordPublishedPageUrl = (params: unknown): void => { - recordedUrlParams.push(params) - } - - findPageByWebContentsId(): undefined { - return undefined - } - } -})) - -vi.mock('./paired-runtime-browser-client-host-composition', () => ({ - PairedRuntimeBrowserClientHostComposition: class { - constructor(options: CompositionOptions) { - compositionOptions.push(options) - } - - start(): Promise { - return Promise.resolve({ authority: 'lease-a' }) - } - - close(): Promise { - return Promise.resolve(true) - } - - whenClosed(): Promise { - return Promise.resolve() - } - } -})) - -const ENVIRONMENT_ID = 'environment-a' - -const metadata = { - browserHostClientId: 'host-a', - browserHostGeneration: 3, - browserPageId: 'page-a', - pageHostGeneration: 7, - revision: 2, - url: 'https://example.internal/where-the-user-went', - title: 'Where the user went', - loading: false, - canGoBack: true, - canGoForward: false -} - -function pairedEnvironment(): KnownRuntimeEnvironment { - return { - id: ENVIRONMENT_ID, - name: 'Environment A', - createdAt: 1, - updatedAt: 1, - lastUsedAt: null, - runtimeId: null, - preferredEndpointId: 'endpoint-a', - endpoints: [ - { - id: 'endpoint-a', - endpoint: 'ws://127.0.0.1:9999', - deviceToken: 'token-a', - publicKeyB64: 'key-a' - } - ] - } as KnownRuntimeEnvironment -} - -async function startHost() { - vi.resetModules() - compositionOptions.length = 0 - recordedUrlParams.length = 0 - const runtime = await import('./paired-runtime-browser-client-host-runtime') - runtime.configurePairedRuntimeBrowserClientHostsForOrcaProfile({ orcaProfileId: 'profile-a' }) - await runtime.startPairedRuntimeBrowserClientHost({ - environment: pairedEnvironment(), - authorityRuntimeId: 'runtime-a' - }) - const options = compositionOptions[0] - if (!options) { - throw new Error('client host composition was never constructed') - } - const { publishBrowserClientPageMetadata } = - await import('./browser-client-page-metadata-transport') - return { options, publishBrowserClientPageMetadata } -} - -afterEach(() => { - vi.resetModules() -}) - -describe('published-url observation wiring', () => { - // Adoption restores inventory URLs, so user navigations must reach this executor. - it('routes a page-metadata publish into the executor that owns the page', async () => { - const { options, publishBrowserClientPageMetadata } = await startHost() - options.createExecutor( - { orcaProfileId: 'profile-a' }, - { retainNetworkRoute: () => Promise.resolve({}), onPageUnavailable: () => {} } - ) - - await publishBrowserClientPageMetadata(ENVIRONMENT_ID, metadata).catch(() => undefined) - - expect(recordedUrlParams).toEqual([metadata]) - }) -}) diff --git a/src/main/browser/browser-client-page-command-executor.test.ts b/src/main/browser/browser-client-page-command-executor.test.ts index dee4b568ee4..a6e57345434 100644 --- a/src/main/browser/browser-client-page-command-executor.test.ts +++ b/src/main/browser/browser-client-page-command-executor.test.ts @@ -9,137 +9,6 @@ import { describe('BrowserClientPageCommandExecutor', () => { beforeEach(() => vi.restoreAllMocks()) - it('prepares the exact route and blank guest before granting navigation', async () => { - const { dependencies, executor, order, renderer } = createHarness() - const command = createCommand('createPage') - - await expect(executor.handle(command, new AbortController().signal)).resolves.toEqual({ - status: 'completed' - }) - - expect(order).toEqual([ - 'retain-route', - 'prepare-page', - 'mount-page', - 'claim-guest', - 'register-guest', - 'grant-navigation', - 'bind-guest' - ]) - expect(dependencies.guestBinding.bind).toHaveBeenCalledWith({ - registration: { - partition, - browserPageId: 'page-a', - pageHostGeneration: 7, - rendererWebContentsId: 11, - webContentsId: 41 - }, - browserProfileId: 'profile-a' - }) - expect(dependencies.retainNetworkRoute).toHaveBeenCalledWith( - 'execution-host-a', - expect.any(AbortSignal) - ) - expect(dependencies.routeSessions.preparePage).toHaveBeenCalledWith({ - identity: { - orcaProfileId: 'orca-profile-a', - browserProfileId: 'profile-a', - authorityConnectionIdentity: 'authority-record-a', - executionHostIdentity: 'execution-host-record-a' - }, - compatibleIdentity: { - orcaProfileId: 'orca-profile-a', - browserProfileId: 'profile-a', - authorityConnectionIdentity: 'pairing-authority-record-a', - executionHostIdentity: 'execution-host-record-a' - }, - legacyIdentity: { - orcaProfileId: 'orca-profile-a', - browserProfileId: 'profile-a', - authorityConnectionIdentity: 'legacy-authority-record-a', - executionHostIdentity: 'legacy-execution-host-record-a' - }, - storageScope: 'a'.repeat(64), - browserPageId: 'page-a', - pageHostGeneration: 7, - rendererWebContentsId: 11, - proxyEndpoint: { host: '127.0.0.1', port: 43123 } - }) - expect(renderer.mountPage).toHaveBeenCalledWith( - { - partition, - browserPageId: 'page-a', - pageHostGeneration: 7 - }, - expect.any(AbortSignal) - ) - expect(dependencies.routeWebContents.registerGuest).toHaveBeenCalledWith({ - partition, - browserPageId: 'page-a', - pageHostGeneration: 7, - rendererWebContentsId: 11, - webContentsId: 41 - }) - }) - - it('loads a normalized URL only through the retained exact guest', async () => { - const { dependencies, executor } = createHarness() - await executor.handle(createCommand('createPage'), new AbortController().signal) - const lifecycleClaim = dependencies.routeWebContents.claimGuestLifecycle.mock.results[0]?.value - - await expect( - executor.handle(createCommand('navigate'), new AbortController().signal) - ).resolves.toEqual({ status: 'completed' }) - - expect(dependencies.routeWebContents.navigateGuest).toHaveBeenCalledWith( - lifecycleClaim, - 'https://example.internal/path' - ) - expect(executor.snapshotPageInventory()).toEqual([ - { - authorityRuntimeId: 'runtime-a', - authorityEpoch: 'epoch-a', - browserHostClientId: 'client-a', - browserHostGeneration: 3, - browserPageId: 'page-a', - pageHostGeneration: 7, - browserProfileId: 'profile-a', - executionHostKey: 'execution-host-a', - state: 'active', - currentUrl: 'https://example.internal/path' - } - ]) - }) - - it('executes one automation envelope against the exact retained page', async () => { - const { dependencies, executor } = createHarness() - await executor.handle(createCommand('createPage'), new AbortController().signal) - const command = createCommand('navigate', { - commandId: 'automation-a', - command: { - type: 'automation', - method: 'browser.click', - params: { element: 'Submit' } - } - }) - - await expect(executor.handle(command, new AbortController().signal)).resolves.toEqual({ - status: 'completed', - value: { clicked: true } - }) - expect(dependencies.executeAutomation).toHaveBeenCalledWith( - { - browserPageId: 'page-a', - pageHostGeneration: 7, - browserProfileId: 'profile-a', - method: 'browser.click', - params: { element: 'Submit' }, - registration: expect.objectContaining({ browserPageId: 'page-a', webContentsId: 41 }) - }, - expect.any(AbortSignal) - ) - }) - it('keeps legacy-valid command identities when inventory cannot encode them', async () => { const { executor } = createHarness() const browserProfileId = '\0'.repeat(256) @@ -605,8 +474,4 @@ describe('BrowserClientPageCommandExecutor', () => { resolveRoute(route) await expect(first).resolves.toEqual({ status: 'completed' }) }) - - it('rejects a page limit above the attach inventory maximum', () => { - expect(() => createHarness({ maxPages: 257 })).toThrow('browser_client_page_limit_invalid') - }) }) diff --git a/src/main/browser/browser-client-page-metadata-transport.test.ts b/src/main/browser/browser-client-page-metadata-transport.test.ts index f3b24922c6c..de71406f6b1 100644 --- a/src/main/browser/browser-client-page-metadata-transport.test.ts +++ b/src/main/browser/browser-client-page-metadata-transport.test.ts @@ -1,9 +1,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { RemoteRuntimeClientError } from '../../shared/remote-runtime-client-error' import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope' -import { BROWSER_CLIENT_FILE_CHANNEL_REQUEST_TIMEOUT_MS } from './browser-client-file-channel-transport' import { - BROWSER_CLIENT_PAGE_METADATA_REQUEST_TIMEOUT_MS, BrowserClientPageMetadataTransport, publishBrowserClientPageMetadata, registerBrowserClientPageMetadataTransport, @@ -36,31 +34,6 @@ afterEach(() => { }) describe('browser client page metadata transport', () => { - it('waits at least as long as anything else sharing the lease connection', () => { - expect(BROWSER_CLIENT_PAGE_METADATA_REQUEST_TIMEOUT_MS).toBeGreaterThanOrEqual( - BROWSER_CLIENT_FILE_CHANNEL_REQUEST_TIMEOUT_MS - ) - expect(BROWSER_CLIENT_PAGE_METADATA_REQUEST_TIMEOUT_MS).toBeGreaterThanOrEqual(30_000) - }) - - it('sends the publish over the bound lease and reports the acknowledgement', async () => { - const sendPageMetadataRequest = vi.fn().mockResolvedValue(answered({ accepted: true })) - const transport = new BrowserClientPageMetadataTransport() - transport.bind({ sendPageMetadataRequest }) - - await expect(transport.publish(PARAMS)).resolves.toEqual({ accepted: true }) - expect(sendPageMetadataRequest).toHaveBeenCalledWith(PARAMS, expect.any(Number)) - }) - - it('carries an un-accepted acknowledgement back to the caller', async () => { - const transport = new BrowserClientPageMetadataTransport() - transport.bind({ - sendPageMetadataRequest: () => Promise.resolve(answered({ accepted: false })) - }) - - await expect(transport.publish(PARAMS)).resolves.toEqual({ accepted: false }) - }) - it('rejects a runtime error and an unreadable acknowledgement', async () => { const failing = new BrowserClientPageMetadataTransport() failing.bind({ @@ -148,29 +121,3 @@ describe('browser client page metadata routing', () => { }) }) }) - -describe('published-url observation', () => { - it('reports every publish to the observer', async () => { - const observeCurrentUrl = vi.fn() - const transport = new BrowserClientPageMetadataTransport(observeCurrentUrl) - transport.bind({ - sendPageMetadataRequest: vi.fn().mockResolvedValue(answered({ accepted: true })) - }) - - await transport.publish(PARAMS) - await transport.publish({ ...PARAMS, revision: 3, url: 'https://example.internal/again' }) - - expect(observeCurrentUrl.mock.calls).toEqual([ - [PARAMS], - [{ ...PARAMS, revision: 3, url: 'https://example.internal/again' }] - ]) - }) - - it('observes the publish even when no lease is bound to send it', async () => { - const observeCurrentUrl = vi.fn() - const transport = new BrowserClientPageMetadataTransport(observeCurrentUrl) - - await expect(transport.publish(PARAMS)).rejects.toBeInstanceOf(RemoteRuntimeClientError) - expect(observeCurrentUrl).toHaveBeenCalledWith(PARAMS) - }) -}) diff --git a/src/main/browser/browser-client-page-published-url.test.ts b/src/main/browser/browser-client-page-published-url.test.ts deleted file mode 100644 index 493639474ad..00000000000 --- a/src/main/browser/browser-client-page-published-url.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - BROWSER_CLIENT_HOST_PAGE_INVENTORY_URL_MAX_LENGTH, - type BrowserClientHostedPageInventory -} from '../../shared/browser-client-host-protocol' -import { BrowserClientPageMetadataParams } from '../../shared/browser-client-page-metadata-protocol' -import { recordBrowserClientPagePublishedUrl } from './browser-client-page-inventory' - -const HOST_CLIENT_ID = 'client-a' -const PAGE_ID = 'page-a' -const PAGE_HOST_GENERATION = 4 - -const inventory = ( - overrides: Partial = {} -): BrowserClientHostedPageInventory => ({ - authorityRuntimeId: 'runtime-a', - authorityEpoch: 'epoch-a', - browserHostClientId: HOST_CLIENT_ID, - browserHostGeneration: 3, - browserPageId: PAGE_ID, - pageHostGeneration: PAGE_HOST_GENERATION, - browserProfileId: 'profile-a', - executionHostKey: `native:runtime-a:1`, - state: 'active', - ...overrides -}) - -const tracked = ( - entry: BrowserClientHostedPageInventory = inventory(), - generation = PAGE_HOST_GENERATION -) => new Map([[entry.browserPageId, { generation, inventory: entry }]]) - -const metadata = (overrides: Partial = {}) => ({ - browserHostClientId: HOST_CLIENT_ID, - browserHostGeneration: 3, - browserPageId: PAGE_ID, - pageHostGeneration: PAGE_HOST_GENERATION, - revision: 1, - url: 'https://remote.example/after-navigation', - title: 'After navigation', - loading: false, - canGoBack: true, - canGoForward: false, - ...overrides -}) - -describe('recordBrowserClientPagePublishedUrl', () => { - it('records the published url on the matching page', () => { - const pages = tracked() - - recordBrowserClientPagePublishedUrl(pages, metadata()) - - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toBe('https://remote.example/after-navigation') - }) - - it('ignores metadata whose pageHostGeneration does not match the tracked page', () => { - const pages = tracked(inventory({ currentUrl: 'https://remote.example/opened-at' })) - - recordBrowserClientPagePublishedUrl(pages, metadata({ pageHostGeneration: 99 })) - - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toBe('https://remote.example/opened-at') - }) - - it('ignores metadata for a page id the map does not track', () => { - const pages = tracked(inventory({ currentUrl: 'https://remote.example/opened-at' })) - - expect(() => - recordBrowserClientPagePublishedUrl(pages, metadata({ browserPageId: 'page-unknown' })) - ).not.toThrow() - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toBe('https://remote.example/opened-at') - expect(pages.has('page-unknown')).toBe(false) - }) - - it.each([ - ['an empty object', {}], - ['undefined', undefined], - ['a string', 'browser.clientHost.pageMetadata'], - ['a non-string url', metadata({ url: 42 as unknown as string })], - ['a generation below the schema floor', metadata({ pageHostGeneration: 0 })] - ])('ignores params that fail schema validation: %s', (_label, params) => { - const pages = tracked(inventory({ currentUrl: 'https://remote.example/opened-at' })) - - expect(() => recordBrowserClientPagePublishedUrl(pages, params)).not.toThrow() - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toBe('https://remote.example/opened-at') - }) - - it('stores a url exactly at the inventory url cap', () => { - const pages = tracked() - const url = `https://remote.example/${'a'.repeat( - BROWSER_CLIENT_HOST_PAGE_INVENTORY_URL_MAX_LENGTH - 'https://remote.example/'.length - )}` - - recordBrowserClientPagePublishedUrl(pages, metadata({ url })) - - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toHaveLength( - BROWSER_CLIENT_HOST_PAGE_INVENTORY_URL_MAX_LENGTH - ) - }) - - // The metadata schema rejects oversized URLs before the inventory’s URL-drop fallback. - it('rejects an over-cap url at schema validation rather than recording it', () => { - const pages = tracked(inventory({ currentUrl: 'https://remote.example/opened-at' })) - const url = 'h'.repeat(BROWSER_CLIENT_HOST_PAGE_INVENTORY_URL_MAX_LENGTH + 1) - - expect(BrowserClientPageMetadataParams.safeParse(metadata({ url })).success).toBe(false) - - recordBrowserClientPagePublishedUrl(pages, metadata({ url })) - - expect(pages.get(PAGE_ID)?.inventory.currentUrl).toBe('https://remote.example/opened-at') - }) -}) diff --git a/src/main/browser/browser-client-page-reconciliation-adapters.test.ts b/src/main/browser/browser-client-page-reconciliation-adapters.test.ts index 3244621c29c..8f73aebcd04 100644 --- a/src/main/browser/browser-client-page-reconciliation-adapters.test.ts +++ b/src/main/browser/browser-client-page-reconciliation-adapters.test.ts @@ -248,32 +248,6 @@ describe('browser client page reconciliation adapters', () => { ]) }) - it('restores a host-owned profile with the metadata the host sent', async () => { - const { executor, dependencies } = createHarness() - const profile = { - id: 'remote-profile', - label: 'Remote profile', - scope: 'isolated' as const - } - const restore = command('restorePage', { - command: { - type: 'restorePage', - browserProfileId: profile.id, - profile, - executionHostKey: 'execution-host-a' - } - }) - await expect(executor.handle(restore, new AbortController().signal)).resolves.toEqual({ - status: 'completed' - }) - expect(dependencies.routeSessions.preparePage).toHaveBeenCalledWith( - expect.objectContaining({ - identity: expect.objectContaining({ browserProfileId: profile.id }), - profileMetadata: profile - }) - ) - }) - it('destroys a rekeyed guest instead of restoring authority after renderer failure', async () => { const { executor, renderer, routeWebContents } = createHarness() await executor.handle(command('createPage'), new AbortController().signal) diff --git a/src/main/browser/browser-client-page-unavailability.test.ts b/src/main/browser/browser-client-page-unavailability.test.ts index 83dd1bc2dbb..a3b156008a5 100644 --- a/src/main/browser/browser-client-page-unavailability.test.ts +++ b/src/main/browser/browser-client-page-unavailability.test.ts @@ -41,16 +41,6 @@ describe('browser client page unavailability', () => { expect(renderer.retirePage).not.toHaveBeenCalled() expect(executor.snapshotPageInventory()).toEqual([]) }) - - it('releases the page availability watch when the page retires', async () => { - const { executor, releaseAvailabilityWatch } = harness() - await executor.handle(createCommand(), new AbortController().signal) - expect(releaseAvailabilityWatch).not.toHaveBeenCalled() - - await expect(executor.retirePage('page-a', 7)).resolves.toBe(true) - - expect(releaseAvailabilityWatch).toHaveBeenCalledOnce() - }) }) function harness() { diff --git a/src/main/browser/browser-client-page-upload-routing.test.ts b/src/main/browser/browser-client-page-upload-routing.test.ts index 5217137b52a..4c7550e2846 100644 --- a/src/main/browser/browser-client-page-upload-routing.test.ts +++ b/src/main/browser/browser-client-page-upload-routing.test.ts @@ -6,10 +6,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { BrowserClientHostCommandEvent } from '../../shared/browser-client-host-protocol' import { BrowserClientFileChannelTransport } from './browser-client-file-channel-transport' import { BrowserClientPageCommandExecutor } from './browser-client-page-command-executor' -import { - BROWSER_CLIENT_UPLOAD_STAGING_MAX_COMMANDS_PER_PAGE, - BrowserClientUploadStaging -} from './browser-client-upload-staging' +import { BrowserClientUploadStaging } from './browser-client-upload-staging' import type { BrowserRoutePageGuestIdentity } from './browser-route-page-authority' const partition = `persist:orca-browser-v1-${'a'.repeat(64)}` @@ -153,23 +150,6 @@ function negotiatedTransport(contents: string): BrowserClientFileChannelTranspor } describe('client-placed browser.upload routing', () => { - it('never forwards the remote paths verbatim to the local automation runtime', async () => { - const transport = negotiatedTransport('remote-bytes') - const { executor, automationCalls } = createHarness({ fileChannel: transport }) - await executor.handle(createPage, new AbortController().signal) - - const result = await executor.handle( - uploadCommand(['docs/report.pdf']), - new AbortController().signal - ) - - expect(result).toEqual({ status: 'completed', value: { uploaded: true } }) - const forwarded = automationCalls[0].params.files as string[] - expect(forwarded).not.toContain('docs/report.pdf') - expect(path.basename(forwarded[0])).toBe('report.pdf') - expect(forwarded[0].startsWith(stagingRoot)).toBe(true) - }) - it('fails the command instead of resolving remote paths on this desktop when unnegotiated', async () => { const { executor, executeAutomation } = createHarness() await executor.handle(createPage, new AbortController().signal) @@ -202,26 +182,6 @@ describe('client-placed browser.upload routing', () => { expect(await readdir(stagingRoot)).toHaveLength(0) }) - it('evicts a page oldest-first once it exceeds the staged-command budget', async () => { - const transport = negotiatedTransport('remote-bytes') - const { executor, uploadStaging } = createHarness({ fileChannel: transport }) - await executor.handle(createPage, new AbortController().signal) - - for (let sequence = 2; sequence <= 8; sequence += 1) { - await executor.handle( - uploadCommand(['docs/report.pdf'], sequence), - new AbortController().signal - ) - } - - expect(uploadStaging.activeStagingCount()).toBe( - BROWSER_CLIENT_UPLOAD_STAGING_MAX_COMMANDS_PER_PAGE - ) - expect(await readdir(stagingRoot)).toHaveLength( - BROWSER_CLIENT_UPLOAD_STAGING_MAX_COMMANDS_PER_PAGE - ) - }) - it('retires the guest and releases the route when the staged copies cannot be removed', async () => { const transport = negotiatedTransport('remote-bytes') const { executor, retireRendererPage, releaseRouteSession, releaseNetworkRoute } = diff --git a/src/main/browser/browser-client-route-cookie-import.test.ts b/src/main/browser/browser-client-route-cookie-import.test.ts index 1ebc5194807..f916130ef33 100644 --- a/src/main/browser/browser-client-route-cookie-import.test.ts +++ b/src/main/browser/browser-client-route-cookie-import.test.ts @@ -88,31 +88,6 @@ beforeEach(() => { }) describe('importCookiesIntoClientRoutePartition', () => { - it('imports into a main-derived route partition, never the server profile partition', async () => { - const result = await importCookiesIntoClientRoutePartition(request) - - expect(result).toMatchObject({ ok: true, profileId: 'default' }) - expect(requireRouteBrowserProfileMock).toHaveBeenCalledWith('default') - const [source, partition] = importCookiesFromBrowserMock.mock.calls[0] ?? [] - expect(source).toBe(chrome) - expect(partition).toMatch(/^persist:orca-browser-v1-[a-f0-9]{64}$/) - expect(bindingStore.set).toHaveBeenCalledWith( - partition, - expect.stringMatching(/^[a-f0-9]{64}$/), - routeIdentity.storageScope, - expect.stringMatching(/^[a-f0-9]{64}$/) - ) - expect(recordClientRouteImportSourceMock).toHaveBeenCalledWith({ - environmentId: 'env-a', - profileId: 'default', - source: { - browserFamily: 'chrome', - profileName: 'Person 1', - importedAt: expect.any(Number) - } - }) - }) - it('defers to the server-side import when the desktop hosts no pages for the server', async () => { getRouteIdentityMock.mockReturnValue(null) @@ -137,16 +112,6 @@ describe('importCookiesIntoClientRoutePartition', () => { expect(detectInstalledBrowsersMock).not.toHaveBeenCalled() }) - it('reselects a non-default desktop browser profile', async () => { - const secondary = { ...chrome, selectedProfile: 'Profile 2' } - selectBrowserProfileMock.mockReturnValue(secondary) - - await importCookiesIntoClientRoutePartition({ ...request, browserProfile: 'Profile 2' }) - - expect(selectBrowserProfileMock).toHaveBeenCalledWith(chrome, 'Profile 2') - expect(importCookiesFromBrowserMock.mock.calls[0]?.[0]).toBe(secondary) - }) - it('leaves the profile source untouched when the import fails', async () => { importCookiesFromBrowserMock.mockResolvedValue({ ok: false, reason: 'locked database' }) diff --git a/src/main/browser/browser-client-upload-command.test.ts b/src/main/browser/browser-client-upload-command.test.ts index 57a98626973..829236b3934 100644 --- a/src/main/browser/browser-client-upload-command.test.ts +++ b/src/main/browser/browser-client-upload-command.test.ts @@ -1,8 +1,7 @@ -import { existsSync } from 'node:fs' -import { mkdtemp, readFile, readdir, realpath, rm } from 'node:fs/promises' +import { mkdtemp, realpath, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' import type { BrowserClientHostCommandEvent } from '../../shared/browser-client-host-protocol' import { BrowserClientFileChannelTransport } from './browser-client-file-channel-transport' @@ -62,79 +61,6 @@ function transportReturning( } describe('executeBrowserClientUploadCommand', () => { - it('rewrites remote paths to staged copies that outlive the command until the page is released', async () => { - const staging = new BrowserClientUploadStaging(stagingRoot) - const { transport, calls } = transportReturning( - new Map([ - [ - 'docs/report.pdf', - [{ contentBase64: Buffer.from('hello').toString('base64'), bytesRead: 5, eof: true }] - ] - ]) - ) - const run = vi.fn().mockResolvedValue({ uploaded: true }) - - const result = await executeBrowserClientUploadCommand({ - event: uploadEvent(['docs/report.pdf']), - params: { element: '#f', files: ['docs/report.pdf'] }, - fileChannel: transport, - staging, - run - }) - - expect(result).toEqual({ uploaded: true }) - const rewritten = run.mock.calls[0][0] as { files: string[] } - expect(rewritten.files).toHaveLength(1) - expect(path.basename(rewritten.files[0])).toBe('report.pdf') - expect(rewritten.files[0]).not.toBe('docs/report.pdf') - expect(calls).toHaveLength(1) - expect(await readFile(rewritten.files[0], 'utf8')).toBe('hello') - expect(staging.activeStagingCount()).toBe(1) - - expect(await staging.releasePage('page-1')).toBe(1) - expect(await readdir(stagingRoot)).toHaveLength(0) - }) - - it('fails closed and stages nothing when the file channel was not negotiated', async () => { - const staging = new BrowserClientUploadStaging(stagingRoot) - const transport = new BrowserClientFileChannelTransport() - const run = vi.fn() - - await expect( - executeBrowserClientUploadCommand({ - event: uploadEvent(['/etc/passwd']), - params: { element: '#f', files: ['/etc/passwd'] }, - fileChannel: transport, - staging, - run - }) - ).rejects.toThrow('browser_client_file_channel_unsupported') - expect(run).not.toHaveBeenCalled() - expect(staging.activeStagingCount()).toBe(0) - expect(existsSync(stagingRoot)).toBe(false) - }) - - it('removes staged files when the upload itself fails', async () => { - const staging = new BrowserClientUploadStaging(stagingRoot) - const { transport } = transportReturning( - new Map([['a.txt', [{ contentBase64: '', bytesRead: 0, eof: true }]]]) - ) - - await expect( - executeBrowserClientUploadCommand({ - event: uploadEvent(['a.txt']), - params: { element: '#f', files: ['a.txt'] }, - fileChannel: transport, - staging, - run: async () => { - throw new Error('element not found') - } - }) - ).rejects.toThrow('element not found') - expect(staging.activeStagingCount()).toBe(0) - expect(await readdir(stagingRoot)).toHaveLength(0) - }) - it('reports the upload failure, not the cleanup failure, when both fail', async () => { const staging = new BrowserClientUploadStaging(stagingRoot, { mkdir: async () => {}, diff --git a/src/main/browser/browser-client-upload-staging.test.ts b/src/main/browser/browser-client-upload-staging.test.ts index 933f16436d1..ff71dab3de1 100644 --- a/src/main/browser/browser-client-upload-staging.test.ts +++ b/src/main/browser/browser-client-upload-staging.test.ts @@ -1,9 +1,8 @@ import { existsSync } from 'node:fs' -import type * as NodeFsPromises from 'node:fs/promises' import { mkdir, mkdtemp, readFile, readdir, realpath, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { BROWSER_CLIENT_UPLOAD_STAGING_MAX_BYTES_PER_PAGE, @@ -11,19 +10,6 @@ import { BrowserClientUploadStaging } from './browser-client-upload-staging' -const nodeRemovals = vi.hoisted(() => ({ rm: [] as { target: unknown; options: unknown }[] })) - -vi.mock('node:fs/promises', async (importOriginal) => { - const actual = await importOriginal() - return { - ...actual, - rm: (target: unknown, options: unknown) => { - nodeRemovals.rm.push({ target, options }) - return (actual.rm as (t: unknown, o: unknown) => Promise)(target, options) - } - } -}) - let stagingRoot = '' beforeEach(async () => { @@ -308,24 +294,4 @@ describe('BrowserClientUploadStaging', () => { // A later release needs the retained directory record to retry cleanup. expect(staging.activeStagingCount()).toBe(1) }) - - it('retries the staged removal so a briefly held file is not orphaned', async () => { - const staging = new BrowserClientUploadStaging(stagingRoot) - const staged = await staging.stage({ - browserPageId: 'page-1', - pageHostGeneration: 1, - files: [{ remotePath: 'a.txt', contents: Buffer.from('a') }] - }) - const directory = staging.stagedDirectory(staged.stagingId) - nodeRemovals.rm.length = 0 - - expect(await staging.release(staged.stagingId)).toBe(true) - - expect(nodeRemovals.rm).toEqual([ - { - target: directory, - options: { recursive: true, force: true, maxRetries: 3, retryDelay: 50 } - } - ]) - }) }) diff --git a/src/main/browser/browser-client-upload-transfer.test.ts b/src/main/browser/browser-client-upload-transfer.test.ts index fdc129618e8..3c80d39cbf3 100644 --- a/src/main/browser/browser-client-upload-transfer.test.ts +++ b/src/main/browser/browser-client-upload-transfer.test.ts @@ -1,11 +1,7 @@ import { describe, expect, it } from 'vitest' import type { BrowserClientHostCommandEvent } from '../../shared/browser-client-host-protocol' -import { - BROWSER_CLIENT_FILE_CHANNEL_READ_METHOD, - fetchBrowserClientUploadFiles, - readBrowserClientUploadPaths -} from './browser-client-upload-transfer' +import { fetchBrowserClientUploadFiles } from './browser-client-upload-transfer' const event = { authorityRuntimeId: 'runtime-1', @@ -17,37 +13,6 @@ const event = { } as BrowserClientHostCommandEvent describe('fetchBrowserClientUploadFiles', () => { - it('reassembles a multi-chunk remote file and stamps the page authority on every read', async () => { - const requests: { method: string; params: Record }[] = [] - const chunks = ['abc', 'de'] - const files = await fetchBrowserClientUploadFiles({ - request: async (method, params) => { - requests.push({ method, params: params as Record }) - const chunk = chunks.shift() ?? '' - return { - contentBase64: Buffer.from(chunk).toString('base64'), - bytesRead: chunk.length, - totalBytes: 5, - eof: chunks.length === 0 - } - }, - event, - remotePaths: ['docs/a.txt'] - }) - - expect(files).toEqual([{ remotePath: 'docs/a.txt', contents: Buffer.from('abcde') }]) - expect(requests).toHaveLength(2) - expect(requests[0].method).toBe(BROWSER_CLIENT_FILE_CHANNEL_READ_METHOD) - expect(requests[0].params).toMatchObject({ - fileChannelProtocolVersion: 1, - browserPageId: 'page-1', - pageHostGeneration: 3, - workspaceRelativePath: 'docs/a.txt', - offset: 0 - }) - expect(requests[1].params).toMatchObject({ offset: 3 }) - }) - it('stops instead of looping forever on a host that never reports eof', async () => { await expect( fetchBrowserClientUploadFiles({ @@ -83,22 +48,3 @@ describe('fetchBrowserClientUploadFiles', () => { ).rejects.toThrow('browser_client_upload_file_count_exceeded') }) }) - -describe('readBrowserClientUploadPaths', () => { - it('accepts a list of non-empty path strings', () => { - expect(readBrowserClientUploadPaths({ files: ['a.txt', 'b/c.txt'] })).toEqual([ - 'a.txt', - 'b/c.txt' - ]) - }) - - it('rejects a missing, non-array, or non-string files payload', () => { - expect(() => readBrowserClientUploadPaths({})).toThrow('browser_client_upload_files_required') - expect(() => readBrowserClientUploadPaths({ files: 'a.txt' })).toThrow( - 'browser_client_upload_files_required' - ) - expect(() => readBrowserClientUploadPaths({ files: [''] })).toThrow( - 'browser_client_upload_files_required' - ) - }) -}) diff --git a/src/main/browser/browser-execution-host-storage-identity.test.ts b/src/main/browser/browser-execution-host-storage-identity.test.ts deleted file mode 100644 index f685e036909..00000000000 --- a/src/main/browser/browser-execution-host-storage-identity.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - browserAuthorityExecutionHostStorageIdentity, - browserNetworkExecutionHostStorageIdentity, - legacyBrowserNativeExecutionHostStorageIdentity, - legacyBrowserNetworkExecutionHostStorageIdentity -} from './browser-execution-host-storage-identity' -import { browserNetworkExecutionHostKey } from './browser-network-execution-route' - -const storageKey = 'a'.repeat(64) -const otherStorageKey = 'b'.repeat(64) - -describe('browserNetworkExecutionHostStorageIdentity', () => { - it('ignores the per-boot components the route key fences on', () => { - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-a', revision: 1 }, - storageKey - ) - ).toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-a', revision: 2 }, - storageKey - ) - ) - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 1, distro: 'Ubuntu' }, - storageKey - ) - ).toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 2, distro: 'Ubuntu' }, - storageKey - ) - ) - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: 'ssh-1', providerEpoch: 'epoch-a', connectionGeneration: 1 }, - storageKey - ) - ).toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: 'ssh-1', providerEpoch: 'epoch-b', connectionGeneration: 2 }, - storageKey - ) - ) - }) - - it('survives the authority runtime restarting under a new runtimeId', () => { - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-a', revision: 1 }, - storageKey - ) - ).toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-b', revision: 9 }, - storageKey - ) - ) - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 1, distro: 'Ubuntu' }, - storageKey - ) - ).toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-b', revision: 9, distro: 'Ubuntu' }, - storageKey - ) - ) - }) - - it('separates every boundary that changes storage or egress', () => { - const identities = [ - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-a', revision: 1 }, - storageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'runtime-a', revision: 1 }, - otherStorageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 1, distro: 'Ubuntu' }, - storageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 1, distro: 'Debian' }, - storageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'runtime-a', revision: 1, distro: 'Ubuntu' }, - otherStorageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: 'ssh-1', providerEpoch: 'epoch-a', connectionGeneration: 1 }, - storageKey - ), - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: 'ssh-2', providerEpoch: 'epoch-a', connectionGeneration: 1 }, - storageKey - ) - ] - - expect(new Set(identities).size).toBe(identities.length) - }) - - it('keeps delimiter-bearing components structurally distinct', () => { - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'a', revision: 1, distro: 'b","c' }, - 'a' - ) - ).not.toBe( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'a', revision: 1, distro: 'c' }, - 'a","b' - ) - ) - }) - - // These serialized identities name persisted partitions, including migration sources. - it('pins the storage identity of every host kind, current and pre-migration', () => { - const tag = '"orca-browser-execution-host-storage",1' - - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'native', runtimeId: 'r', revision: 1 }, - storageKey - ) - ).toBe(`[${tag},"authority","${storageKey}"]`) - expect(browserAuthorityExecutionHostStorageIdentity(storageKey)).toBe( - `[${tag},"authority","${storageKey}"]` - ) - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'wsl', runtimeId: 'r', revision: 1, distro: 'Ubuntu' }, - storageKey - ) - ).toBe(`[${tag},"authority-wsl","${storageKey}","Ubuntu"]`) - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: 'ssh-1', providerEpoch: 'e', connectionGeneration: 1 }, - storageKey - ) - ).toBe(`[${tag},"ssh","ssh-1"]`) - - expect(legacyBrowserNativeExecutionHostStorageIdentity('r')).toBe(`[${tag},"native","r"]`) - expect( - legacyBrowserNetworkExecutionHostStorageIdentity({ - kind: 'native', - runtimeId: 'r', - revision: 1 - }) - ).toBe(`[${tag},"native","r"]`) - expect( - legacyBrowserNetworkExecutionHostStorageIdentity({ - kind: 'wsl', - runtimeId: 'r', - revision: 1, - distro: 'Ubuntu' - }) - ).toBe(`[${tag},"wsl","r","Ubuntu"]`) - expect( - legacyBrowserNetworkExecutionHostStorageIdentity({ - kind: 'ssh', - targetId: 'ssh-1', - providerEpoch: 'e', - connectionGeneration: 1 - }) - ).toBe(`[${tag},"ssh","ssh-1"]`) - }) - - it('keeps pre-migration WSL distros apart on one runtime', () => { - expect( - legacyBrowserNetworkExecutionHostStorageIdentity({ - kind: 'wsl', - runtimeId: 'r', - revision: 1, - distro: 'Ubuntu' - }) - ).not.toBe( - legacyBrowserNetworkExecutionHostStorageIdentity({ - kind: 'wsl', - runtimeId: 'r', - revision: 1, - distro: 'Debian' - }) - ) - }) - - it('never lets an SSH target share the paired server machine identity', () => { - const shared = 'ssh-1700000000-aaa111' - - expect( - browserNetworkExecutionHostStorageIdentity( - { kind: 'ssh', targetId: shared, providerEpoch: 'e', connectionGeneration: 1 }, - shared - ) - ).not.toBe(browserAuthorityExecutionHostStorageIdentity(shared)) - }) - - it('is never mistaken for a route fencing key', () => { - const host = { - kind: 'native', - runtimeId: 'runtime-a', - revision: 7 - } as const - - expect(browserNetworkExecutionHostStorageIdentity(host, storageKey)).not.toBe( - browserNetworkExecutionHostKey(host) - ) - }) -}) diff --git a/src/main/browser/browser-external-app-launch.test.ts b/src/main/browser/browser-external-app-launch.test.ts index ec734babe61..ab2f091b1ca 100644 --- a/src/main/browser/browser-external-app-launch.test.ts +++ b/src/main/browser/browser-external-app-launch.test.ts @@ -127,11 +127,6 @@ describe('browser external app launch', () => { expect(renderer.send).not.toHaveBeenCalled() }) - it('names no site when the asking frame is unknown', () => { - ask(OKTA_URL, null) - expect(sentRequests(renderer)[0].details).toMatchObject({ origin: null }) - }) - it('refuses a page with no window on this computer', () => { resolveTarget = () => null expect(ask()).toBe(false) diff --git a/src/main/browser/browser-google-auth-ua.test.ts b/src/main/browser/browser-google-auth-ua.test.ts deleted file mode 100644 index a1ed1370418..00000000000 --- a/src/main/browser/browser-google-auth-ua.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import { - googleAuthUserAgent, - isGoogleAuthUrl, - setUserAgentHeader, - stripClientHints -} from './browser-google-auth-ua' - -describe('isGoogleAuthUrl', () => { - it('matches the Google/YouTube sign-in hosts exactly', () => { - expect(isGoogleAuthUrl('https://accounts.google.com/')).toBe(true) - expect(isGoogleAuthUrl('https://accounts.google.com/v3/signin/identifier')).toBe(true) - expect(isGoogleAuthUrl('https://accounts.youtube.com/signin')).toBe(true) - expect(isGoogleAuthUrl('https://ACCOUNTS.GOOGLE.COM/')).toBe(true) - }) - - it('does not match post-auth app subdomains or lookalikes', () => { - expect(isGoogleAuthUrl('https://myaccount.google.com/')).toBe(false) - expect(isGoogleAuthUrl('https://mail.google.com/')).toBe(false) - expect(isGoogleAuthUrl('https://accounts.google.com.evil.test/')).toBe(false) - expect(isGoogleAuthUrl('https://google.com/')).toBe(false) - expect(isGoogleAuthUrl('not a url')).toBe(false) - }) -}) - -describe('googleAuthUserAgent', () => { - it('produces an internally consistent Firefox UA for the host platform', () => { - const ua = googleAuthUserAgent() - expect(ua).toMatch(/^Mozilla\/5\.0 \(.+; rv:\d+\.0\) Gecko\/20100101 Firefox\/\d+\.0$/) - expect(ua).not.toContain('Chrome') - expect(ua).not.toContain('Electron') - }) -}) - -describe('stripClientHints', () => { - it('removes every sec-ch-ua* header regardless of case, keeps others', () => { - const headers: Record = { - 'sec-ch-ua': 'a', - 'Sec-CH-UA-Platform': 'b', - 'sec-ch-ua-full-version-list': 'c', - 'User-Agent': 'ua', - Accept: 'text/html' - } - stripClientHints(headers) - expect(Object.keys(headers).some((k) => k.toLowerCase().startsWith('sec-ch-ua'))).toBe(false) - expect(headers['User-Agent']).toBe('ua') - expect(headers.Accept).toBe('text/html') - }) -}) - -describe('setUserAgentHeader', () => { - it('overwrites an existing user-agent header in place, preserving its casing', () => { - const headers: Record = { 'user-agent': 'old' } - setUserAgentHeader(headers, 'new') - expect(headers['user-agent']).toBe('new') - expect(Object.keys(headers)).toEqual(['user-agent']) - }) - - it('adds a canonical header when none exists', () => { - const headers: Record = {} - setUserAgentHeader(headers, 'new') - expect(headers['User-Agent']).toBe('new') - }) -}) diff --git a/src/main/browser/browser-guest-context-menu.test.ts b/src/main/browser/browser-guest-context-menu.test.ts deleted file mode 100644 index 24ac14407bd..00000000000 --- a/src/main/browser/browser-guest-context-menu.test.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { screenGetCursorScreenPointMock } = vi.hoisted(() => ({ - screenGetCursorScreenPointMock: vi.fn(() => ({ x: 0, y: 0 })) -})) - -vi.mock('electron', () => ({ - screen: { getCursorScreenPoint: screenGetCursorScreenPointMock } -})) - -import { setupGuestContextMenu } from './browser-guest-context-menu' - -describe('setupGuestContextMenu', () => { - const browserTabId = 'tab-1' - let rendererSendMock: ReturnType - let guestOnMock: ReturnType - let guestOffMock: ReturnType - - function makeGuest(overrides: Record = {}) { - return { - getURL: vi.fn(() => 'https://example.com'), - canGoBack: vi.fn(() => true), - canGoForward: vi.fn(() => false), - navigationHistory: { - canGoBack: vi.fn(() => true), - canGoForward: vi.fn(() => false) - }, - on: guestOnMock, - off: guestOffMock, - ...overrides - } as unknown as Electron.WebContents - } - - function makeRenderer() { - return { send: rendererSendMock } as unknown as Electron.WebContents - } - - beforeEach(() => { - rendererSendMock = vi.fn() - guestOnMock = vi.fn() - guestOffMock = vi.fn() - screenGetCursorScreenPointMock.mockReturnValue({ x: 0, y: 0 }) - }) - - function triggerContextMenu( - _guest: Electron.WebContents, - params: Partial - ) { - const handler = guestOnMock.mock.calls.find((call) => call[0] === 'context-menu')?.[1] as - | ((event: unknown, params: Electron.ContextMenuParams) => void) - | undefined - - expect(handler).toBeTypeOf('function') - handler!({}, { x: 0, y: 0, linkURL: '', ...params } as Electron.ContextMenuParams) - } - - it('passes through guest viewport coordinates (params.x/y) to the renderer', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 150, y: 275 }) - - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:context-menu-requested', - expect.objectContaining({ x: 150, y: 275 }) - ) - }) - - it('includes navigation state and page URL alongside coordinates', () => { - screenGetCursorScreenPointMock.mockReturnValue({ x: 500, y: 375 }) - const guest = makeGuest({ - getURL: vi.fn(() => 'https://test.dev/page'), - navigationHistory: { - canGoBack: vi.fn(() => true), - canGoForward: vi.fn(() => true) - } - }) - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 50, y: 75, linkURL: 'https://test.dev/link' }) - - expect(rendererSendMock).toHaveBeenCalledWith('browser:context-menu-requested', { - browserPageId: browserTabId, - x: 50, - y: 75, - screenX: 500, - screenY: 375, - pageUrl: 'https://test.dev/page', - linkUrl: 'https://test.dev/link', - selectionText: '', - canGoBack: true, - canGoForward: true, - editable: null, - image: null - }) - }) - - it('forwards the native selection text so the renderer can offer Copy', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 10, y: 20, selectionText: 'copied selection' }) - - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:context-menu-requested', - expect.objectContaining({ selectionText: 'copied selection' }) - ) - }) - - it('reads navigation state from navigationHistory', () => { - const deprecatedCanGoBack = vi.fn(() => false) - const deprecatedCanGoForward = vi.fn(() => false) - const guest = makeGuest({ - canGoBack: deprecatedCanGoBack, - canGoForward: deprecatedCanGoForward, - navigationHistory: { - canGoBack: vi.fn(() => true), - canGoForward: vi.fn(() => true) - } - }) - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 50, y: 75 }) - - expect(deprecatedCanGoBack).not.toHaveBeenCalled() - expect(deprecatedCanGoForward).not.toHaveBeenCalled() - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:context-menu-requested', - expect.objectContaining({ canGoBack: true, canGoForward: true }) - ) - }) - - it('describes the editable field and image under the cursor', () => { - const renderer = makeRenderer() - setupGuestContextMenu({ browserTabId, guest: makeGuest(), resolveRenderer: () => renderer }) - - triggerContextMenu(makeGuest(), { - isEditable: true, - editFlags: { - canUndo: true, - canRedo: false, - canCut: true, - canCopy: true, - canPaste: false, - canSelectAll: true, - canDelete: true, - canEditRichly: false - }, - misspelledWord: 'quikc', - dictionarySuggestions: ['quick', 'quirk', 'quack', 'quiche', 'quince', 'quinoa'], - mediaType: 'image', - srcURL: 'https://cdn.test/a.png', - hasImageContents: true - }) - - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:context-menu-requested', - expect.objectContaining({ - editable: { - canUndo: true, - canRedo: false, - canCut: true, - canCopy: true, - canPaste: false, - canSelectAll: true, - spelling: { - suggestions: ['quick', 'quirk', 'quack', 'quiche', 'quince'], - misspelledWord: 'quikc' - } - }, - image: { url: 'https://cdn.test/a.png', canCopy: true } - }) - ) - }) - - it('does not send when renderer is unavailable', () => { - const guest = makeGuest() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => null - }) - - triggerContextMenu(guest, { x: 100, y: 200 }) - - expect(rendererSendMock).not.toHaveBeenCalled() - }) - - it('cleans up context-menu listener on teardown', () => { - const guest = makeGuest() - - const cleanup = setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => makeRenderer() - }) - - cleanup() - - expect(guestOffMock).toHaveBeenCalledWith('context-menu', expect.any(Function)) - }) - - describe('dismiss handler', () => { - function triggerMouseEvent(button: string, type: string = 'mouseDown') { - const beforeMouseHandler = guestOnMock.mock.calls.find( - (call) => call[0] === 'before-mouse-event' - )?.[1] as ((event: unknown, mouse: { type: string; button: string }) => void) | undefined - - expect(beforeMouseHandler).toBeTypeOf('function') - beforeMouseHandler!({}, { type, button }) - } - - it('dismisses context menu on left-click', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 100, y: 200 }) - rendererSendMock.mockClear() - - triggerMouseEvent('left') - - expect(rendererSendMock).toHaveBeenCalledWith('browser:context-menu-dismissed', { - browserPageId: browserTabId - }) - }) - - it('does not dismiss context menu on right-click', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 100, y: 200 }) - rendererSendMock.mockClear() - - triggerMouseEvent('right') - - expect(rendererSendMock).not.toHaveBeenCalledWith( - 'browser:context-menu-dismissed', - expect.anything() - ) - }) - - it('dismisses context menu on middle-click', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 100, y: 200 }) - rendererSendMock.mockClear() - - triggerMouseEvent('middle') - - expect(rendererSendMock).toHaveBeenCalledWith('browser:context-menu-dismissed', { - browserPageId: browserTabId - }) - }) - - it('ignores non-mouseDown events', () => { - const guest = makeGuest() - const renderer = makeRenderer() - - setupGuestContextMenu({ - browserTabId, - guest, - resolveRenderer: () => renderer - }) - - triggerContextMenu(guest, { x: 100, y: 200 }) - rendererSendMock.mockClear() - - triggerMouseEvent('left', 'mouseMove') - - expect(rendererSendMock).not.toHaveBeenCalled() - }) - }) -}) diff --git a/src/main/browser/browser-guest-shortcut-forwarding.test.ts b/src/main/browser/browser-guest-shortcut-forwarding.test.ts index 60a7a07f7a1..2c923a8f2c6 100644 --- a/src/main/browser/browser-guest-shortcut-forwarding.test.ts +++ b/src/main/browser/browser-guest-shortcut-forwarding.test.ts @@ -9,14 +9,10 @@ vi.mock('electron', () => ({ webContents: { fromId: vi.fn() } })) -import { - resolveGuestMouseWheelZoomDirection, - setupGuestMouseWheelZoomForwarding -} from './browser-guest-wheel-zoom' +import { setupGuestMouseWheelZoomForwarding } from './browser-guest-wheel-zoom' import { setupGuestShortcutForwarding } from './browser-guest-shortcut-forwarding' const zoomTo = (direction: 'in' | 'out' | 'reset') => ({ browserPageId: 'tab-1', direction }) -const historyTo = (direction: 'back' | 'forward') => ({ browserPageId: 'tab-1', direction }) describe('guest mouse wheel browser zoom', () => { const browserTabId = 'tab-1' @@ -64,37 +60,6 @@ describe('guest mouse wheel browser zoom', () => { guestOffMock = vi.fn() }) - it('resolves ctrl wheel direction from guest mouse input', () => { - expect(resolveGuestMouseWheelZoomDirection(mouseWheel({ deltaY: -120 }), 'win32')).toBe('in') - expect(resolveGuestMouseWheelZoomDirection(mouseWheel({ deltaY: 120 }), 'linux')).toBe('out') - }) - - it('allows command wheel only on macOS', () => { - const commandWheel = mouseWheel({ modifiers: ['cmd'], deltaY: -120 }) - - expect(resolveGuestMouseWheelZoomDirection(commandWheel, 'darwin')).toBe('in') - expect(resolveGuestMouseWheelZoomDirection(commandWheel, 'win32')).toBeNull() - }) - - it('ignores non-zoom wheel input', () => { - expect( - resolveGuestMouseWheelZoomDirection(mouseWheel({ modifiers: [], deltaY: -120 }), 'linux') - ).toBeNull() - expect( - resolveGuestMouseWheelZoomDirection(mouseWheel({ modifiers: ['ctrl', 'alt'] }), 'linux') - ).toBeNull() - expect( - resolveGuestMouseWheelZoomDirection(mouseWheel({ modifiers: ['ctrl', 'shift'] }), 'linux') - ).toBeNull() - expect(resolveGuestMouseWheelZoomDirection(mouseWheel({ deltaY: 0 }), 'linux')).toBeNull() - expect( - resolveGuestMouseWheelZoomDirection( - { type: 'mouseMove', x: 0, y: 0, modifiers: ['ctrl'] }, - 'linux' - ) - ).toBeNull() - }) - it('forwards ctrl wheel to browser page zoom and consumes the guest wheel event', () => { setupGuestMouseWheelZoomForwarding({ browserTabId, @@ -123,18 +88,6 @@ describe('guest mouse wheel browser zoom', () => { expect(preventDefault).toHaveBeenCalledTimes(1) expect(rendererSendMock).not.toHaveBeenCalled() }) - - it('cleans up the mouse wheel listener on teardown', () => { - const cleanup = setupGuestMouseWheelZoomForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer() - }) - - cleanup() - - expect(guestOffMock).toHaveBeenCalledWith('before-mouse-event', expect.any(Function)) - }) }) describe('setupGuestShortcutForwarding', () => { @@ -250,151 +203,6 @@ describe('setupGuestShortcutForwarding', () => { } }) - it('forwards browser page zoom shortcuts from focused guest pages', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer() - }) - - const zoomInPreventDefault = triggerBeforeInput({ code: 'Equal', key: '=' }) - const shiftedPlusPreventDefault = triggerBeforeInput({ - code: 'Equal', - key: '+', - shift: true - }) - const zoomOutPreventDefault = triggerBeforeInput({ code: 'Minus', key: '-' }) - const numpadSubtractPreventDefault = triggerBeforeInput({ code: 'NumpadSubtract', key: '-' }) - const resetPreventDefault = triggerBeforeInput({ code: 'Digit0', key: '0' }) - const repeatPreventDefault = triggerBeforeInput({ - code: 'NumpadAdd', - key: '+', - isAutoRepeat: true - }) - - expect(zoomInPreventDefault).toHaveBeenCalledTimes(1) - expect(shiftedPlusPreventDefault).toHaveBeenCalledTimes(1) - expect(zoomOutPreventDefault).toHaveBeenCalledTimes(1) - expect(numpadSubtractPreventDefault).toHaveBeenCalledTimes(1) - expect(resetPreventDefault).toHaveBeenCalledTimes(1) - expect(repeatPreventDefault).toHaveBeenCalledTimes(1) - expect(rendererSendMock).toHaveBeenNthCalledWith(1, 'ui:zoomBrowserPage', zoomTo('in')) - expect(rendererSendMock).toHaveBeenNthCalledWith(2, 'ui:zoomBrowserPage', zoomTo('in')) - expect(rendererSendMock).toHaveBeenNthCalledWith(3, 'ui:zoomBrowserPage', zoomTo('out')) - expect(rendererSendMock).toHaveBeenNthCalledWith(4, 'ui:zoomBrowserPage', zoomTo('out')) - expect(rendererSendMock).toHaveBeenNthCalledWith(5, 'ui:zoomBrowserPage', zoomTo('reset')) - expect(rendererSendMock).toHaveBeenNthCalledWith(6, 'ui:zoomBrowserPage', zoomTo('in')) - }) - - it('forwards browser history shortcuts from focused guest pages', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer() - }) - - const backInput = - process.platform === 'darwin' - ? { code: 'BracketLeft', key: '[', meta: true, control: false, alt: false } - : { code: 'ArrowLeft', key: 'ArrowLeft', meta: false, control: false, alt: true } - const forwardInput = - process.platform === 'darwin' - ? { code: 'BracketRight', key: ']', meta: true, control: false, alt: false } - : { code: 'ArrowRight', key: 'ArrowRight', meta: false, control: false, alt: true } - - const backPreventDefault = triggerBeforeInput(backInput) - const forwardPreventDefault = triggerBeforeInput(forwardInput) - - expect(backPreventDefault).toHaveBeenCalledTimes(1) - expect(forwardPreventDefault).toHaveBeenCalledTimes(1) - expect(rendererSendMock).toHaveBeenNthCalledWith( - 1, - 'ui:browserHistoryNavigate', - historyTo('back') - ) - expect(rendererSendMock).toHaveBeenNthCalledWith( - 2, - 'ui:browserHistoryNavigate', - historyTo('forward') - ) - }) - - it('forwards browser Find with its registered page and workspace owner', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer(), - resolveWorkspaceId: () => 'workspace-1' - }) - - const preventDefault = triggerBeforeInput({ code: 'KeyF', key: 'f' }) - - expect(preventDefault).toHaveBeenCalledOnce() - expect(rendererSendMock).toHaveBeenCalledWith('ui:findInBrowserPage', { - browserPageId: browserTabId, - browserWorkspaceId: 'workspace-1' - }) - }) - - it('forwards browser Find by page alone when no workspace owner is registered', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer() - }) - - const preventDefault = triggerBeforeInput({ code: 'KeyF', key: 'f' }) - - expect(preventDefault).toHaveBeenCalledOnce() - expect(rendererSendMock).toHaveBeenCalledWith('ui:findInBrowserPage', { - browserPageId: browserTabId, - browserWorkspaceId: undefined - }) - }) - - it('forwards quick-command menu shortcuts from focused guest pages', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer(), - getKeybindings: () => ({ - 'tab.openQuickCommandsMenu': ['Mod+Shift+Q'] - }) - }) - - const isMac = process.platform === 'darwin' - const preventDefault = triggerBeforeInput({ - code: 'KeyQ', - key: 'q', - meta: isMac, - control: !isMac, - shift: true - }) - - expect(preventDefault).toHaveBeenCalledTimes(1) - expect(rendererSendMock).toHaveBeenCalledWith('ui:toggleQuickCommandsMenu') - }) - - it('forwards workspace delete shortcuts from focused guest pages', () => { - setupGuestShortcutForwarding({ - browserTabId, - guest: makeGuest(), - resolveRenderer: () => makeRenderer() - }) - - const isMac = process.platform === 'darwin' - const preventDefault = triggerBeforeInput({ - code: 'Backspace', - key: 'Backspace', - meta: isMac, - control: !isMac, - shift: true - }) - - expect(preventDefault).toHaveBeenCalledOnce() - expect(rendererSendMock).toHaveBeenCalledWith('ui:deleteCurrentWorkspace') - }) - // Why: Orca claims these on every repeat but acts once, so a held chord's repeats never reach the page. it.each([ ['workspace delete', { code: 'Backspace', key: 'Backspace' }], diff --git a/src/main/browser/browser-host-client-identity.test.ts b/src/main/browser/browser-host-client-identity.test.ts index f5b395d9b81..7779469708d 100644 --- a/src/main/browser/browser-host-client-identity.test.ts +++ b/src/main/browser/browser-host-client-identity.test.ts @@ -1,4 +1,4 @@ -import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs' +import { mkdtempSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it, vi } from 'vitest' @@ -28,17 +28,6 @@ describe('browser host client identity', () => { ) }) - it('persists the identity where the next launch reads it', () => { - const directory = profileDirectory() - - const identity = readOrCreateBrowserHostClientId(directory) - - expect(JSON.parse(readFileSync(browserHostClientIdentityPath(directory), 'utf-8'))).toEqual({ - version: 1, - browserHostClientId: identity - }) - }) - it.each([ ['unparseable content', 'not json at all'], ['a future store version', JSON.stringify({ version: 2, browserHostClientId: 'kept' })], diff --git a/src/main/browser/browser-host-lease-reconnect-delay.test.ts b/src/main/browser/browser-host-lease-reconnect-delay.test.ts deleted file mode 100644 index e335e6a1514..00000000000 --- a/src/main/browser/browser-host-lease-reconnect-delay.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - nextBrowserHostReconnectDelay, - resolveBrowserHostReconnectDelay -} from './browser-host-lease-reconnect-delay' - -describe('browser host lease reconnect delay', () => { - it('uses deterministic client-specific jitter within exponential and grace bounds', () => { - const delays = Array.from({ length: 8 }, (_, attempt) => - nextBrowserHostReconnectDelay({ - baseDelayMs: 100, - attempt, - remainingMs: 15_000, - browserHostClientId: 'host-a' - }) - ) - - expect(delays).toEqual( - delays.map((_, attempt) => - nextBrowserHostReconnectDelay({ - baseDelayMs: 100, - attempt, - remainingMs: 15_000, - browserHostClientId: 'host-a' - }) - ) - ) - expect(delays[0]).toBeGreaterThanOrEqual(50) - expect(delays[0]).toBeLessThanOrEqual(100) - expect(Math.max(...delays)).toBeLessThanOrEqual(2_000) - expect( - nextBrowserHostReconnectDelay({ - baseDelayMs: 100, - attempt: 8, - remainingMs: 37, - browserHostClientId: 'host-b' - }) - ).toBe(37) - }) - - it('rejects invalid timer delays', () => { - expect(resolveBrowserHostReconnectDelay(undefined)).toBe(100) - expect(() => resolveBrowserHostReconnectDelay(0)).toThrow( - 'Browser host reconnect delay is invalid' - ) - }) -}) diff --git a/src/main/browser/browser-identity-mode-record.test.ts b/src/main/browser/browser-identity-mode-record.test.ts deleted file mode 100644 index f83932e9bd4..00000000000 --- a/src/main/browser/browser-identity-mode-record.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { - BROWSER_IDENTITY_MODE_FILE, - BROWSER_IDENTITY_MODE_VERSION, - readBrowserIdentityModeRecord -} from './browser-identity-mode-record' - -function makeUserData(): string { - return mkdtempSync(join(tmpdir(), 'orca-browser-identity-')) -} - -function writeRecord(userDataPath: string, value: unknown): void { - writeFileSync(join(userDataPath, BROWSER_IDENTITY_MODE_FILE), JSON.stringify(value), 'utf8') -} - -describe('readBrowserIdentityModeRecord', () => { - it('distinguishes missing data as implicit clean', () => { - expect(readBrowserIdentityModeRecord(makeUserData())).toEqual({ - state: 'missing', - appliedMode: 'clean', - configuredMode: 'clean', - explicitSelection: false, - migrationNoticePending: false - }) - }) - - it('returns a valid configured identity', () => { - const userDataPath = makeUserData() - writeRecord(userDataPath, { - version: BROWSER_IDENTITY_MODE_VERSION, - mode: 'native', - explicitSelection: true, - migrationNoticePending: true - }) - - expect(readBrowserIdentityModeRecord(userDataPath)).toEqual({ - state: 'valid', - appliedMode: 'native', - configuredMode: 'native', - explicitSelection: true, - migrationNoticePending: true - }) - }) - - it('falls back to clean without inventing a configured mode for corrupt data', () => { - const userDataPath = makeUserData() - writeFileSync(join(userDataPath, BROWSER_IDENTITY_MODE_FILE), '{not json', 'utf8') - - expect(readBrowserIdentityModeRecord(userDataPath)).toEqual({ - state: 'corrupt', - appliedMode: 'clean', - configuredMode: null, - explicitSelection: null, - migrationNoticePending: null - }) - }) - - it('distinguishes a future record from corrupt data', () => { - const userDataPath = makeUserData() - writeRecord(userDataPath, { - version: BROWSER_IDENTITY_MODE_VERSION + 1, - mode: 'native', - explicitSelection: true, - migrationNoticePending: false - }) - - expect(readBrowserIdentityModeRecord(userDataPath)).toEqual({ - state: 'future', - appliedMode: 'clean', - configuredMode: null, - explicitSelection: null, - migrationNoticePending: null - }) - }) - - it('distinguishes an unreadable record from missing data', () => { - const userDataPath = makeUserData() - mkdirSync(join(userDataPath, BROWSER_IDENTITY_MODE_FILE)) - - expect(readBrowserIdentityModeRecord(userDataPath)).toEqual({ - state: 'unreadable', - appliedMode: 'clean', - configuredMode: null, - explicitSelection: null, - migrationNoticePending: null - }) - }) -}) diff --git a/src/main/browser/browser-link-materialization.electron.test.ts b/src/main/browser/browser-link-materialization.electron.test.ts index 8642118d8e7..d9fb79af358 100644 --- a/src/main/browser/browser-link-materialization.electron.test.ts +++ b/src/main/browser/browser-link-materialization.electron.test.ts @@ -178,41 +178,6 @@ describe('production link bridge, store and mounted guest', () => { expect(receipts.some((receipt) => receipt.path === '/removed-page')).toBe(false) }) - // These assertions capture unsafe baseline behavior pending claim handoff repair. - it.each(['remove-tab', 'profile-switch', 'delete-profile'])( - 'records unsafe stale-source baseline through the real mount: %s', - async (transition) => { - rendererErrors.length = 0 - await page.reload() - await ready() - const path = `/baseline-${transition}` - await open(false, path, true) - await page.evaluate(`window.linkProbe.mutate(${JSON.stringify(transition)})`) - await page.evaluate('window.probeApi.fixture.release()') - await page.evaluate('window.linkProbe.mountTabs()') - await expect - .poll(() => receipts.some((receipt) => receipt.path === path), { timeout: 10_000 }) - .toBe(true) - const state = await page.evaluate('window.linkProbe.snapshot()') - const replacement = state.tabs.find((tab) => tab.id !== state.sourceWorkspaceId) - expect(replacement).toBeDefined() - const expectedProfile = transition === 'delete-profile' ? 'isolated' : 'other' - expect(receipts.filter((receipt) => receipt.path === path)).toEqual([ - { path, cookie: `synthetic_profile=${expectedProfile}` } - ]) - await expect - .poll(() => page.evaluate('window.probeApi.fixture.guests()'), { timeout: 10_000 }) - .toEqual([ - expect.objectContaining({ - url: `${origin}${path}`, - isolated: expectedProfile === 'isolated', - other: expectedProfile === 'other' - }) - ]) - }, - 25_000 - ) - it.todo( 'rejects removed source tabs, profile switches and deleted profiles without a tab, mount, default fallback or destination request' ) diff --git a/src/main/browser/browser-manager-annotation-bridge.test.ts b/src/main/browser/browser-manager-annotation-bridge.test.ts index 2f795da1d2f..a4a7aba8fe9 100644 --- a/src/main/browser/browser-manager-annotation-bridge.test.ts +++ b/src/main/browser/browser-manager-annotation-bridge.test.ts @@ -97,28 +97,6 @@ describe('browserManager.setAnnotationViewportBridge', () => { vi.restoreAllMocks() }) - it('installs the bridge in an isolated world', async () => { - const { guest } = makeGuest(4646) - registerPage('tab-annotations', guest) - - const ok = await browserManager.setAnnotationViewportBridge( - 'tab-annotations', - BRIDGE_OPTIONS, - resolveFromRegistry('tab-annotations') - ) - - expect(ok).toBe(true) - expect(guest.executeJavaScriptInIsolatedWorld).toHaveBeenCalledWith( - expect.any(Number), - [ - expect.objectContaining({ - code: expect.stringContaining('__orcaBrowserAnnotationViewportBridge') - }) - ], - false - ) - }) - // Navigation can replace the registered guest while its old WebContents remains alive. it('injects into the guest the page has when a queued op finally runs, not the one it was asked with', async () => { const { guest: firstGuest } = makeGuest(5101) diff --git a/src/main/browser/browser-manager-auth-user-agent.test.ts b/src/main/browser/browser-manager-auth-user-agent.test.ts index 37fee8641d0..6fe489896ab 100644 --- a/src/main/browser/browser-manager-auth-user-agent.test.ts +++ b/src/main/browser/browser-manager-auth-user-agent.test.ts @@ -390,44 +390,6 @@ describe('browserManager', () => { }) // Direct navigation permits setUserAgent before request dispatch. - it('still uses the WebContents UA write for navigations that are not redirects', () => { - const guest = { - id: 420, - isDestroyed: vi.fn(() => false), - isCurrentlyAudible: vi.fn(() => false), - send: vi.fn(), - getType: vi.fn(() => 'webview'), - setBackgroundThrottling: guestSetBackgroundThrottlingMock, - setWindowOpenHandler: guestSetWindowOpenHandlerMock, - on: guestOnMock, - off: guestOffMock, - openDevTools: guestOpenDevToolsMock, - getURL: vi.fn(() => 'https://example.com/'), - getUserAgent: vi.fn(() => guestBaseUserAgent), - setUserAgent: vi.fn(), - debugger: { isAttached: vi.fn(() => true), sendCommand: vi.fn(async () => undefined) }, - session: { getUserAgent: vi.fn(() => guestBaseUserAgent) } - } - webContentsFromIdMock.mockReturnValue(guest) - - browserManager.attachGuestPolicies(guest as never) - browserManager.registerGuest({ - browserPageId: 'browser-direct-ua', - webContentsId: guest.id, - rendererWebContentsId - }) - const didStartNavigation = guestOnMock.mock.calls.find( - ([event]) => event === 'did-start-navigation' - )?.[1] as (event: unknown, url: string, isInPlace: boolean, isMainFrame: boolean) => void - - didStartNavigation(null, 'https://accounts.google.com/v3/signin/identifier', false, true) - - expect(guest.setUserAgent).toHaveBeenLastCalledWith(googleAuthUserAgent()) - expect(guest.debugger.sendCommand).not.toHaveBeenCalledWith( - 'Emulation.setUserAgentOverride', - expect.anything() - ) - }) // Keep the process UA override until direct navigation can replace the stale Firefox base UA. it('never clears the override onto a stale Firefox WebContents UA and heals it on the next navigation', async () => { diff --git a/src/main/browser/browser-manager-client-hosted-downloads.test.ts b/src/main/browser/browser-manager-client-hosted-downloads.test.ts index 2b13b17588f..8763c0efbe3 100644 --- a/src/main/browser/browser-manager-client-hosted-downloads.test.ts +++ b/src/main/browser/browser-manager-client-hosted-downloads.test.ts @@ -223,34 +223,6 @@ describe('client-hosted downloads', () => { } }) - it('reports the whole lifecycle of a plain client-hosted download to its page renderer', async () => { - const { route, completed } = stubRoute() - registerBrowserClientDownloadRouter('env-a', { route: () => ({ kind: 'remote', route }) }) - const item = createDownloadItem() - - browserManager.handleGuestWillDownload({ guestWebContentsId: GUEST_WEB_CONTENTS_ID, item }) - - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:download-requested', - expect.objectContaining({ browserPageId: BROWSER_PAGE_ID, status: 'downloading' }) - ) - - getDownloadItemEventHandler(item, 'once', 'done')?.({} as Electron.Event, 'completed') - await completed - - expect(rendererSendMock).toHaveBeenCalledWith( - 'browser:download-finished', - expect.objectContaining({ - browserPageId: BROWSER_PAGE_ID, - status: 'completed', - remoteDestination: { - workspaceRelativePath: '.orca/browser-downloads/report.csv', - hostLabel: 'build-box' - } - }) - ) - }) - it('cancels a client-hosted download that no composition owns', () => { registerBrowserClientDownloadRouter('env-a', { route: () => ({ kind: 'unowned' }) }) const item = createDownloadItem() diff --git a/src/main/browser/browser-manager-destroyed-guest.test.ts b/src/main/browser/browser-manager-destroyed-guest.test.ts index faacbb56fc4..c46901ad349 100644 --- a/src/main/browser/browser-manager-destroyed-guest.test.ts +++ b/src/main/browser/browser-manager-destroyed-guest.test.ts @@ -72,14 +72,6 @@ describe('browser guest destruction ownership', () => { expectRetainedCount(0) }) - it('leaves no dead-guest callbacks for the window-close unregisterAll path', () => { - for (let index = 0; index < 1000; index++) { - register(`window-${index}`).destroy() - } - manager.unregisterAll() - expectRetainedCount(0) - }) - it('keeps explicit unregister before destruction idempotent', () => { const guest = register('explicit') manager.unregisterGuest('explicit') diff --git a/src/shared/project-host-setup-projection.test.ts b/src/shared/project-host-setup-projection.test.ts index 23c91ec5a0b..aade8b16611 100644 --- a/src/shared/project-host-setup-projection.test.ts +++ b/src/shared/project-host-setup-projection.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest' import { projectHostSetupProjectionFromRepos, getProjectHostSetupsForProject, - isGitHubBackedRepo, getProjectIdForProviderIdentity, isProjectRemoteIdentityPending } from './project-host-setup-projection' @@ -449,31 +448,6 @@ describe('project host setup projection', () => { ).toHaveLength(3) }) - it('keeps same-named cross-host records separate when there is no shared repo identity', () => { - const projection = projectHostSetupProjectionFromRepos([ - repo({ - id: 'local-sample-app', - path: '/Users/alice/work/sample-app', - displayName: 'sample-app' - }), - repo({ - id: 'ssh-sample-app', - path: '/srv/unrelated/sample-app', - displayName: 'sample-app', - connectionId: 'staging server' - }), - repo({ - id: 'runtime-sample-app', - path: '/workspace/sample-app', - displayName: 'sample-app', - executionHostId: 'runtime:preview' - }) - ]) - - // Display names cannot prove identity; merge only with normalized remote identity or explicit user linking. - expect(projection.projects).toHaveLength(3) - }) - it('does not collapse case-distinct remote paths for self-hosted git remotes', () => { const projection = projectHostSetupProjectionFromRepos([ repo({ @@ -537,47 +511,6 @@ describe('project host setup projection', () => { }) }) -describe('isGitHubBackedRepo', () => { - it('is true when an explicit upstream owner/repo is present', () => { - const target = repo({ - id: 'r', - path: '/r', - displayName: 'r', - upstream: { owner: 'stablyai', repo: 'orca' } - }) - expect(isGitHubBackedRepo(target)).toBe(true) - }) - - it('is true when a GitHub-sourced avatar icon encodes the slug', () => { - const target = repo({ - id: 'r', - path: '/r', - displayName: 'r', - repoIcon: { - type: 'image', - src: 'https://github.com/stablyai.png?size=64', - source: 'github', - label: 'stablyai/orca' - } - }) - expect(isGitHubBackedRepo(target)).toBe(true) - }) - - it('is false for a non-GitHub icon and no upstream (GitLab/folder)', () => { - const target = repo({ - id: 'r', - path: '/r', - displayName: 'r', - repoIcon: { type: 'lucide', name: 'gitlab' } - }) - expect(isGitHubBackedRepo(target)).toBe(false) - }) - - it('is false for a plain local repo with no provider signal', () => { - expect(isGitHubBackedRepo(repo({ id: 'r', path: '/r', displayName: 'r' }))).toBe(false) - }) -}) - describe('getProjectIdForProviderIdentity', () => { it('uses the same normalized identity key as project projection', () => { expect( diff --git a/src/shared/pty-owner-color-query-colors.test.ts b/src/shared/pty-owner-color-query-colors.test.ts index b8689e069cb..32395cf5bb3 100644 --- a/src/shared/pty-owner-color-query-colors.test.ts +++ b/src/shared/pty-owner-color-query-colors.test.ts @@ -1,20 +1,10 @@ import { expect, it } from 'vitest' -import { DEFAULT_TERMINAL_THEME_DARK } from './terminal-theme-selection' -import { TERMINAL_THEME_CATALOG } from './terminal-themes' import { ORCA_DEFAULT_COLOR_QUERY_REPLY_COLORS, normalizeColorQueryReplyColors, resolvePtyOwnerColorQueryColors } from './pty-owner-color-query-colors' -it("falls back to the colours of Orca's default dark terminal theme", () => { - const theme = TERMINAL_THEME_CATALOG[DEFAULT_TERMINAL_THEME_DARK] - expect(ORCA_DEFAULT_COLOR_QUERY_REPLY_COLORS).toEqual({ - foreground: theme?.foreground, - background: theme?.background - }) -}) - it('prefers host colours, then spawn colours, then the default, skipping unusable pairs', () => { const host = { foreground: '#000000', background: '#111111' } const spawn = { foreground: '#222222', background: '#333333' } diff --git a/src/shared/pty-startup-ingress-lifetime-colors.test.ts b/src/shared/pty-startup-ingress-lifetime-colors.test.ts index 5d3fd25ae4d..df7ef8b13ac 100644 --- a/src/shared/pty-startup-ingress-lifetime-colors.test.ts +++ b/src/shared/pty-startup-ingress-lifetime-colors.test.ts @@ -37,16 +37,6 @@ describe('PTY owner colour replies for the terminal life', () => { expect(visible()).toBe('\x1b]11;#123456\x07\x1b]111\x07') }) - it('answers with the viewer colours the host holds now, not the colours sent at spawn', () => { - // The host's viewer may have changed theme since this pane was created. - const spawnedLight = { foreground: '#2e3434', background: '#ffffff' } - const { ingress, writes } = createHarness({ colors: spawnedLight, deadlineMs: 5_000 }) - - ingress.accept(QUERY) - - expect(writes).toEqual([HOST_BACKGROUND_REPLY]) - }) - it('answers a query torn right after its ESC once the Kitty window has closed', () => { const { ingress, writes, visible } = createHarness({ colors: HOST, diff --git a/src/shared/pty-startup-ingress-live-query-reply.test.ts b/src/shared/pty-startup-ingress-live-query-reply.test.ts index 3b02cbd44a6..859b3f6a0fa 100644 --- a/src/shared/pty-startup-ingress-live-query-reply.test.ts +++ b/src/shared/pty-startup-ingress-live-query-reply.test.ts @@ -46,14 +46,6 @@ describe('live query replies', () => { expect(needsCookedEchoSafeQueryReply(mode2031SequenceFor('light'))).toBe(true) }) - it('writes an echo-risk reply in the calling turn', () => { - const { ingress, writes } = harness() - expect(ingress.answerLiveQueryReply(OSC_COLOR_REPLY)).toBe(true) - // No timer advance: the whole point is that there is nothing to wait for. - expect(writes).toEqual([OSC_COLOR_REPLY]) - ingress.drainAndClose() - }) - it('answers repeated identical replies without collapsing them', () => { const { ingress, writes } = harness() expect(ingress.answerLiveQueryReply(COLOR_SCHEME_REPLY)).toBe(true) diff --git a/src/shared/pty-startup-ingress.test.ts b/src/shared/pty-startup-ingress.test.ts index 0403b94d9b9..149845e8132 100644 --- a/src/shared/pty-startup-ingress.test.ts +++ b/src/shared/pty-startup-ingress.test.ts @@ -79,16 +79,6 @@ describe('PtyStartupIngress', () => { } }) - it('suppresses the first echo immediately and keeps a later exact collision', () => { - const { ingress, emissions } = createHarness({ projection: true }) - ingress.accept('\x1b]10;?\x07') - const projected = ']10;rgb:2e2e/3434/3434\\' - ingress.accept(projected) - ingress.accept(projected) - ingress.drainAndClose() - expect(visible(emissions)).toBe(projected) - }) - it('matches each echo across every split without skipping an earlier FIFO candidate', () => { const foregroundEcho = ']10;rgb:2e2e/3434/3434\\' const backgroundEcho = ']11;rgb:ffff/ffff/ffff\\' @@ -751,32 +741,4 @@ describe('PtyStartupIngress', () => { expect(ingress.acceptedRawSequence).toBe(closedAt) expect(visible(emissions)).toBe(']10;rgb:2e2e/') }) - - it('suppresses the readline echo, which no reading of the ECHO bit could predict', () => { - const { ingress, writes, emissions } = createHarness() - ingress.accept('\x1b]10;?\x07') - expect(writes).toEqual([FOREGROUND_REPLY]) - // Live bash PTYs echo via readline even with raw mode/ECHO-clear; kernel ECHO cannot predict software echoes. - ingress.accept(POSIX_COOKED_ECHOES[1]?.(FOREGROUND_REPLY) ?? '') - ingress.drainAndClose() - expect(visible(emissions)).toBe('') - }) - - it('suppresses the kernel caret echo', () => { - const { ingress, writes, emissions } = createHarness() - ingress.accept('\x1b]10;?\x07') - expect(writes).toEqual([FOREGROUND_REPLY]) - // Always arm the default ECHOCTL projection; no probe verdict can safely retire it. - ingress.accept(POSIX_COOKED_ECHOES[0]?.(FOREGROUND_REPLY) ?? '') - ingress.drainAndClose() - expect(visible(emissions)).toBe('') - }) - - it('writes the reply in the accepting turn, with no probe and no wait', () => { - const { ingress, writes } = createHarness() - ingress.accept('\u001b]10;?\u0007') - // No deferral: only output projections remove echo, while delayed writes reorder replies (#15559). - expect(writes).toEqual(['\u001b]10;rgb:2e2e/3434/3434\u001b\\']) - ingress.drainAndClose() - }) }) diff --git a/src/shared/pty-startup-reply-echo-shapes.test.ts b/src/shared/pty-startup-reply-echo-shapes.test.ts index 0109c42105b..002ee430f1f 100644 --- a/src/shared/pty-startup-reply-echo-shapes.test.ts +++ b/src/shared/pty-startup-reply-echo-shapes.test.ts @@ -40,26 +40,6 @@ describe('replyEchoProjections on a POSIX pty', () => { expect(locateEcho(replyEchoProjections(reply, 'posix-pty'), reply).kind).toBe('complete') }) - it('caret-encodes every control, not just ESC', () => { - const [kernel] = replyEchoProjections(OSC11_BEL, 'posix-pty') - expect(kernel?.needle).toBe('^[]11;rgb:2e2e/3434/3434^G') - expect(kernel?.needle).not.toContain('\x07') - }) - - // ECHOCTL leaves TAB/LF/CR literal and maps DEL→^? (vis(3)); pin future grammar behavior even though current replies lack them. - it.each([ - { name: 'TAB stays literal', input: '\t', encoded: '\t' }, - { name: 'LF stays literal', input: '\n', encoded: '\n' }, - { name: 'CR stays literal', input: '\r', encoded: '\r' }, - { name: 'NUL carets to ^@', input: '\x00', encoded: '^@' }, - { name: 'BEL carets to ^G', input: '\x07', encoded: '^G' }, - { name: 'ESC carets to ^[', input: '\x1b', encoded: '^[' }, - { name: 'DEL carets to ^?', input: '\x7f', encoded: '^?' } - ])('$name under ECHOCTL', ({ input, encoded }) => { - const [kernel] = replyEchoProjections(`\x1b[?9${input}n`, 'posix-pty') - expect(kernel?.needle).toBe(`^[[?9${encoded}n`) - }) - // Guard the private-DSR grammar locally: widening a caller must not arm DA1’s shared-prefix BEL needle. it.each(['\x1b[?1;2c', '\x1b[?0u', '\x1b[?2026;2$y', '\x1b[?12;5R'])( 'projects no readline needle for %j, which is not a private DSR', @@ -82,13 +62,6 @@ describe('replyEchoProjections on a POSIX pty', () => { } ) - it('projects readline for a private DSR, which carries no OSC introducer', () => { - const needles = replyEchoProjections(DSR_997, 'posix-pty').map( - (projection) => projection.needle - ) - expect(needles).toContain('\x07997;1n') - }) - // Never hold ESC-led partials: a bare ESC could belong to a torn query and would escape unanswered when the hold expires. it('holds a partial only for needles that do not start with ESC', () => { for (const { reply } of LIVE_ECHOES) { diff --git a/src/shared/published-pane-agent-identity.test.ts b/src/shared/published-pane-agent-identity.test.ts index b002a4b8c14..606c8428a26 100644 --- a/src/shared/published-pane-agent-identity.test.ts +++ b/src/shared/published-pane-agent-identity.test.ts @@ -22,11 +22,6 @@ describe('resolvePublishedPaneAgentIdentity', () => { }) }) - it('keeps the launch record when an unambiguous title names a different agent', () => { - // Unlike agreeing/absent title cases, this detects the title-first mutant: explicit Claude branding cannot override a Codex launch. - expect(resolve({ launchAgent: 'codex', title: '✳ Claude Code' })).toBe('codex') - }) - it('prefers the live foreground process to the launch record', () => { expect(resolve({ launchAgent: 'claude', foregroundAgent: 'codex' })).toBe('codex') }) diff --git a/src/shared/pull-request-generation.test.ts b/src/shared/pull-request-generation.test.ts index 0bb21502bec..8e814007d8c 100644 --- a/src/shared/pull-request-generation.test.ts +++ b/src/shared/pull-request-generation.test.ts @@ -33,14 +33,6 @@ describe('buildPullRequestFieldsPrompt', () => { expect(prompt).toContain('Use conventional PR titles.') }) - it('requires ELI5 problem and solution sections before implementation details', () => { - const prompt = buildPullRequestFieldsPrompt(context, '') - - expect(prompt).toContain('start with `## Problem`, then `## Solution`') - expect(prompt).toContain('simple ELI5 language before details') - expect(prompt).toContain('Reuse equivalent existing sections instead of duplicating them') - }) - it('includes GitHub issue details and complete or partial reference guidance', () => { const prompt = buildPullRequestFieldsPrompt( { @@ -110,19 +102,6 @@ describe('buildPullRequestFieldsPrompt', () => { expect(prompt).toContain('`Fixes AB#99` only for a complete fix') expect(prompt).toContain('use `AB#99`') }) - - it('tells the agent to preserve existing review templates', () => { - const prompt = buildPullRequestFieldsPrompt( - { - ...context, - currentBody: '## Summary\n\n## Testing\n\n- [ ] Required checks' - }, - '' - ) - - expect(prompt).toContain('Retain every heading, required section, and checklist') - expect(prompt).toContain('Leave genuinely unknown template items as TODO or unchecked') - }) }) describe('parseGeneratedPullRequestFields', () => { @@ -140,28 +119,6 @@ describe('parseGeneratedPullRequestFields', () => { }) }) - it('parses CRLF fenced JSON output without full-string fence matching', () => { - const matchSpy = vi.spyOn(String.prototype, 'match') - const replaceSpy = vi.spyOn(String.prototype, 'replace') - const fields = parseGeneratedPullRequestFields( - '```JSON\r\n{"base":"main","title":"fix: add details.","body":"Summary","draft":true}\r\n```', - context - ) - - expect(fields.title).toBe('fix: add details') - const usedFenceMatch = matchSpy.mock.calls.some( - ([pattern]) => - pattern instanceof RegExp && - pattern.source.startsWith('^```') && - pattern.source.includes('[\\s\\S]') - ) - const usedCrlfReplace = replaceSpy.mock.calls.some( - ([pattern]) => pattern instanceof RegExp && pattern.source === '\\r\\n' && pattern.global - ) - expect(usedFenceMatch).toBe(false) - expect(usedCrlfReplace).toBe(false) - }) - it('falls back for missing optional values', () => { const fields = parseGeneratedPullRequestFields('{"title":""}', context) diff --git a/src/shared/quick-open-filter.test.ts b/src/shared/quick-open-filter.test.ts index bba9d8caafe..dc5d4f10a0d 100644 --- a/src/shared/quick-open-filter.test.ts +++ b/src/shared/quick-open-filter.test.ts @@ -4,7 +4,6 @@ import { buildGitLsFilesArgsForQuickOpen, buildHiddenDirExcludeGlobs, buildRgArgsForQuickOpen, - HIDDEN_DIR_BLOCKLIST, normalizeQuickOpenRgLine, shouldExcludeQuickOpenRelPath, shouldIncludeQuickOpenPath @@ -38,14 +37,6 @@ describe('shouldIncludeQuickOpenPath', () => { expect(shouldIncludeQuickOpenPath('nested/.local/share/app/state.db')).toBe(false) expect(shouldIncludeQuickOpenPath('.local/bin/tool')).toBe(true) }) - - it('does NOT blocklist user-authored dirs like .config, .ssh, .github', () => { - expect(HIDDEN_DIR_BLOCKLIST.has('.config')).toBe(false) - expect(HIDDEN_DIR_BLOCKLIST.has('.ssh')).toBe(false) - expect(HIDDEN_DIR_BLOCKLIST.has('.github')).toBe(false) - expect(HIDDEN_DIR_BLOCKLIST.has('.devcontainer')).toBe(false) - expect(HIDDEN_DIR_BLOCKLIST.has('.local')).toBe(false) - }) }) describe('buildExcludePathPrefixes', () => { @@ -91,40 +82,9 @@ describe('buildExcludePathPrefixes', () => { it('strips trailing slashes', () => { expect(buildExcludePathPrefixes('/r', ['/r/a/', '/r/b///'])).toEqual(['a', 'b']) }) - - it('keeps valid child prefixes whose segment starts with dotdot characters', () => { - expect(buildExcludePathPrefixes('/home/u/repo', ['/home/u/repo/..fixtures'])).toEqual([ - '..fixtures' - ]) - }) }) describe('shouldExcludeQuickOpenRelPath', () => { - it('matches the original filter across boundary and Unicode path combinations', () => { - const paths = [ - '', - '/', - 'a', - 'a/', - 'a//', - 'ab', - 'a/b', - 'a\\b', - 'A/b', - '界/😀', - '界/😀x', - 'a[1]/x', - 'a./x' - ] - for (const prefix of paths) { - for (const relPath of paths) { - const expected = - relPath === prefix || (relPath.length > prefix.length && relPath.startsWith(`${prefix}/`)) - expect(shouldExcludeQuickOpenRelPath(relPath, [prefix])).toBe(expected) - } - } - }) - it('preserves normalized Windows and UNC exclusion boundaries', () => { for (const [root, excluded] of [ ['C:\\Repo', 'C:\\Repo\\trees\\one'], @@ -327,14 +287,6 @@ describe('buildGitLsFilesArgsForQuickOpen', () => { expect(ignoredPass).not.toContain(':(glob)**/.env*') }) - it('collapses untracked directories in both passes without generated pathspec churn', () => { - const { primary, ignoredPass } = buildGitLsFilesArgsForQuickOpen() - expect(primary).toContain('--directory') - expect(ignoredPass).toContain('--directory') - expect(ignoredPass).toContain('--no-empty-directory') - expect([...primary, ...ignoredPass]).not.toContain(':(exclude,glob)**/node_modules/**') - }) - it('exclude prefixes prepend positive "." pathspec', () => { const { primary, ignoredPass } = buildGitLsFilesArgsForQuickOpen(['packages/app']) const dashDashIdx = primary.indexOf('--') diff --git a/src/shared/quick-open-transport-budget.test.ts b/src/shared/quick-open-transport-budget.test.ts index 1a2eff1d845..b33e539ca8f 100644 --- a/src/shared/quick-open-transport-budget.test.ts +++ b/src/shared/quick-open-transport-budget.test.ts @@ -1,16 +1,10 @@ import { describe, expect, it } from 'vitest' import { limitQuickOpenFilesBySerializedBytes, - limitQuickOpenSearchReplyBySerializedBytes, - serializedQuickOpenPathBytes + limitQuickOpenSearchReplyBySerializedBytes } from './quick-open-transport-budget' describe('Quick Open transport byte budget', () => { - it('matches JSON.stringify byte accounting, including escaped paths', () => { - const path = 'src/line\nquote".ts' - expect(serializedQuickOpenPathBytes(path)).toBe(Buffer.byteLength(JSON.stringify(path), 'utf8')) - }) - it('returns the largest prefix that fits as a JSON array', () => { const files = ['src/a.ts', 'data/ignored\nfile.bin', 'src/b.ts'] const budget = Buffer.byteLength(JSON.stringify(files.slice(0, 2)), 'utf8') diff --git a/src/shared/raster-image-base64-preview.test.ts b/src/shared/raster-image-base64-preview.test.ts index 9f6f81c40d6..2d8c1991811 100644 --- a/src/shared/raster-image-base64-preview.test.ts +++ b/src/shared/raster-image-base64-preview.test.ts @@ -1,25 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { decodeBase64Prefix, exceedsRasterImagePreviewLimits } from './raster-image-base64-preview' import type * as RasterImageDimensionsModule from './raster-image-dimensions' -import { readRasterImageDimensions } from './raster-image-dimensions' -import { - isKnownRasterImageMimeType, - isRasterImagePreviewDimensions, - RASTER_IMAGE_PREVIEW_HEADER_MAX_BYTES -} from './raster-image-preview-limits' - -/** Frozen full-payload decode/dimension baseline for comparison with the prefix probe. */ -function unprobedExceeds(content: string, mimeType: string | undefined): boolean { - if (!isKnownRasterImageMimeType(mimeType)) { - return false - } - const prefix = referenceDecodeBase64Prefix(content, RASTER_IMAGE_PREVIEW_HEADER_MAX_BYTES) - if (!prefix) { - return false - } - const dimensions = readRasterImageDimensions(prefix) - return dimensions !== null && !isRasterImagePreviewDimensions(dimensions) -} +import { RASTER_IMAGE_PREVIEW_HEADER_MAX_BYTES } from './raster-image-preview-limits' // Observe decoded bytes handed to dimension reading; output equivalence alone cannot detect wasted full-payload decoding. const { dimensionReadLengths } = vi.hoisted(() => ({ dimensionReadLengths: [] as number[] })) @@ -35,123 +17,6 @@ vi.mock('./raster-image-dimensions', async (importOriginal) => { } }) -// ── Reference decoder: the pre-change implementation, verbatim ────────────────────────────────── -const BASE64_PADDING = -2 -const INVALID_BASE64 = -1 - -function base64Value(code: number): number { - if (code >= 65 && code <= 90) { - return code - 65 - } - if (code >= 97 && code <= 122) { - return code - 71 - } - if (code >= 48 && code <= 57) { - return code + 4 - } - if (code === 43) { - return 62 - } - if (code === 47) { - return 63 - } - if (code === 61) { - return BASE64_PADDING - } - return INVALID_BASE64 -} - -function isWhitespace(code: number): boolean { - return code === 9 || code === 10 || code === 12 || code === 13 || code === 32 -} - -function referenceWriteQuartet( - output: Uint8Array, - offset: number, - quartet: readonly number[] -): { bytesWritten: number; padded: boolean } | null { - const [a, b, c, d] = quartet - if (a === undefined || b === undefined || a < 0 || b < 0) { - return null - } - if (c === BASE64_PADDING) { - if (d !== BASE64_PADDING) { - return null - } - if (offset < output.length) { - output[offset] = (a << 2) | (b >> 4) - } - return { bytesWritten: Math.min(1, output.length - offset), padded: true } - } - if (c === undefined || c < 0) { - return null - } - if (offset < output.length) { - output[offset] = (a << 2) | (b >> 4) - } - if (offset + 1 < output.length) { - output[offset + 1] = ((b & 15) << 4) | (c >> 2) - } - if (d === BASE64_PADDING) { - return { bytesWritten: Math.min(2, output.length - offset), padded: true } - } - if (d === undefined || d < 0) { - return null - } - if (offset + 2 < output.length) { - output[offset + 2] = ((c & 3) << 6) | d - } - return { bytesWritten: Math.min(3, output.length - offset), padded: false } -} - -function referenceDecodeBase64Prefix(content: string, maxBytes: number): Uint8Array | null { - const capacity = Math.min(maxBytes, Math.ceil(content.length / 4) * 3) - const output = new Uint8Array(capacity) - const quartet: number[] = [] - let outputLength = 0 - let padded = false - - for (let index = 0; index < content.length && outputLength < capacity; index += 1) { - const code = content.charCodeAt(index) - if (isWhitespace(code)) { - continue - } - if (padded) { - return null - } - const value = base64Value(code) - if (value === INVALID_BASE64) { - return null - } - quartet.push(value) - if (quartet.length !== 4) { - continue - } - const decoded = referenceWriteQuartet(output, outputLength, quartet) - if (!decoded) { - return null - } - outputLength += decoded.bytesWritten - padded = decoded.padded - quartet.length = 0 - } - - if (!padded && outputLength < capacity && quartet.length > 0) { - if (quartet.length === 1 || quartet.includes(BASE64_PADDING)) { - return null - } - while (quartet.length < 4) { - quartet.push(BASE64_PADDING) - } - const decoded = referenceWriteQuartet(output, outputLength, quartet) - if (!decoded) { - return null - } - outputLength += decoded.bytesWritten - } - return output.subarray(0, outputLength) -} - function pngBytes(totalBytes: number, width: number, height: number): Buffer { const bytes = Buffer.alloc(Math.max(totalBytes, 24)) Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]).copy(bytes) @@ -191,89 +56,7 @@ function jpegBytes( return Buffer.concat(parts) } -function gifBytes(width: number, height: number): Buffer { - const gif = Buffer.alloc(64) - gif.write('GIF89a', 0, 'ascii') - gif.writeUInt16LE(width, 6) - gif.writeUInt16LE(height, 8) - return gif -} - -function webpBytes(width: number, height: number): Buffer { - const webp = Buffer.alloc(64) - webp.write('RIFF', 0, 'ascii') - webp.writeUInt32LE(50, 4) - webp.write('WEBP', 8, 'ascii') - webp.write('VP8X', 12, 'ascii') - webp.writeUInt32LE(10, 16) - webp.writeUIntLE(width - 1, 24, 3) - webp.writeUIntLE(height - 1, 27, 3) - return webp -} - -const DECODE_FIXTURES: { label: string; content: string }[] = [ - { label: 'png', content: pngBytes(24, 512, 512).toString('base64') }, - { label: 'png padded once', content: pngBytes(26, 512, 512).toString('base64') }, - { label: 'png padded twice', content: pngBytes(25, 512, 512).toString('base64') }, - { label: 'png 70 KiB', content: pngBytes(70_000, 512, 512).toString('base64') }, - { label: 'jpeg', content: jpegBytes(0, 640, 480).toString('base64') }, - { label: 'jpeg 70 KiB exif', content: jpegBytes(70_000, 4000, 3000).toString('base64') }, - { label: 'gif', content: gifBytes(320, 240).toString('base64') }, - { label: 'webp', content: webpBytes(800, 600).toString('base64') }, - { label: 'empty', content: '' }, - { label: 'one character', content: 'A' }, - { label: 'two characters', content: 'AB' }, - { label: 'three characters', content: 'ABC' }, - { label: 'invalid character', content: 'AB*D' }, - { label: 'invalid tail', content: `${pngBytes(24, 4, 4).toString('base64')}!!!` }, - { label: 'padding mid-payload', content: 'AAAA=AAA' }, - { label: 'lone padding in tail', content: 'AAAAAB=' }, - { label: 'single padding', content: 'AAAAAA==' }, - { label: 'double padding', content: 'AAAAAAA=' }, - { label: 'stray padding after padding', content: 'AAAA====' }, - { - label: 'line-wrapped png', - content: pngBytes(70_000, 512, 512) - .toString('base64') - .replace(/(.{76})/g, '$1\r\n') - }, - { label: 'leading and trailing whitespace', content: `\n\t ${'AAAA'} \r\n` }, - { label: 'truncated png header', content: pngBytes(24, 512, 512).toString('base64').slice(0, 18) } -] - -const DECODE_CAPS = [ - 0, - 1, - 2, - 3, - 4, - 23, - 24, - 25, - 63, - 64, - 65, - 1024, - RASTER_IMAGE_PREVIEW_HEADER_MAX_BYTES -] - describe('decodeBase64Prefix', () => { - it('decodes byte-for-byte identically to the reference implementation', () => { - for (const { label, content } of DECODE_FIXTURES) { - for (const maxBytes of DECODE_CAPS) { - const expected = referenceDecodeBase64Prefix(content, maxBytes) - const actual = decodeBase64Prefix(content, maxBytes) - const detail = `${label} @ maxBytes=${maxBytes}` - if (expected === null) { - expect(actual, detail).toBeNull() - continue - } - expect(actual, detail).not.toBeNull() - expect(Array.from(actual!), detail).toEqual(Array.from(expected)) - } - } - }) - it('stops at the byte cap instead of decoding the whole payload', () => { const content = pngBytes(70_000, 512, 512).toString('base64') expect(decodeBase64Prefix(content, 32)?.byteLength).toBe(32) @@ -311,30 +94,4 @@ describe('exceedsRasterImagePreviewLimits', () => { const content = `${pngBytes(70_000, 32_769, 1).toString('base64')}!!!` expect(exceedsRasterImagePreviewLimits(content, 'image/png')).toBe(false) }) - - it('agrees with the unprobed implementation on every fixture and mime type', () => { - const mimeTypes = [ - 'image/png', - 'image/jpeg', - 'image/gif', - 'image/webp', - 'image/svg+xml', - undefined - ] - const fixtures = [ - ...DECODE_FIXTURES, - { label: 'over-limit png', content: pngBytes(24, 32_769, 1).toString('base64') }, - { label: 'over-limit pixels png', content: pngBytes(24, 8192, 8192).toString('base64') }, - { label: 'over-limit gif', content: gifBytes(65_535, 65_535).toString('base64') }, - { label: 'over-limit jpeg', content: jpegBytes(70_000, 40_000, 40_000).toString('base64') }, - { label: 'over-limit webp', content: webpBytes(40_000, 40_000).toString('base64') } - ] - for (const { label, content } of fixtures) { - for (const mimeType of mimeTypes) { - expect(exceedsRasterImagePreviewLimits(content, mimeType), `${label} / ${mimeType}`).toBe( - unprobedExceeds(content, mimeType) - ) - } - } - }) }) diff --git a/src/shared/react-update-depth-attribution.test.ts b/src/shared/react-update-depth-attribution.test.ts index e6e15a85b8b..20d4b5e1fd7 100644 --- a/src/shared/react-update-depth-attribution.test.ts +++ b/src/shared/react-update-depth-attribution.test.ts @@ -15,16 +15,6 @@ describe('react update depth attribution', () => { ).toBe(UNRELIABLE_BOUNDARY_ATTRIBUTION) }) - it('matches the legacy error-decoder invariant form', () => { - expect( - getReactErrorBoundaryAttribution( - new Error( - 'Minified React error #185; visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message.' - ) - ) - ).toBe(UNRELIABLE_BOUNDARY_ATTRIBUTION) - }) - it('matches the development message', () => { expect( getReactErrorBoundaryAttribution(new Error('Maximum update depth exceeded. This can happen…')) diff --git a/src/shared/release-channel.test.ts b/src/shared/release-channel.test.ts index 9ee5a3f8648..cfffd178168 100644 --- a/src/shared/release-channel.test.ts +++ b/src/shared/release-channel.test.ts @@ -2,15 +2,12 @@ import { describe, expect, it } from 'vitest' import { findInstallerAssetName, getReleaseNotesUrlForVersion, - getReleaseRepoForChannel, getVersionChannel, - hasDedicatedReleaseRepo, hasInstallableArtifactForPlatform, isAdhocVersion, isChannelSupportedOnPlatform, isDailyVersion, isHourlyVersion, - isReleaseChannel, parseAdhocVersionStamp, parseDailyVersionStamp, parseDevBuildStamp, @@ -33,24 +30,6 @@ describe('release channel', () => { expect(getVersionChannel('not-a-version')).toBeNull() }) - // The main release Atom feed holds only 10 entries; daily/hourly tags would evict stable/RC updates. - it('keeps dev builds out of the main release repo, and apart from each other', () => { - expect(getReleaseRepoForChannel('hourly')).toBe('stablyai/orca-hourly') - expect(getReleaseRepoForChannel('daily')).toBe('stablyai/orca-daily') - // Keep unlanded adhoc branch builds away from users following main’s hourly channel. - expect(getReleaseRepoForChannel('adhoc')).toBe('stablyai/orca-adhoc') - expect(getReleaseRepoForChannel('stable')).toBe('stablyai/orca') - expect(getReleaseRepoForChannel('rc')).toBe('stablyai/orca') - }) - - it('marks exactly the dev channels as having their own repo', () => { - expect(hasDedicatedReleaseRepo('hourly')).toBe(true) - expect(hasDedicatedReleaseRepo('daily')).toBe(true) - expect(hasDedicatedReleaseRepo('adhoc')).toBe(true) - expect(hasDedicatedReleaseRepo('stable')).toBe(false) - expect(hasDedicatedReleaseRepo('rc')).toBe(false) - }) - // Dev tags exist only in their dedicated repos; main-repo links 404. it('builds release-notes links against the repo that published the version', () => { expect(getReleaseNotesUrlForVersion('1.4.160-hourly.202607281400')).toBe( @@ -221,16 +200,6 @@ describe('release channel', () => { } }) - it('accepts only known channels', () => { - expect(isReleaseChannel('hourly')).toBe(true) - expect(isReleaseChannel('daily')).toBe(true) - expect(isReleaseChannel('adhoc')).toBe(true) - expect(isReleaseChannel('stable')).toBe(true) - expect(isReleaseChannel('nightly')).toBe(false) - expect(isReleaseChannel(null)).toBe(false) - expect(isReleaseChannel(undefined)).toBe(false) - }) - // Timestamp tails distinguish consecutive hourlies; keep picker ordering chronological. it('sorts consecutive hourly builds newest first', () => { const build = (version: string): ReleaseBuild => ({ diff --git a/src/shared/remote-runtime-client.test.ts b/src/shared/remote-runtime-client.test.ts index dc62b95eec8..76ce176ce19 100644 --- a/src/shared/remote-runtime-client.test.ts +++ b/src/shared/remote-runtime-client.test.ts @@ -18,12 +18,7 @@ import { import { sendRemoteRuntimeRequest, subscribeRemoteRuntimeRequest } from './remote-runtime-client' import { remoteRuntimeClientCapabilities } from './remote-runtime-client-capabilities' import { MAX_TIMER_DELAY_MS } from './timer-delay' -import { - BROWSER_CLIENT_HOST_RUNTIME_CAPABILITY, - BROWSER_CLIENT_PAGE_METADATA_RUNTIME_CAPABILITY, - BROWSER_NETWORK_TUNNEL_RUNTIME_CAPABILITY -} from './protocol-version' -import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from './electron-remote-runtime-client-capabilities' +import { BROWSER_NETWORK_TUNNEL_RUNTIME_CAPABILITY } from './protocol-version' const servers: WebSocketServer[] = [] @@ -189,39 +184,6 @@ describe('subscribeRemoteRuntimeRequest', () => { await vi.waitFor(() => expect(releaseSocket).toHaveBeenCalledOnce()) }) - it('detaches subscription socket listeners after close', async () => { - const offSpy = vi.spyOn(WebSocketClient.prototype, 'off') - try { - const server = await createSubscriptionServer() - const onResponse = vi.fn() - const onError = vi.fn() - const onClose = vi.fn() - - const subscription = await subscribeRemoteRuntimeRequest( - server.pairing, - 'terminal.subscribe', - { terminal: 't1' }, - 1000, - { - onResponse, - onError, - onClose - } - ) - - await vi.waitFor(() => expect(onResponse).toHaveBeenCalled()) - subscription.close() - await vi.waitFor(() => expect(onClose).toHaveBeenCalledOnce()) - - const removedEvents = offSpy.mock.calls.map(([event]) => event) - expect(removedEvents).toEqual(expect.arrayContaining(['open', 'error', 'close', 'message'])) - expect(subscription.sendBinary(new Uint8Array([9]))).toBe(false) - expect(onError).not.toHaveBeenCalled() - } finally { - offSpy.mockRestore() - } - }) - it('closes a half-open subscription socket via client liveness so callers can resubscribe', async () => { // Half-open dedicated streams get no close/pongs/data; client liveness must trigger onError/onClose so renderer resubscription runs (#7718). const server = await createSubscriptionServer({ disableAutoPong: true }) @@ -288,52 +250,6 @@ describe('subscribeRemoteRuntimeRequest', () => { }) describe('sendRemoteRuntimeRequest', () => { - it('keeps generic native authentication free of Electron placement support', async () => { - let receivedAuth: Record | null = null - const server = await createOneShotServer({ - onAuth: (auth) => { - receivedAuth = auth - } - }) - - await sendRemoteRuntimeRequest( - server.pairing, - 'session.tabs.list', - { worktree: 'id:worktree-a' }, - 1000 - ) - - expect(receivedAuth).toMatchObject({ - clientCapabilities: remoteRuntimeClientCapabilities() - }) - }) - - it('advertises browser placement support only when the Electron caller opts in', async () => { - let receivedAuth: Record | null = null - const server = await createOneShotServer({ - onAuth: (auth) => { - receivedAuth = auth - } - }) - - await sendRemoteRuntimeRequest( - server.pairing, - 'session.tabs.list', - { worktree: 'id:worktree-a' }, - 1000, - undefined, - undefined, - ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES - ) - - expect(receivedAuth).toMatchObject({ - clientCapabilities: expect.arrayContaining([ - BROWSER_CLIENT_HOST_RUNTIME_CAPABILITY, - BROWSER_CLIENT_PAGE_METADATA_RUNTIME_CAPABILITY - ]) - }) - }) - it.each([-1, 1.5, MAX_TIMER_DELAY_MS + 1, Number.MAX_SAFE_INTEGER + 1])( 'rejects invalid timer delay %s before reading pairing data', async (timeoutMs) => { @@ -565,25 +481,6 @@ describe('sendRemoteRuntimeRequest', () => { expect(receivedRequest).not.toHaveProperty('orchestrationContractVersion') expect(receivedRequest).not.toHaveProperty('orchestrationRequestId') }) - - it('detaches one-shot socket listeners after a successful response', async () => { - const offSpy = vi.spyOn(WebSocketClient.prototype, 'off') - try { - const server = await createOneShotServer() - - await sendRemoteRuntimeRequest<{ satisfied: boolean }>( - server.pairing, - 'terminal.wait', - { terminal: 't1', for: 'tui-idle', timeoutMs: 550 }, - 300 - ) - - const removedEvents = offSpy.mock.calls.map(([event]) => event) - expect(removedEvents).toEqual(expect.arrayContaining(['open', 'error', 'close', 'message'])) - } finally { - offSpy.mockRestore() - } - }) }) async function createSubscriptionServer( diff --git a/src/shared/remote-runtime-socket-resume-probe.test.ts b/src/shared/remote-runtime-socket-resume-probe.test.ts index 962e71d8e4c..7b8e80c2afc 100644 --- a/src/shared/remote-runtime-socket-resume-probe.test.ts +++ b/src/shared/remote-runtime-socket-resume-probe.test.ts @@ -3,7 +3,6 @@ import { probeAllRemoteRuntimeSocketsNow, startRemoteRuntimeSocketLiveness } from './remote-runtime-socket-liveness' -import { SharedControlReconnectScheduler } from './remote-runtime/control/remote-runtime-shared-control-reconnect' // After sleep, probe every socket immediately; 8s misses only re-ping and the normal 25s keepalive decides death so relayed paths have time to recover (#9092). describe('remote runtime resume probe', () => { @@ -154,45 +153,3 @@ describe('remote runtime resume probe', () => { expect(onDead).toHaveBeenCalledTimes(1) }) }) - -describe('SharedControlReconnectScheduler.retryNow after an outage', () => { - beforeEach(() => { - vi.useFakeTimers() - }) - afterEach(() => { - vi.useRealTimers() - }) - - it('restarts the backoff ladder so a failure right after wake retries quickly', () => { - const scheduler = new SharedControlReconnectScheduler() - const open = vi.fn() - for (let attempt = 0; attempt < 6; attempt += 1) { - scheduler.scheduleWithIdleBackoff(false, open) - vi.runOnlyPendingTimers() - } - expect(scheduler.attemptCount).toBeGreaterThanOrEqual(6) - - scheduler.scheduleWithIdleBackoff(false, open) - expect(scheduler.retryNow()).toBe(true) - expect(scheduler.attemptCount).toBe(0) - }) - - it('restarts the ladder even when the wake lands while a reconnect is already opening', () => { - const scheduler = new SharedControlReconnectScheduler() - const open = vi.fn() - for (let attempt = 0; attempt < 6; attempt += 1) { - scheduler.scheduleWithIdleBackoff(false, open) - vi.runOnlyPendingTimers() - } - // The last open() is in flight, so no timer is pending for retryNow to advance. - expect(scheduler.isScheduled).toBe(false) - expect(scheduler.retryNow()).toBe(false) - expect(scheduler.attemptCount).toBe(0) - }) - - it('does nothing when no reconnect is pending', () => { - const scheduler = new SharedControlReconnectScheduler() - expect(scheduler.retryNow()).toBe(false) - expect(scheduler.attemptCount).toBe(0) - }) -}) diff --git a/src/shared/remote-runtime-transport-error-agreement.test.ts b/src/shared/remote-runtime-transport-error-agreement.test.ts index 5a299d9db0c..541b103e107 100644 --- a/src/shared/remote-runtime-transport-error-agreement.test.ts +++ b/src/shared/remote-runtime-transport-error-agreement.test.ts @@ -5,7 +5,6 @@ import { Buffer } from 'node:buffer' import { describe, expect, it } from 'vitest' import { RemoteRuntimeClientError } from './remote-runtime-client-error' import { - RECOVERABLE_CODES, RECOVERABLE_MESSAGE_FRAGMENTS, isRecoverableRemoteRuntimeConnectionError, isRuntimeRpcQueueOverloadError, @@ -411,12 +410,6 @@ function classifyByMessageOnly(pair: TransportErrorPair): boolean { } describe('transport error code/message classification agreement', () => { - it('enumerates every reachable coded producer', () => { - // Use a floor so corpus additions pass; lower deliberately only for genuinely removed producers (#12667 originally enumerated 34). - expect(CODED_TRANSPORT_ERRORS.length).toBeGreaterThanOrEqual(59) - expect(CODED_TRANSPORT_ERRORS.every((pair) => typeof pair.code === 'string')).toBe(true) - }) - it('never classifies a coded error as fatal while its own message reads as recoverable', () => { const violations = CODED_TRANSPORT_ERRORS.filter( (pair) => !isRecoverableRemoteRuntimeConnectionError(pair) && classifyByMessageOnly(pair) @@ -450,33 +443,6 @@ describe('transport error code/message classification agreement', () => { expect(misclassified).toEqual([]) }) - it('backs every recoverable message fragment with a producer in the corpus', () => { - const allMessages = [...CODED_TRANSPORT_ERRORS, ...CODELESS_TRANSPORT_ERRORS].map((pair) => - pair.message.toLowerCase() - ) - const unbacked = RECOVERABLE_MESSAGE_FRAGMENTS.filter( - (fragment) => !allMessages.some((message) => message.includes(fragment)) - ).map( - (fragment) => - `recoverable fragment "${fragment}" matches no producer in this corpus — add the producer that emits it, or drop the fragment.` - ) - expect(unbacked).toEqual([]) - }) - - it('backs every recoverable code with a producer in the corpus', () => { - // 'reconnecting' is vocabulary borrowed from the SSH/runtime status states; no error - // producer raises it. Kept recoverable defensively. - const codesWithoutProducer = new Set(['reconnecting']) - const corpusCodes = new Set(CODED_TRANSPORT_ERRORS.map((pair) => pair.code)) - const unbacked = [...RECOVERABLE_CODES] - .filter((code) => !corpusCodes.has(code) && !codesWithoutProducer.has(code)) - .map( - (code) => - `recoverable code "${code}" matches no producer in this corpus — add the producer that raises it so its message is checked, or declare it producer-less here.` - ) - expect(unbacked).toEqual([]) - }) - it('classifies remote_runtime_busy recoverable with or without its code (STA-3479)', () => { for (const pair of BUSY_TRANSPORT_ERRORS) { expect(isRecoverableRemoteRuntimeConnectionError(pair)).toBe(true) diff --git a/src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts b/src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts index be93d0fc32a..6308582037f 100644 --- a/src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts +++ b/src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts @@ -11,7 +11,6 @@ import { import { getRemoteRuntimeRequestAdmissionEvidence } from '../requests/remote-runtime-prepared-request-admission' import { remoteRuntimeClientCapabilities } from '../../remote-runtime-client-capabilities' import { RemoteRuntimeSharedControlConnection } from './remote-runtime-shared-control-connection' -import * as sharedControlProtocol from './remote-runtime-shared-control-protocol' import { isRuntimeSubscriptionReplayResponse } from '../../runtime-subscription-replay' import { closeSharedControlTestServers, @@ -81,10 +80,6 @@ describe('RemoteRuntimeSharedControlConnection', () => { connection.close() }) - it('does not expose a binary sender on the shared control protocol surface', () => { - expect('sendSharedControlEncryptedBinary' in sharedControlProtocol).toBe(false) - }) - it('releases a pending request when the socket send throws', async () => { const connection = new RemoteRuntimeSharedControlConnection( pairedRuntimeConnectionFromOffer({ @@ -122,36 +117,6 @@ describe('RemoteRuntimeSharedControlConnection', () => { connection.close() }) - it('replaces a stuck pre-ready socket when a one-shot probe proves reachability', () => { - const connection = new RemoteRuntimeSharedControlConnection( - pairedRuntimeConnectionFromOffer({ - v: 2, - endpoint: 'ws://127.0.0.1:1', - deviceToken: 'token', - publicKeyB64: Buffer.from(new Uint8Array(32).fill(1)).toString('base64') - }) - ) - const close = vi.fn() - const cleanup = vi.fn() - const open = vi.fn() - const unsafe = connection as unknown as { - state: string - ws: { readyState: number; close: () => void } | null - socketCleanup: (() => void) | null - open: () => void - } - unsafe.state = 'awaiting_ready' - unsafe.ws = { readyState: 0, close } - unsafe.socketCleanup = cleanup - unsafe.open = open - - connection.reconnectNow() - - expect(cleanup).toHaveBeenCalledOnce() - expect(close).toHaveBeenCalledOnce() - expect(open).toHaveBeenCalledOnce() - }) - it('keeps a waiting request alive when a reachability probe replaces its pre-ready socket', async () => { const server = await createServer({ suppressReadyFrameCount: 1 }) const connection = new RemoteRuntimeSharedControlConnection(server.pairing) @@ -257,29 +222,6 @@ describe('RemoteRuntimeSharedControlConnection', () => { connection.close() }) - it('keeps many logical subscriptions on one authenticated WebSocket', async () => { - const server = await createServer() - const connection = new RemoteRuntimeSharedControlConnection(server.pairing) - const subscriptions = await Promise.all( - Array.from({ length: 35 }, (_value, index) => - connection.subscribe('runtime.clientEvents.subscribe', { index }, 1000, { - onResponse: vi.fn(), - onError: vi.fn() - }) - ) - ) - - await vi.waitFor(() => expect(server.requests).toHaveLength(35)) - - expect(server.connectionCount()).toBe(1) - expect( - server.requests.every((request) => request.method === 'runtime.clientEvents.subscribe') - ).toBe(true) - - subscriptions.forEach((subscription) => subscription.close()) - connection.close() - }) - it('reconnects and replays passive subscriptions without closing them', async () => { const server = await createServer({ closeAfterFirstStreamingResponse: true }) const connection = new RemoteRuntimeSharedControlConnection(server.pairing) @@ -310,74 +252,6 @@ describe('RemoteRuntimeSharedControlConnection', () => { connection.close() }) - it('keeps passive subscriptions alive after reaching the capped reconnect delay', async () => { - const server = await createServer() - const connection = new RemoteRuntimeSharedControlConnection(server.pairing) - const onClose = vi.fn() - - const unsafe = connection as unknown as { - reconnect: { - attempt: number - scheduleWithDefaultBackoff: (intentionallyClosed: boolean, open: () => void) => void - } - subscriptions: Map - } - unsafe.reconnect.attempt = 7 - unsafe.subscriptions.set('sub-1', { - requestId: 'sub-1', - method: 'runtime.clientEvents.subscribe', - params: null, - callbacks: { onResponse: vi.fn(), onError: vi.fn(), onClose }, - sent: false, - closed: false, - closeAfterReady: false, - remoteSubscriptionId: null - }) - - unsafe.reconnect.scheduleWithDefaultBackoff(false, () => {}) - - expect(onClose).not.toHaveBeenCalled() - expect(connection.getDiagnostics()).toMatchObject({ - state: 'reconnecting', - reconnectAttempt: 8, - subscriptionCount: 1 - }) - - connection.close() - }) - - it('resets reconnect attempts after a stable authenticated ready period', async () => { - const server = await createServer() - const connection = new RemoteRuntimeSharedControlConnection(server.pairing, { - reconnectStableResetMs: 50 - }) - - await expect(connection.request('worktree.ps', undefined, 1000)).resolves.toMatchObject({ - ok: true - }) - ;(connection as unknown as { reconnect: { attempt: number } }).reconnect.attempt = 3 - - await vi.waitFor(() => - expect(connection.getDiagnostics()).toMatchObject({ reconnectAttempt: 0 }) - ) - connection.close() - }) - - it('removes ready waiters when a one-shot request times out during handshake', async () => { - const server = await createServer({ suppressReadyFrame: true }) - const connection = new RemoteRuntimeSharedControlConnection(server.pairing) - const unsafe = connection as unknown as { - readyWaiters: unknown[] - pendingRequests: Map - } - - await expect(connection.request('worktree.ps', undefined, 25)).rejects.toThrow('Timed out') - - await vi.waitFor(() => expect(unsafe.readyWaiters).toHaveLength(0)) - expect(unsafe.pendingRequests.size).toBe(0) - connection.close() - }) - it('cleans up an id-scoped subscription closed before its ready response', async () => { const server = await createServer({ delaySubscriptionReady: true }) const connection = new RemoteRuntimeSharedControlConnection(server.pairing) diff --git a/src/shared/remote-runtime/control/remote-runtime-shared-control-keepalive-refresh.test.ts b/src/shared/remote-runtime/control/remote-runtime-shared-control-keepalive-refresh.test.ts index 486ec011d70..e13be351532 100644 --- a/src/shared/remote-runtime/control/remote-runtime-shared-control-keepalive-refresh.test.ts +++ b/src/shared/remote-runtime/control/remote-runtime-shared-control-keepalive-refresh.test.ts @@ -74,15 +74,6 @@ describe('shared control keepalive timeout refresh semantics', () => { await expect(promise).resolves.toMatchObject({ ok: true }) }) - it('fires the deadline for a short RPC when no keepalives arrive', async () => { - const { pendingRequests, promise } = startRequest() - - await vi.advanceTimersByTimeAsync(1001) - - await expect(promise).rejects.toThrow() - expect(pendingRequests.size).toBe(0) - }) - it('aborts one pending request without disturbing its peer', async () => { const pendingRequests = new Map>() const controller = new AbortController() diff --git a/src/shared/remote-runtime/control/remote-runtime-shared-control-standing-intent.test.ts b/src/shared/remote-runtime/control/remote-runtime-shared-control-standing-intent.test.ts index fdbbebb81c0..abe52973574 100644 --- a/src/shared/remote-runtime/control/remote-runtime-shared-control-standing-intent.test.ts +++ b/src/shared/remote-runtime/control/remote-runtime-shared-control-standing-intent.test.ts @@ -92,31 +92,6 @@ describe('shared-control standing intent', () => { scheduler.clear() }) - it.each([ - { manual: false, paused: false, subscriptions: 0, scheduled: true }, - { manual: false, paused: false, subscriptions: 1, scheduled: true }, - { manual: false, paused: true, subscriptions: 0, scheduled: false }, - { manual: false, paused: true, subscriptions: 1, scheduled: true }, - { manual: true, paused: false, subscriptions: 0, scheduled: false }, - { manual: true, paused: false, subscriptions: 1, scheduled: false }, - { manual: true, paused: true, subscriptions: 0, scheduled: false }, - { manual: true, paused: true, subscriptions: 1, scheduled: false } - ])( - 'applies the intent/capability truth table: $manual/$paused/$subscriptions', - ({ manual, paused, subscriptions, scheduled }) => { - vi.useFakeTimers() - const connection = testConnection({ manual, paused }) - if (subscriptions > 0) { - addLogicalSubscription(connection) - } - - closeCurrentGeneration(connection) - - expect(connection.getDiagnostics().state === 'reconnecting').toBe(scheduled) - connection.close() - } - ) - it('advances an armed idle retry without recreating cleared work', () => { vi.useFakeTimers() const connection = testConnection() diff --git a/src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts b/src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts index 035647d5ad1..8ef0e5a1638 100644 --- a/src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts +++ b/src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts @@ -1,7 +1,5 @@ import { describe, expect, it } from 'vitest' import { - RECOVERABLE_CODES, - RECOVERABLE_MESSAGE_FRAGMENTS, isRecoverableRemoteRuntimeConnectionError, isRuntimeRpcQueueOverloadError, toRemoteRuntimeClientErrorLike @@ -64,33 +62,6 @@ describe('remote runtime client error classification', () => { expect(isRecoverableRemoteRuntimeConnectionError(error)).toBe(true) }) - it('pins the recoverable codes and message fragments', () => { - expect([...RECOVERABLE_CODES].toSorted()).toEqual([ - 'reconnecting', - 'remote_runtime_busy', - 'remote_runtime_unavailable', - 'runtime_rpc_queue_overloaded', - 'runtime_timeout', - 'runtime_unavailable', - 'timeout' - ]) - expect(RECOVERABLE_MESSAGE_FRAGMENTS).toEqual([ - 'could not connect to the remote orca runtime', - 'remote orca runtime closed the connection', - 'remote orca runtime connection closed', - 'remote orca runtime is not connected', - 'remote runtime call queue is full', - 'remote runtime connection closed', - 'remote runtime connection wait limit reached', - 'remote runtime request limit reached', - 'remote runtime subscription limit reached', - 'remote runtime subscription memory limit reached', - 'remote runtime subscription closed before it started', - 'remote terminal stream is not connected', - 'timed out waiting for the remote orca runtime' - ]) - }) - it.each([ 'runtime_manually_disconnected', 'ssh_user_disconnected', @@ -99,17 +70,4 @@ describe('remote runtime client error classification', () => { ])('does not treat client code %s as recoverable', (code) => { expect(isRecoverableRemoteRuntimeConnectionError({ code, message: 'refused' })).toBe(false) }) - - it.each(RECOVERABLE_MESSAGE_FRAGMENTS)( - 'recovers a code-stripped message containing %s', - (fragment) => { - expect( - isRecoverableRemoteRuntimeConnectionError( - toRemoteRuntimeClientErrorLike( - new Error(`Error invoking remote method 'x': Error: ${fragment}.`) - ) - ) - ).toBe(true) - } - ) }) diff --git a/src/shared/renderer-restart-preparation.test.ts b/src/shared/renderer-restart-preparation.test.ts index e5babe85707..48555a1c0c0 100644 --- a/src/shared/renderer-restart-preparation.test.ts +++ b/src/shared/renderer-restart-preparation.test.ts @@ -136,27 +136,6 @@ describe('createUpdaterQuitAbortRelay', () => { expect(aborted).toHaveBeenCalledTimes(1) }) - it('resets a prepared restart on a linux package-install recovery status', () => { - const eventTarget = new EventTarget() - const aborted = vi.fn() - eventTarget.addEventListener('update-restart-aborted', aborted) - const relay = createUpdaterQuitAbortRelay(eventTarget, 'update-restart-aborted') - relay.markPrepared() - - relay.handleStatus({ - state: 'error', - message: 'No authentication agent found.', - recovery: { - kind: 'linux-package-install', - packageType: 'deb', - reason: 'authentication-agent-unavailable', - version: '1.0.61' - } - } satisfies UpdateStatus) - - expect(aborted).toHaveBeenCalledTimes(1) - }) - it('ignores updater errors when no update restart was prepared', () => { const eventTarget = new EventTarget() const aborted = vi.fn() diff --git a/src/shared/repo-display-labels.test.ts b/src/shared/repo-display-labels.test.ts index cc9ddd18951..20d24f17dc5 100644 --- a/src/shared/repo-display-labels.test.ts +++ b/src/shared/repo-display-labels.test.ts @@ -2,18 +2,6 @@ import { describe, expect, it } from 'vitest' import { getRepoDisplayLabelKey, getRepoDisplayLabelsByPath } from './repo-display-labels' describe('getRepoDisplayLabelsByPath', () => { - it('keeps non-colliding repository names basename-only', () => { - const labels = getRepoDisplayLabelsByPath([ - { path: '/workspace/platform/web', displayName: 'web' }, - { path: '/workspace/platform/worker', displayName: 'worker' } - ]) - - expect(labels.get(getRepoDisplayLabelKey({ path: '/workspace/platform/web' }))).toBe('web') - expect(labels.get(getRepoDisplayLabelKey({ path: '/workspace/platform/worker' }))).toBe( - 'worker' - ) - }) - it('adds the minimal real parent suffix only for colliding basenames', () => { const labels = getRepoDisplayLabelsByPath([ { path: '/workspace/platform/web', displayName: 'web' }, @@ -80,15 +68,6 @@ describe('getRepoDisplayLabelsByPath', () => { ) }) - it('keeps identical project paths short across local and paired execution hosts', () => { - const local = { path: '/home/user/projects/app', displayName: 'app' } - const paired = { ...local, executionHostId: 'runtime:home' as const } - const labels = getRepoDisplayLabelsByPath([local, paired]) - expect(labels.get(getRepoDisplayLabelKey(local))).toBe('app') - expect(labels.get(getRepoDisplayLabelKey(paired))).toBe('app') - expect(labels.size).toBe(2) - }) - it('disambiguates collisions only within their owning host, even when paths overlap', () => { const local = { path: '/workspace/payments/api', displayName: 'api' } const sibling = { path: '/workspace/billing/api', displayName: 'api' } diff --git a/src/shared/repo-host-identity.test.ts b/src/shared/repo-host-identity.test.ts index 2326b94951d..39910cb5df4 100644 --- a/src/shared/repo-host-identity.test.ts +++ b/src/shared/repo-host-identity.test.ts @@ -1,10 +1,6 @@ -import { describe, expect, expectTypeOf, it } from 'vitest' +import { describe, expect, it } from 'vitest' import { toRuntimeExecutionHostId, toSshExecutionHostId } from './execution-host' -import { - getRepoHostIdentityForParts, - getRepoRegistrationHostIds, - type HostScopedRepoKey -} from './repo-host-identity' +import { getRepoHostIdentityForParts, getRepoRegistrationHostIds } from './repo-host-identity' // Why: these keys index persisted and cross-module repo state, so the bytes are frozen (D25). describe('repo host identity golden bytes', () => { @@ -21,17 +17,6 @@ describe('repo host identity golden bytes', () => { ] as const)('composes %s on %s', (repoId, hostId, expected) => { expect(getRepoHostIdentityForParts(repoId, hostId)).toBe(expected) }) - - it('brands the key so a plain string cannot pose as one', () => { - const key = getRepoHostIdentityForParts('repo-1', 'local') - expectTypeOf(key).toEqualTypeOf() - expectTypeOf().toExtend() - expectTypeOf().not.toExtend() - const raw: string = key - // @ts-expect-error a raw string is not a host-scoped key - const forged: HostScopedRepoKey = 'local\0repo-1' - expect(raw).toBe(forged) - }) }) describe('getRepoRegistrationHostIds', () => { diff --git a/src/shared/repo-ref-maintenance.test.ts b/src/shared/repo-ref-maintenance.test.ts index 593c08169dc..396fa491932 100644 --- a/src/shared/repo-ref-maintenance.test.ts +++ b/src/shared/repo-ref-maintenance.test.ts @@ -554,20 +554,6 @@ describe('RepoRefMaintenance single-flight and backoff', () => { expect(spans).toHaveLength(1) }) - it('records a failure when the pack left the backlog in place', async () => { - const refs = await refsDirectoryWith(THRESHOLD + 2) - const { maintenance, spans } = createHarness({ - packRefs: async () => { - throw new Error('permission denied') - } - }) - - maintenance.arm(target('local::/denied/.git', refs, () => Promise.reject(new Error('denied')))) - await elapseQuietPeriod(maintenance) - - expect(attributesOf(spans[0])['repo.maintenance_outcome']).toBe('failed') - }) - it('records a failure instead of throwing, and backs off', async () => { const refs = await refsDirectoryWith(THRESHOLD + 2) const { maintenance, spans, packRefs } = createHarness({ diff --git a/src/shared/repo-routing-connection-id.test.ts b/src/shared/repo-routing-connection-id.test.ts index aa1f88d7583..f13a78f58c7 100644 --- a/src/shared/repo-routing-connection-id.test.ts +++ b/src/shared/repo-routing-connection-id.test.ts @@ -22,18 +22,6 @@ describe('getRepoRoutingConnectionId', () => { expect(getRepoRoutingConnectionId(repo)).toBe(expected) }) - it('never answers local for a row whose raw connectionId is set', () => { - for (const executionHostId of [ - undefined, - 'local', - 'runtime:env', - 'ssh:gpu', - 'ssh:other' - ] as const) { - expect(getRepoRoutingConnectionId({ executionHostId, connectionId: 'gpu' })).not.toBeNull() - } - }) - it('lets executionHostId win on a contradictory SSH row, as getRepoExecutionHostId does', () => { expect(getRepoRoutingConnectionId({ executionHostId: 'ssh:b', connectionId: 'a' })).toBe('b') }) diff --git a/src/shared/repro-13889-claude-quarter-circle-busy-title.test.ts b/src/shared/repro-13889-claude-quarter-circle-busy-title.test.ts index 838c7538198..93b530fc742 100644 --- a/src/shared/repro-13889-claude-quarter-circle-busy-title.test.ts +++ b/src/shared/repro-13889-claude-quarter-circle-busy-title.test.ts @@ -9,12 +9,10 @@ import { } from './agent-detection' import { isDecorativeAgentTitleFrameChange } from './agent-decorative-title-signature' import { stripLeadingAgentTitleDecoration } from './agent-title-decoration' -import { resolveExplicitTerminalTitleAgentType } from './terminal-title-agent-type' // Titles captured from real `claude` binaries running the same one-line prompt. // 2.1.228 swapped the busy spinner from braille to quarter circles, which read as // "no agent" and made the tracker report a confirmed exit mid-turn (#13889). -const BUSY_2_1_227 = ['⠂ Claude Code', '⠐ Claude Code', '⠂ Say hi in one word'] as const const BUSY_2_1_228 = ['◐ Claude Code', '◑ Claude Code', '◑ Say hi in one word'] as const const CAPTURED_2_1_228_TURN = [ @@ -58,30 +56,12 @@ describe('Claude Code quarter-circle busy titles (#13889)', () => { } }) - it('matches 2.1.227 status and identity frame for frame', () => { - BUSY_2_1_228.forEach((title, index) => { - const braille = BUSY_2_1_227[index] - expect(detectAgentStatusFromTitle(title)).toBe(detectAgentStatusFromTitle(braille)) - expect(getAgentLabel(title)).toBe(getAgentLabel(braille)) - expect(resolveExplicitTerminalTitleAgentType(title)).toBe( - resolveExplicitTerminalTitleAgentType(braille) - ) - }) - }) - it('never confirms an agent exit across a real 2.1.228 turn', () => { const events = trackTurn(CAPTURED_2_1_228_TURN) expect(events).not.toContain('exited') expect(events).toEqual(['working', 'idle']) }) - it('tracks the 2.1.228 turn exactly like the 2.1.227 turn', () => { - const brailleTurn = CAPTURED_2_1_228_TURN.map((title) => - title.replace('◐', '⠂').replace('◑', '⠐') - ) - expect(trackTurn(CAPTURED_2_1_228_TURN)).toEqual(trackTurn(brailleTurn)) - }) - it('strips the spinner from stale exit titles and displayed labels', () => { expect(clearWorkingIndicators('◐ Say hi in one word')).toBe('Say hi in one word') expect(stripLeadingAgentTitleDecoration('◐ Claude Code')).toBe('Claude Code') diff --git a/src/shared/resolved-worktree-lineage.test.ts b/src/shared/resolved-worktree-lineage.test.ts index 19d3561ad01..8d2f7eb62b5 100644 --- a/src/shared/resolved-worktree-lineage.test.ts +++ b/src/shared/resolved-worktree-lineage.test.ts @@ -51,22 +51,12 @@ describe('sharesWorktreeLineageBoundary', () => { ...overrides }) - it('accepts the same host', () => { - expect(sharesWorktreeLineageBoundary(boundary(), boundary())).toBe(true) - }) - it.each([ ['child host', boundary({ hostId: undefined }), boundary()], ['parent host', boundary(), boundary({ hostId: undefined })] ])('treats an undefined %s as compatible', (_label, child, parent) => { expect(sharesWorktreeLineageBoundary(child, parent)).toBe(true) }) - - it('rejects a different known host', () => { - expect(sharesWorktreeLineageBoundary(boundary(), boundary({ hostId: 'ssh:remote' }))).toBe( - false - ) - }) }) describe('projectResolvedWorktreeLineage', () => { diff --git a/tests/e2e/browser-settings-preview.spec.ts b/tests/e2e/browser-settings-preview.spec.ts deleted file mode 100644 index bf8348e499f..00000000000 --- a/tests/e2e/browser-settings-preview.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Opt-in Settings → Browser preview; enable with ORCA_SETTINGS_PREVIEW=1. -import { expect, test } from './helpers/orca-app' - -test.skip( - process.env.ORCA_SETTINGS_PREVIEW !== '1', - 'Preview only; run with ORCA_SETTINGS_PREVIEW=1' -) - -const HOLD_MINUTES = 20 - -test('shows the remote browsing settings section and holds for review', async ({ - electronApp, - orcaPage -}) => { - test.setTimeout((HOLD_MINUTES + 10) * 60_000) - - await electronApp.evaluate(({ BrowserWindow }) => { - const window = BrowserWindow.getAllWindows()[0] - window?.setSize(1440, 900) - window?.center() - window?.show() - window?.focus() - }) - - // Seed one opted-out SSH host so the "Route again" list renders too. - await orcaPage.evaluate(() => { - const state = window.__store?.getState() - state?.updateSettings({ browserSshWorkspaceRoutingDisabledTargetIds: ['preview-target'] }) - window.__store?.setState({ - sshTargetLabels: new Map([['preview-target', 'openclaw']]) - } as never) - state?.openSettingsTarget({ - pane: 'browser', - repoId: null, - sectionId: 'browser-ssh-workspace-routing' - }) - state?.openSettingsPage() - }) - - await expect(orcaPage.getByText('Remote browsing', { exact: true })).toBeVisible({ - timeout: 30_000 - }) - - console.log(`\n=== SETTINGS PREVIEW READY — window stays up ${HOLD_MINUTES} minutes ===\n`) - await orcaPage.waitForTimeout(HOLD_MINUTES * 60_000) -}) diff --git a/tests/e2e/browser-tab.spec.ts b/tests/e2e/browser-tab.spec.ts index 9ff55d9056c..71cd0ee77e0 100644 --- a/tests/e2e/browser-tab.spec.ts +++ b/tests/e2e/browser-tab.spec.ts @@ -410,56 +410,6 @@ test.describe('Browser Tab', () => { } }) - test('Cmd/Ctrl+0 resets a zoomed browser page to 100%', async ({ orcaPage }) => { - const formServer = await startBrowserFormServer() - try { - const worktreeId = (await getActiveWorktreeId(orcaPage))! - const browserTab = await createBrowserTab( - orcaPage, - worktreeId, - formServer.url('Zoom reset'), - 'Zoom Reset' - ) - expect(browserTab?.id).toBeTruthy() - await expect - .poll(async () => readBrowserInputValue(orcaPage, browserTab!.id), { timeout: 5_000 }) - .not.toBeNull() - - const zoomModifier: 'meta' | 'control' = process.platform === 'darwin' ? 'meta' : 'control' - await orcaPage.evaluate( - async ({ browserTabId, browserPageId, modifier }) => { - const slot = document.querySelector(`[data-browser-overlay-tab-id="${browserTabId}"]`) - const webview = slot?.querySelector('webview') as Electron.WebviewTag | null - if (!webview) { - throw new Error(`Missing webview for browser tab ${browserTabId}`) - } - window.dispatchEvent( - new CustomEvent('orca:browser-page-zoom', { - detail: { browserPageId, direction: 'in' } - }) - ) - await webview.sendInputEvent({ type: 'keyDown', keyCode: '0', modifiers: [modifier] }) - await webview.sendInputEvent({ type: 'keyUp', keyCode: '0', modifiers: [modifier] }) - }, - { - browserTabId: browserTab!.id, - browserPageId: browserTab!.pageId ?? browserTab!.id, - modifier: zoomModifier - } - ) - await expect - .poll(() => - orcaPage.evaluate((browserTabId) => { - const slot = document.querySelector(`[data-browser-overlay-tab-id="${browserTabId}"]`) - return (slot?.querySelector('webview') as Electron.WebviewTag | null)?.getZoomLevel() - }, browserTab!.id) - ) - .toBe(0) - } finally { - await formServer.close() - } - }) - test('reloading one browser tab does not adopt another tab zoom', async ({ orcaPage }) => { const [formServerA, formServerB] = await Promise.all([ startBrowserFormServer(), diff --git a/tests/e2e/client-hosted-browser-tooltip-preview.spec.ts b/tests/e2e/client-hosted-browser-tooltip-preview.spec.ts deleted file mode 100644 index 5bbf167722d..00000000000 --- a/tests/e2e/client-hosted-browser-tooltip-preview.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Opt-in client-hosted browser tooltip preview; enable with ORCA_TOOLTIP_PREVIEW=1. -import { createServer, type Server } from 'node:http' -import type { AddressInfo } from 'node:net' -import type { Page } from '@stablyai/playwright-test' -import { expect, test } from './helpers/orca-app' -import { launchHeadlessPairedRuntimeHost } from './helpers/headless-paired-runtime-host' -import { launchPairedElectronClient } from './helpers/paired-electron-client' - -test.skip(process.env.ORCA_TOOLTIP_PREVIEW !== '1', 'Preview only; run with ORCA_TOOLTIP_PREVIEW=1') - -const HOLD_MINUTES = 20 - -async function startFixtureServer(): Promise<{ close(): Promise; url: string }> { - const server = createServer((_request, response) => { - response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }) - response.end( - 'tooltip-preview

Client-hosted page

The intro tooltip should point at the controls above.

' - ) - }) - await new Promise((resolve, reject) => { - server.once('error', reject) - server.listen(0, '127.0.0.1', () => { - server.off('error', reject) - resolve() - }) - }) - return { - close: () => closeServer(server), - url: `http://127.0.0.1:${(server.address() as AddressInfo).port}/preview` - } -} - -async function closeServer(server: Server): Promise { - await new Promise((resolve, reject) => { - server.closeAllConnections() - server.close((error) => (error ? reject(error) : resolve())) - }) -} - -async function findPairedWorktreeId(page: Page, repoPath: string): Promise { - const read = () => - page.evaluate( - (path) => - window.__store - ?.getState() - .allWorktrees() - .find((worktree) => worktree.path === path)?.id ?? null, - repoPath - ) - await expect - .poll(read, { timeout: 60_000, message: 'paired client never received the host worktree' }) - .not.toBeNull() - const worktreeId = await read() - if (!worktreeId) { - throw new Error('Paired worktree disappeared after discovery') - } - return worktreeId -} - -test('shows the client-hosted browser intro tooltip and holds for review', async ({ - testRepoPath -}, testInfo) => { - test.setTimeout((HOLD_MINUTES + 10) * 60_000) - const fixtureServer = await startFixtureServer() - const host = await launchHeadlessPairedRuntimeHost() - let dispose = async (): Promise => { - await host.dispose() - await fixtureServer.close() - } - try { - await host.client.call('repo.add', { path: testRepoPath, kind: 'git' }) - await host.client.call('terminal.create', { - worktree: `path:${testRepoPath}`, - title: 'Preview Terminal' - }) - const client = await launchPairedElectronClient(host.offer, testInfo, 'tooltip preview') - const disposeWithoutClient = dispose - dispose = async () => { - await client.dispose() - await disposeWithoutClient() - } - - // The e2e profile launches hidden; surface the window for the human. - await client.app.evaluate(({ BrowserWindow }) => { - const window = BrowserWindow.getAllWindows()[0] - window?.setSize(1440, 900) - window?.center() - window?.show() - window?.focus() - }) - - const worktreeId = await findPairedWorktreeId(client.page, testRepoPath) - await client.page.evaluate( - ({ environmentId, worktreeId }) => { - window.__store?.getState().setActiveWorktree(worktreeId, `runtime:${environmentId}`) - }, - { environmentId: client.environmentId, worktreeId } - ) - await expect - .poll( - (): Promise => - client.page.evaluate( - (id) => window.__store?.getState().groupsByWorktree[id]?.[0]?.tabOrder.length ?? 0, - worktreeId - ), - { timeout: 120_000, message: 'paired client never adopted the host tab group' } - ) - .toBeGreaterThanOrEqual(1) - - await client.page.evaluate( - async ({ url, worktreeId }) => { - const state = window.__store?.getState() - if (!state) { - throw new Error('store unavailable') - } - const groupId = state.groupsByWorktree[worktreeId]?.[0]?.id - state.setBrowserDefaultUrl(url) - await state.openNewBrowserTabInActiveWorkspace(groupId) - }, - { url: fixtureServer.url, worktreeId } - ) - - await expect - .poll( - (): Promise => - client.page.evaluate(() => window.__store?.getState().activeContextualTourId ?? null), - { timeout: 60_000, message: 'intro tooltip never became the active contextual tour' } - ) - .toBe('client-hosted-browser') - - console.log(`\n=== TOOLTIP PREVIEW READY — window stays up ${HOLD_MINUTES} minutes ===\n`) - await new Promise((resolve) => setTimeout(resolve, HOLD_MINUTES * 60_000)) - } finally { - await dispose() - } -})