feat(story-1.8): RecordedTransport — built-in Transport test double#13
Merged
Conversation
Pragmatic Transport test double consolidating the five drifting in-tree stubs (_OkTransport, _FailingTransport, _FakeTransport, two _RecordingTransport variants, _TrackingTransport). Routes keyed by (method.upper(), url) → Response | BaseException with a configurable default for the no-match case (None → archive's RuntimeError). Routes fire indefinitely on repeat matches. requests: list[Request] + last_request property + aclose_calls counter cover the observability patterns spread across the existing stubs. In-tree stub replacement bundled as a single follow-up commit on the same branch. Out of scope: URL pattern matching, cassette files, streaming (Epic 4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds src/httpware/transports/recorded.py with RecordedTransport: - routes: Mapping[(method, url), Response | BaseException] with method uppercased on insert - default: Response | BaseException | None — None raises RuntimeError per archive AC; otherwise the default is returned or raised - requests: list[Request] populated on every __call__ - last_request property reading requests[-1] - aclose_calls counter - add_route(method, url, response_or_exception) for incremental setup - stream() raises NotImplementedError (lands in Epic 4) Five tests cover: route match returns Response, route raises Exception, no-match raises RuntimeError, default Response returned on no-match, default Exception raised on no-match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ll suite Ten additional tests bring RecordedTransport to 15 total: - method normalization in both directions (route key vs request) - requests list captures every call in order; last_request property - aclose counter; idempotent close that doesn't block subsequent calls - stream() raises NotImplementedError pointing to Epic 4 - isinstance(RecordedTransport(), Transport) — protocol satisfaction - add_route adds and replaces entries - routes fire indefinitely on repeat matching calls 100% line coverage on src/httpware/transports/recorded.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds RecordedTransport to httpware/__init__.py imports and __all__ so consumers can `from httpware import RecordedTransport` in addition to the subpackage path. CHANGELOG records the Story 1.8 surface and notes the in-tree stub consolidation (Task 4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…edTransport Replaces the file-local _FakeTransport (test_client_construction.py), _OkTransport and _FailingTransport (test_middleware.py), two distinct _RecordingTransport variants (test_client_methods.py, test_client_response_model.py, test_client_middleware_wiring.py), and _TrackingTransport (test_client_lifecycle.py) with one shared RecordedTransport class. Each replacement is mechanical: - _FakeTransport() → RecordedTransport() - _OkTransport() → RecordedTransport(default=Response(...)) - _FailingTransport(exc) → RecordedTransport(default=exc) - _RecordingTransport (last_request flavor) → RecordedTransport(default=Response(...)) - _RecordingTransport (calls counter) → RecordedTransport(default=Response(...)) with .calls → len(transport.requests) - _TrackingTransport → RecordedTransport() (aclose_calls attribute preserved) Also fixes a pre-existing ruff format violation in recorded.py (ternary expression on three lines instead of one). No test behavior changes. Total test count unchanged from this commit's edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…onale Final review concern: the docstring named the route/default type (`Response | BaseException`) without explaining why BaseException (not Exception) is the chosen union. Adds a one-paragraph note: the choice lets test code express `asyncio.CancelledError`, `SystemExit`, etc.; surfaces that these bypass `except Exception:`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
This closes Epic 1.
Out of scope (subsequent stories): URL pattern matching / globs, cassette files loaded from JSON, streaming responses (Epic 4).
Spec + plan: `docs/superpowers/specs/2026-05-31-recordedtransport-design.md`, `docs/superpowers/plans/2026-05-31-recordedtransport-plan.md`.
Test plan
🤖 Generated with Claude Code