test: cover browser recovery from a stale keep-alive socket - #34592
Open
jennifer-shehane wants to merge 2 commits into
Open
test: cover browser recovery from a stale keep-alive socket#34592jennifer-shehane wants to merge 2 commits into
jennifer-shehane wants to merge 2 commits into
Conversation
With the MITM proxy disabled the browser reaches the origin directly, so recovering a request written to a socket the origin has already closed is the browser's job rather than Cypress's. Pin what Cypress depends on there: the request succeeds, and a retry the browser performs below the interception layer is not reported to the test as a second request. The origin also counts the sockets it kills, so a change in connection pooling cannot quietly turn this into a test that never races anything. Refs: #24716 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrvQo3bxCQTAM7vDx1m3Py
The `Google Chrome` context could not be unskipped: it calls the private `chrome._getArgs(browser)` with one argument while that helper now takes `(browser, options, port)`, so every test in it threw on `options.userAgent` before a browser launched. It also drove a raw browser rather than Cypress, so a failure there described Chrome rather than anything Cypress guarantees. Remove it along with the helpers, controllers and routes only it reached. `network_error_handling.cy.js` covers the Cypress-level contract instead — visit, request and subresource retries — but nothing ever ran it. Wire it up. Its `<form>` body case only holds while Cypress proxies the traffic, since the browser will not replay a non-idempotent request, so that case moves to its own spec the harness selects by proxy mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XrvQo3bxCQTAM7vDx1m3Py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Additional details
With
CYPRESS_INTERNAL_DISABLE_PROXY=1browser traffic no longer goes through the Node MITM proxy — Cypress pauses requests via CDP Fetch and lets Chrome make them. Recovering from a request written to a dead pooled keep-alive socket therefore stops being Cypress's job and becomes the browser's, which leaves Cypress with an implicit dependency on Chromium's network stack that nothing tested.1. New test for that dependency.
network_error_stale_keepalive.cy.jsruns warm/race fetch pairs against an origin that answers the first request on a connection and meets any request on a reused socket with a FIN and no response. It asserts three things:cy.interceptfired exactly once per logical request — this is the fragile property, and what would catch a Chromium change or a Cypress change that moved interception above the retry layer.It is gated on
CYPRESS_INTERNAL_DISABLE_PROXY === '1', mirroringnon_proxied_spec.tsandcookies_spec.ts, so it activates when the flag flips.2. Removed the skipped
Google Chromecontext (9 tests). These could not simply be unskipped — they were bit-rotted.launchBrowsercalls the privatechrome._getArgs(browser)with one argument while that helper now takes(browser, options, port), so every test threwTypeError: Cannot read properties of undefined (reading 'userAgent')before a browser launched. They also drove a raw browser rather than Cypress, so a failure described Chrome rather than anything Cypress guarantees. Removed along with the helpers, controllers and routes only they reached.3. Wired up
network_error_handling.cy.js, which was orphaned. It holds 9 Cypress-level tests (cy.visit/cy.request/subresource retries) and nothing in the repo referenced it, so it never ran — while seven harness routes existed solely to serve it. It now runs, withexpectedExitCode: 2for the twofails after retrying 5xtests that fail by design.Its
<form>body case only holds while Cypress proxies the traffic: with the proxy disabled the browser owns the retry and will not replay a non-idempotent request, so it times out. That case moved to its own spec which the harness selects by proxy mode. This is worth a reviewer's attention — the behavior change looks intentional, but nothing currently asserts either way, and if it is intentional a positive assertion pinning the new contract (a POST is not replayed when unproxied) would be a good follow-up.Net effect: −9 dead tests, +9 live ones, 8 of which run in both proxy modes.
Steps to test
Results observed locally:
The one failure without the flag is
does not connect to the upstream proxy for the SNI server request, which fails identically ondevelopin the same environment — a sandbox networking limitation interfering withDebugProxy, not a regression from this change.Determinism of the socket-kill assertion was checked separately: instrumenting the stats route showed
killedOnReusedSocket=5on 5 consecutive runs, never varying, against a threshold of 1.One caveat on verification: no Google Chrome build was installable in the sandbox, so the new test was exercised against Chromium 141.0.7390.37 rather than branded Chrome. Same version and network stack, but CI will be the first run on the exact binary.
How has the user experience changed?
No change — test-only.
PR Tasks
cypress-documentation?type definitions?Generated by Claude Code
Note
Low Risk
Changes are limited to system-test specs, harness routes, and conditional skips; no product runtime code is modified.
Overview
Test-only refresh of network error system tests around proxy vs. direct-browser (
CYPRESS_INTERNAL_DISABLE_PROXY) behavior.Stale keep-alive: Adds
network_error_stale_keepalive.cy.jsand harness routes that simulate a FIN on a reused pooled socket. The spec checks the browser still gets 200, the server actually killed a reused socket, andcy.interceptonly sees one event per logical request (no double-reporting of browser-level retries). That suite runs only when the internal proxy is disabled.Previously orphaned Cypress retries:
network_error_handling.cy.jsis now executed from the harness (including the two tests that are expected to fail after max retries). The form POST body replay case moves tonetwork_error_form_retry.cy.jsand runs only when the Node MITM proxy is enabled, since body replay on reset is a proxy behavior.Cleanup: Removes the skipped raw-Chrome
launchBrowsercontext and related dead routes/controllers (proxy-invalid hosts, mid-response resets, etc.).Reviewed by Cursor Bugbot for commit 5a5257e. Bugbot is set up for automated code reviews on this repo. Configure here.