Skip to content

Share one fake HTTP body that enforces the net/http lifecycle in every test - #166

Merged
shawnburke merged 3 commits into
mainfrom
shawn/api-fake-body
Oct 14, 2026
Merged

shawnburke merged 3 commits into
mainfrom
shawn/api-fake-body

Conversation

@shawnburke

@shawnburke shawnburke commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #165.

Why

#165 was caught by a one-off body fake that fails reads after the request context is cancelled. Neither existing fake could have caught it:

  • The trackedBody copies in server/api and server/snykbroker are backed by a strings.Reader, which has no idea a request context exists. They verify drain and close, and nothing about cancellation.
  • The httptest.NewServer tests use a real transport, but every body in them is a few bytes. net/http serves bytes it already buffered even after cancel, so small bodies pass by accident. Against a real server, cancelling between Do and ReadAll succeeds for an 11-byte body and fails with context canceled for a 1 MiB body after somewhere between 8 and 20 KB, varying run to run.

What

Commit 1. A new test/httpfake package owns a single Body and Transport. Every body served:

  • fails Read once the request's context is done (strictly, since the real buffered threshold is not something a test can control),
  • fails Read after Close, as net/http does,
  • records whether it was read to EOF and closed.

Both test files now build their client on it, so every test through that path gets all three rules without opting in. The package has its own tests, including one that caught a flaw in my first draft (reusing one canned response served an exhausted reader).

Commit 2. HttpBackend in grpctunnel is the only other http.Client consumer in the agent and had no tests; every tunnel test stubs the Backend interface above the HTTP layer. It hands the raw body to the call handler, which streams it under a context the handler cancels in a defer, which is the same shape as the #165 bug. A new test drives a multi-frame body through startCall with a real HttpBackend over an httpfake client and checks the body is streamed in full, the call ends with End not Cancel, and the body hit EOF and was closed. httpfake.NewBody lets the existing tunnel stubs hand out tracked bodies instead of io.NopCloser.

Proof

All four packages pass go vet and go test -race.

🤖 Generated with Claude Code

shawnburke and others added 3 commits October 15, 2026 06:41
…y test

The fix in the parent PR was caught by a one-off body fake that failed
reads after the request context was cancelled. The two existing fakes
(trackedBody in api and in snykbroker) were backed by a strings.Reader
that ignores the context, so the drain-and-close tests passed against
code that cancelled the deadline before reading the body. The httptest
based tests passed too, because net/http serves a small body out of its
buffer after cancel; only bodies past the buffered threshold fail.

test/httpfake now owns a single Body and Transport. Every body served
fails reads once the request context is done, fails reads after Close,
and records EOF and Close, so each test that builds a client on it gets
all three rules without opting in. With the production fix reverted the
existing TestCall_ReadsDrainsAndClosesResponseBody and
TestCall_RateLimitedBodyIsDrainedAndClosed both fail, so the dedicated
regression test is folded into the first of them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
HttpBackend is the only other consumer of an http.Client in the agent,
and it had no tests: every tunnel test stubs the Backend interface above
the HTTP layer. It hands the raw response body to the call handler,
which streams it as frames under a context that startCall owns and
cancels in a defer. That is the shape of the bug #165 fixed in the
Cortex API path, with nothing pinning it down here.

The new test drives one call through startCall with HttpBackend over an
httpfake client and a body several frames long, and checks the whole
body is streamed, the call ends with End rather than Cancel, and the
body reached EOF and was closed. Cancelling the context right after
backend.Do returns makes it fail with "upstream read failed: context
canceled".

httpfake gains NewBody so fakes above the HTTP client can hand out a
tracked body too; the tunnel stubs now use it instead of io.NopCloser.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shawnburke
shawnburke requested a review from keithfz October 14, 2026 21:18
@shawnburke
shawnburke enabled auto-merge (squash) October 14, 2026 21:18

@keithfz keithfz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@shawnburke
shawnburke merged commit d2a3136 into main Oct 14, 2026
20 checks passed
@shawnburke
shawnburke deleted the shawn/api-fake-body branch October 14, 2026 21:54
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.

2 participants