Cover the reconcile engine gate behaviorally — the seam I said it needed turned out to exist (#2230) - #2245
Conversation
#2230's second door. My own note on the first said the reconcile and snapshot_now gates "observe an ABSENCE (no connection attempted), which needs a counting seam that does not exist yet". That was half wrong, and worth correcting in place: the belt gate inside DarlingXeSessions.ReconcileLongQueryCompletionsAsync is public and is its own precondition, so an ungated call THROWS where a gated one returns. That difference needs no seam, no live store, and no network. The regression it pins was measured, not theoretical. Ungated, the method built a SqlConnection from a PostgreSQL connection string, the ctor threw "Keyword not supported: 'host'", the caller's catch skipped the latch assignment, and because LongQueryTraceApplied resets to null on every connect it retried EVERY sweep forever — roughly 1,440 warnings/day/server (#2213 round 2). Both enabled values, because the gate sits ahead of that branch: ungated, the false arm would try to DROP a session over the same impossible connection. The second fact is the one that makes the first mean anything — same host, same connection string, engine flipped to SqlServer, and the exact field exception surfaces. Without it the pin would pass just as happily against a method that had stopped connecting for some unrelated reason. Confirmed the exception locally against Microsoft.Data.SqlClient before writing the assertion, rather than assuming the message. snapshot_now is still uncovered; #2230 stays open at 2 of 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| }; | ||
| private static void SetField(DarlingWorker worker, string name, object value) => |
There was a problem hiding this comment.
Nit: missing blank line between PostgresRuntime() and SetField — every other member in this file is separated by a blank line (see the rest of the class). Minor, but worth a quick fix for consistency:
| }; | |
| private static void SetField(DarlingWorker worker, string name, object value) => | |
| }; | |
| private static void SetField(DarlingWorker worker, string name, object value) => |
There was a problem hiding this comment.
Fixed in 3b467aca. Cause was mechanical: the block I spliced in ended with a single newline, so the helper's closing brace landed directly against SetField — every other member in the file is separated, as you say.
Also worth recording your async observation, because it's load-bearing for the test and not obvious: new SqlConnection(...) runs synchronously before any await, but since the method is declared async the ArgumentException is captured into the returned Task rather than thrown at the call site. That's exactly why Assert.ThrowsAsync is the right assertion here and a synchronous Assert.Throws would have failed to observe it.
Review summaryThis PR is test-only ( Verified correctness of the two new tests:
One minor style nit left as an inline comment: a missing blank line between two methods, inconsistent with the rest of the file's spacing convention. Nothing else stood out — this is a solid, well-reasoned addition to the #2230 gate coverage. |
Review nit, and correct: the inserted block ended with a single newline, so the helper's closing brace butted against SetField while every other member in the file is separated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Reviewed. This is a test-only addition to Verified the two new tests against the actual implementation in
One very minor, non-blocking observation: No correctness, security, or performance issues found. The doc-comment correction in the class summary (replacing the now-inaccurate "needs a counting seam that does not exist yet" claim) is accurate per the source read above. |
#2230's second of three doors. Leaves the issue open at 2/3.
Correcting my own note
When I covered the
analyze_nowgate, I wrote in that test's doc:Half wrong.
DarlingXeSessions.ReconcileLongQueryCompletionsAsynccarries its own engine precondition — "belt to the worker's braces", per its comment — and it'spublic static. So an ungated call throws where a gated one returns, which an assertion can see with no seam, no live store, and no network. The doc is corrected in place rather than left to mislead the next person who reads it.The regression, which was measured
Ungated, the method built a
SqlConnectionfrom a PostgreSQL connection string:The caller's
catchthen skipped the latch assignment, and becauseLongQueryTraceAppliedresets tonullon every connect, it retried every sweep, forever — roughly 1,440 warnings/day/server (#2213 round 2).I confirmed that exception locally against
Microsoft.Data.SqlClientbefore writing the assertion, rather than trusting my memory of the message.What's pinned
enabled: trueenabled: falseKeyword not supported: 'host'That last row is what makes the first two mean anything.
runneris passednullon purpose in the PG arms: reaching it would mean the gate didn't fire. And the engine is the only difference between the gated and ungated cases — same host, same string — so the test can't pass because of some incidental change to connection handling.Verification
Builds clean (0 warnings). The tests themselves can't run on macOS — both test projects are
net10.0-windowsand needMicrosoft.WindowsDesktop.App, which doesn't exist for this platform — so they're CI-verified. What I did verify locally is the pair of facts the assertions rest on: the gate is the method's first statement (read from source), andSqlConnectionrejects that string (run locally, output above).Still open
snapshot_nowremains uncovered. It's the one that genuinely does observe an absence with no public precondition of its own, so it needs either a counting seam or a different observable. #2230 stays open and says so.