Skip to content

test: fail loudly if cassette replay stops intercepting edgartools' HTTP stack (243z) - #1389

Merged
dgunning merged 1 commit into
mainfrom
test/243z-vcr-interception-guard
Sep 30, 2026
Merged

dgunning merged 1 commit into
mainfrom
test/243z-vcr-interception-guard

Conversation

@dgunning

Copy link
Copy Markdown
Owner

What this changes

vcrpy intercepts by patching httpcore's connection pools by name. If an httpx or vcrpy upgrade, or the planned httpx2 swap, breaks that patching, every cassette test silently fetches from SEC and still passes. The q2iz spike measured exactly that: vcrpy 8.1.1 with httpx2 recorded 0 interactions and the test stayed green. Nothing in the suite noticed.

tests/meta/test_vcr_interception_guard.py adds 3 fast tests. Each replays a cassette with record_mode="none" for 127.0.0.1:9, a dead address that no CACHE_RULES entry covers, and goes through edgartools' own get_with_retry / get_with_retry_async. That puts the throttle and cache layers above vcr in the path, as they are for every real test.

  • sync: asserts the body and cassette.play_count == 1
  • async: asserts the same
  • miss: a request the cassette lacks is refused by vcr (CannotOverwriteExistingCassetteException), not sent

This is a 6.0 gate (bead edgartools-243z, decided 2026-09-30). It must land before 5.60.0 and ahead of the edgar/files deletion.

Verification

Run Unmutated vcr's httpcore patch removed
normal 3 passed 3 failed (ConnectError: Connection refused)
-p tests._offline_harness 3 passed 3 failed (NetworkBlockedError)

The mutation replaced vcr.patch.CassettePatcherBuilder._httpcore with a no-op, which simulates lost interception. Unmutated, the module passes under the offline harness, so the PR gate's offline audit accepts it. tests/meta has 95 passing, and the guard also passes next to tests/core/test_httprequests.py and the #519 regression, both sequentially and with -n 3.

Deviation from the bead

The bead also asked for a record guard against a local HTTP server. I left it out: the offline audit blocks loopback sockets too, so that test would fail the PR gate as network-dependent. Recording and replay go through the same httpcore patch, so losing one means losing the other.

🤖 Generated with Claude Code

…TTP stack (243z)

vcrpy intercepts by patching httpcore's connection pools by name. When that
stops matching the client -- an httpx or vcrpy upgrade, or the httpx2 swap --
a cassette test silently fetches from the network and passes while SEC answers.
The q2iz spike measured it (vcrpy 8.1.1 + httpx2: 0 interactions, test green).

tests/meta/test_vcr_interception_guard.py replays a cassette for
127.0.0.1:9 (the discard port, which no cache rule covers) with
record_mode="none", through edgar.httprequests.get_with_retry and
get_with_retry_async, so the throttle/cache layers above vcr are in the path.
It asserts the body and cassette.play_count == 1, and that a request the
cassette lacks is refused by vcr (CannotOverwriteExistingCassetteException),
not sent.

Verified by mutation: with vcr.patch.CassettePatcherBuilder._httpcore
neutered, all 3 fail -- ConnectError normally, NetworkBlockedError under
tests/_offline_harness. Unmutated, all 3 pass both ways, which is what the
PR-gate offline audit requires.

Deviation from the bead: no record guard against a local HTTP server. The
offline audit blocks loopback too, so it would fail the PR gate as
network-dependent; record and replay share the same httpcore patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dgunning
dgunning enabled auto-merge (squash) September 30, 2026 21:01
@dgunning
dgunning merged commit 34851f9 into main Sep 30, 2026
8 checks passed
@dgunning
dgunning deleted the test/243z-vcr-interception-guard branch September 30, 2026 21:16
@dgunning dgunning mentioned this pull request Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant