Replace live HTTP dependencies in core tests - #3562
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ae4054f21
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Code reviewSummary: Replaces the flaky live-network HTTP tests ( Suggestion: reuse
|
There was a problem hiding this comment.
Code review
Summary: This PR replaces live-network dependencies (thomhurst.github.io, a GitHub release download) in HttpTests and DownloaderTests with a deterministic in-process fixture (LocalHttpServer), closing #3519. I reviewed the current head (4ef8d66), including the fix applied after the earlier codex review comment.
Verified
- The previously flagged race condition is fixed. The earlier review (codex) correctly flagged that
CancellationTokenSource(TimeSpan.FromSeconds(10))started counting down from construction time, not fromDisposeAsync, which could cancelAcceptTcpClientAsyncbefore slow host/pipeline startup even reachedSendAsync. The current code (LocalHttpServer.cs:11) uses a plainnew CancellationTokenSource()with cancellation only triggered inDisposeAsync, so this is resolved — confirmed by reading the file, not just the PR comment claiming it. - Checksum correctness:
md5("local download fixture") == AEDF5D7C23744269F358814E602AFE89, matchesDownloaderTests.cs:20. - Single-accept design matches actual usage:
LocalHttpServer.RunAsyncaccepts exactly one TCP connection. I checkedHttp.SendAsyncandDownloader.DownloadResponseAsync/DownloadFileAsync— neither issues retries or multiple requests per call (no Polly/retry wrapping inHttp.cs), and each test creates its own server instance, so one-shot accept is sufficient and not fragile. - Removing
[Retry(3)]fromCan_Downloadis appropriate now that the dependency is deterministic rather than a flaky external network call. await usingscoping in each test correctly binds the fixture's lifetime to the test, and binding to port 0 on loopback avoids collisions under parallel runs.
Not addressed (pre-existing suggestion, non-blocking)
An earlier review comment suggested using RichardSzalay.MockHttp (already referenced in Directory.Packages.props but currently unused anywhere in the repo) instead of a hand-rolled TcpListener/raw-HTTP fixture, since HttpOptions.HttpClient already provides an injection seam via ConfigurePrimaryHttpMessageHandler. That suggestion wasn't adopted, and the current 71-line LocalHttpServer still hand-parses request lines and writes raw HTTP bytes. This isn't a bug — the fixture is correct and now race-free — but it remains added surface area (manual socket lifecycle, cancellation swallowing for OperationCanceledException/SocketException) that an already-paid-for dependency would remove. Worth considering for a follow-up if more HTTP fixtures are needed later, but not a blocker for this fix.
No functional issues found. Approving.
4ef8d66 to
2ec5f7b
Compare
|
Rebased onto current |
Code reviewSummary: Rebased onto current Previous suggestion (reuse
|
2ec5f7b to
95f4777
Compare
|
Rebased onto current @codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Code review (verification pass)
Summary: Confirmed the current head (95f47779fd) is patch-id identical to the previously fully-reviewed revision — no functional changes since the last review. Re-verified independently by reading the file at this commit rather than trusting the "no changes" claim:
LocalHttpServer.cs:_cancellationTokenSourceis a plainnew()with no timeout, cancelled only inDisposeAsync— the earlier-flagged race (timer starting at construction instead of disposal, which could cancelAcceptTcpClientAsyncbefore the test even issued its request) is confirmed fixed.- Single-accept design matches usage: neither
Http.SendAsyncnorDownloader.DownloadResponseAsyncretries or issues multiple requests per call, so accepting exactly one connection per server instance is correct, not fragile. - Checksum in
DownloaderTests.cs(AEDF5D7C23744269F358814E602AFE89) matches MD5 of"local download fixture". await usingscoping ties each server's lifetime to its test; binding to port 0 on loopback avoids collisions under parallel runs.- Dropping
[Retry(3)]onCan_Downloadis correct now that the dependency is deterministic.
Still open, non-blocking: the earlier suggestion to reuse RichardSzalay.MockHttp (already referenced in Directory.Packages.props, currently unused anywhere in the repo) instead of hand-rolling a TcpListener-based HTTP/1.1 fixture hasn't been adopted. HttpOptions.HttpClient already provides an injection seam (ConfigurePrimaryHttpMessageHandler), and this PR's own customHttpClient: true case in HttpTests proves that seam works. Using MockHttpMessageHandler would remove the manual socket accept-loop/cancellation lifecycle and raw HTTP/1.1 byte-writing this PR introduces, at no cost since the dependency is already paid for. Worth a follow-up decision, but not a blocker — the fixture as written is correct and now race-free.
No new issues found. Approving from a correctness standpoint.
95f4777 to
5351949
Compare
|
Rebased cleanly onto current |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Code reviewSummary: Re-verified at head Architectural suggestion (repeating from earlier reviews — still unaddressed)
There's already an injection seam that avoids all of that: Why this is better than the current design:
This is non-blocking — the fixture is correct and race-free as written — but since this has come up on every prior revision of this PR without being adopted, it's worth an explicit decision (adopt No new issues found. |
5351949 to
a00ad54
Compare
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Code reviewNo issues found. Checked for bugs, security problems, and CLAUDE.md compliance across the diff ( Verified against prior review history:
Standing non-blocking suggestion (raised in earlier review passes, still applicable, not a blocker): Good fix overall — replaces genuinely flaky, network-dependent tests with a deterministic in-process fixture, and the known race from earlier revisions stayed fixed through the rebases. |
Cancel the local server only during disposal so slow test setup cannot expire the accept loop. Refs #3519
a00ad54 to
8b67dfd
Compare
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
Code review
Summary: Re-verified at head 8b67dfdb73. This replaces the live-network dependencies in HttpTests/DownloaderTests (thomhurst.github.io, a GitHub release download) with a deterministic in-process LocalHttpServer fixture, closing #3519.
Verified independently
- MD5 of
"local download fixture"isAEDF5D7C23744269F358814E602AFE89— matchesDownloaderTests.cs. - Single-accept-per-instance design is correct for actual usage:
Http.SendAsync(src/ModularPipelines/Http/Http.cs) andDownloader.DownloadResponseAsync/DownloadFileAsyncnever retry or issue more than one request per call, and each test constructs its ownLocalHttpServer— so oneAcceptTcpClientAsyncper instance is sufficient, not fragile. - The previously-flagged construction-time-timeout race is still fixed:
_cancellationTokenSourceis a plainnew(), cancelled only inDisposeAsync, andAcceptTcpClientAsync/stream.WriteAsyncboth honor that token, so shutdown paths are covered by theOperationCanceledException/SocketExceptioncatches. - Dropping
[Retry(3)]onCan_Downloadis correct now that the fixture is deterministic.
Standing suggestion, unaddressed across ~6 revisions of this PR — worth an explicit decision before merge
RichardSzalay.MockHttp is pinned in Directory.Packages.props and referenced by ModularPipelines.UnitTests.csproj, but nothing in the repo uses it. This PR adds a second, parallel "fake an HTTP response" mechanism instead: a hand-rolled TcpListener-based HTTP/1.1 server that manually reads request lines and writes status/header bytes.
Http.SendAsync already provides the seam to avoid this: when HttpOptions.HttpClient is set, it bypasses the named IHttpClientFactory client but still runs the full SendAndWrapLogging pipeline (request/response logging, status code, duration) — this PR's own Assert_SendAsync_Logs_As_Expected(customHttpClient: true) case proves that path works end-to-end. A MockHttpMessageHandler wired through that seam would produce a real HttpResponseMessage with headers/content/status, so the existing assertions (Server: ..., Content-Length, etc.) would work unchanged, without owning a socket accept-loop, manual CancellationTokenSource lifecycle, or raw HTTP/1.1 wire parsing.
This has been raised on every prior review pass on this PR and hasn't blocked any of them functionally — the fixture as written is correct and race-free. But since it keeps resurfacing without a decision, I'd treat this rebase as the point to either adopt MockHttp and delete LocalHttpServer.cs, or explicitly document why raw-socket fidelity is wanted here (e.g. exercising the real default HttpClient/IHttpClientFactory pipeline rather than only the HttpOptions.HttpClient override path) — otherwise the test project ends up permanently carrying two parallel HTTP-faking mechanisms for no documented reason.
No new issues found.
Summary
thomhurst.github.iorequests with a loopback HTTP fixtureValidation
ModularPipelines.UnitTests.csproj(0 errors; 60 existing warnings)HttpTestsandDownloaderTests)dotnet format --verify-no-changes --severity info: passedCloses #3519