Repository navigation
Darling tests: a test-only read route no longer leaks into other test classes running at the same time (#4782) - #4785
Merged
Conversation
… flow that set it (#4782) ReadLatencyWebRecordingTests registers one extra /api/read dispatch entry (__test_statement_timeout) through DarlingWebEndpoints.s_testOnlyExtraDispatchEntry. That was a plain static, so every test class running at the same time saw the route in its own BuildReadDispatch() call. On dev, DarlingCustomViewsTests.CatalogDescriptors_Keys_EqualTheReadDispatchKeys failed with "reads with no catalog descriptor: __test_statement_timeout". The entry is now an AsyncLocal behind an internal TestOnlyExtraDispatchEntry property, so only the async flow that set it (and what that flow starts or awaits) gets the extra key. The recording test still builds its server in the same method that sets the entry, so its route is still registered. Every assertion in both classes is unchanged. New test: DarlingWebEndpointsTests .TheTestOnlyExtraDispatchEntry_IsSeenBySameFlow_AndNotByAFlowThatDoesNotInheritIt sets the entry, asserts the same flow sees it, then reads the dispatch from a task started under ExecutionContext.SuppressFlow() and asserts it does not. With the plain static it fails on that second assertion.
erikdarlingdata
marked this pull request as ready for review
September 29, 2026 10:16
5 of 6 tasks
erikdarlingdata
added a commit
that referenced
this pull request
Sep 29, 2026
…un_custom_view_panel runs count with the web dashboard off (#4782) (#4839) The web server kept its read-latency accumulator and logger in two process-wide statics. Every MapAll call overwrote them, and the two record sites read them at request time. Production calls MapAll once, but six test classes call it and xUnit runs classes in parallel, so a server that another class built could take a test's samples. That is how ReadLatencyWebRecordingTests.ADispatchEntryThatThrowsA57014PostgresException_RecordsOneWebSample_WithOutcomeTimeout failed in CI run 36592791128, on #4829's branch. #4785 fixed the test-only dispatch entry but not the accumulator. - ReadLatencyRecorder (new) holds the accumulator and logger one server was given. MapAll builds one per call, and both record sites use it: the /api/read/* dispatch loop and the /api/compose/run route. Both statics are removed. - The shared composed-panel runner, RunComposedPanelAsync, takes the recorder as an optional last parameter. A direct caller that passes none records nothing. - MCP run_custom_view_panel takes the recorder as a DI service parameter, which is not in the tool's advertised schema. The MCP host registers one over the same accumulator its per-call latency filter records into. Only the web startup used to fill the static, so with the web dashboard off (the default) these runs were not recorded. They are now recorded as composed-panel reads, the same as a panel run from the web dashboard. - McpServiceParameterDiSeatCensusTests adds ReadLatencyRecorder to the pinned set of DI service parameter types. - New tests, none needing a database. ReadLatencyPerServerRecordingTests builds two servers, each with its own accumulator, and checks that a /api/read/* timeout and a /api/compose/run error land only in the server that answered. A new McpToolLatencyRecordingTests fact checks that the MCP tool's run lands as one Compose sample in the host's accumulator, with no Mcp sample. - RunCustomViewPanel_RecordsNoMcpSample now sends the tool's spec argument. It used to send an argument the tool does not declare, and the unknown-argument guard refused that before the latency filter ran. - Darling only: Lite has no web server and no read-latency accumulator.
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.
Fixes #4782.
Why
Dev CI failed at d85dc2f, in the
Darling PG tests (2)job. One test failed:DarlingCustomViewsTests.CatalogDescriptors_Keys_EqualTheReadDispatchKeys, with "reads with no catalog descriptor:__test_statement_timeout". That route name belongs to another test class. It is not a real read.ReadLatencyWebRecordingTests.ADispatchEntryThatThrowsA57014PostgresException_RecordsOneWebSample_WithOutcomeTimeoutregisters one extra/api/read/*dispatch entry throughDarlingWebEndpoints.s_testOnlyExtraDispatchEntry, andBuildReadDispatch()adds that entry whenever it is set. That was a plain static field, so it is process-wide. The class that sets it is in thelive-postgrescollection, so it only runs on the PostgreSQL CI jobs. About ten other test classes callBuildReadDispatch()outside that collection, so they can run at the same time, and any of them that builds its dispatch while the entry is set sees the extra route.DarlingCustomViewsTestscompares the dispatch keys with the Custom Views catalog, so it fails when that happens. That is what failed on dev.What changes
DarlingWebEndpoints.cs: the field is nowprivate static readonly AsyncLocal<(string Name, ReadToolHandler Handler)?>, exposed through an internalTestOnlyExtraDispatchEntryproperty. Only the async flow that sets the entry, and what that flow starts or awaits, sees it.BuildReadDispatchreads the property. The seam is still null in every production run, and nothing outsideDarling.Testsassigns it. This is the only product-file change, and it does nothing outside a test.ReadLatencyWebRecordingTests.cs: the scope class and the two doc references use the property. Every assertion is unchanged. The test builds its server after setting the entry, in the same async method, so its own server still registers the route.DarlingWebEndpointsTests.cs: a new test,TheTestOnlyExtraDispatchEntry_IsSeenBySameFlow_AndNotByAFlowThatDoesNotInheritIt. It sets the entry under a unique route name and assertsBuildReadDispatch()contains it in the same flow. It then runsBuildReadDispatch().ContainsKey(name)in a task started underExecutionContext.SuppressFlow()and asserts the answer is false. The entry is cleared in afinally.git grep -n "s_testOnlyExtraDispatchEntry" -- Darlingnow shows only the field and the property.Test plan
Total: 1, Failed: 1. The plant is not in the branch.Darling.Testsbuilds with0 Warning(s),0 Error(s).DarlingWebEndpointsTests,DarlingCustomViewsTestsandAsOfWindowAnchorTests, with noDARLING_TEST_PG:Total: 172, Failed: 0, Skipped: 0.ReadLatencyWebRecordingTestsagainst a throwaway local PostgreSQL 18.6 withDARLING_TEST_PGset:Total: 3, Failed: 0, Skipped: 0. The 57014 dispatch-entry test ran, so the test's own server still sees the entry.DARLING_TEST_PGset:Total: 175, Failed: 0, Skipped: 0.Darling.Testssuite, once, with noDARLING_TEST_PG:Total: 17199, Errors: 0, Failed: 0, Skipped: 1123, Not Run: 1. The skips are the live PostgreSQL classes, which needDARLING_TEST_PG. The one not-run test is an explicit-only test, which the runner does not start unless asked.ReadLatencyWebRecordingTestswere not run locally; CI's PostgreSQL jobs run them.CHANGELOG
SECTION: None
Test-only. The one product-file change is a test seam that is null in every production run.