Skip to content

Give the test-host scope one owner: only the newest live test host runs its restartable services, the reset covers every live host, and duplicate connection string keys fail fast - #430

Merged
Vulthil merged 1 commit into
mainfrom
refactor/test-host-scope
Oct 2, 2026
Merged

Vulthil merged 1 commit into
mainfrom
refactor/test-host-scope

Conversation

@Vulthil

@Vulthil Vulthil commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Summary

The test-host scope now has one owner. Before, BaseWebApplicationFactory held the whole scope lifecycle with no in-process test surface, and the reset paused only the host the caller passed in. A test that registers a mock runs on a derived host while the class's shared host stays alive on the same per-class databases and virtual hosts, so the shared host's outbox relay kept running during that test and during the database reset (contrary to testing.md). Since the previous PR made the message consumers restartable, the same gap also meant two hosts consuming the same queues.

  • TestHostScope (new, internal, Vulthil.xUnit) owns:
    • the scope views of the consumed containers and their connection strings;
    • the start order: migrations, then startup resources, before the application's own hosted services;
    • the set of live hosts: a hosted service at the front of every host (its own and each WithWebHostBuilder host) joins the host when it starts and leaves when it stops;
    • the reset between tests, and the disposal of the views.
  • One rule for live hosts: only the newest live host runs its IRestartableHostedServices. When a host starts, the scope pauses the restartable services of the other live hosts; when the newest host stops, the host before it resumes. A double stop of the same host (WebApplicationFactory stops a disposed host twice) is a no-op.
  • The reset pauses the restartable services of every running live host (a paused host stays paused), resets the scope's views and the HTTP mocks with the newest host's services (the host the test ran on), and resumes what it paused. With no live host it does nothing, so a test that built no host never builds one just to reset it. BaseWebApplicationFactory.ResetAsync (internal) no longer takes a host argument.
  • Duplicate connection-string keys among the consumed containers now make InitializeAsync throw, naming the key and the containers (keys compare case-insensitively, like configuration keys). Before, one connection string silently overwrote the other, and which one won was not defined. The check runs on the scope views, which are what reach the host, before they provision any database or virtual host.
  • BaseWebApplicationFactory is a thin adapter over the scope. Its public and protected members do not change. The internal ITestHostMigrator and TestMigrationHostedService are gone.
  • TestHostReset stops and starts services from several hosts, and the scope uses it for pauses and resumes too, with the same per-step timeouts and the same aggregated failure reporting.
  • Docs (testing.md): the per-test host pauses the shared host; the reset covers every live host; a scope lives for one test class, so a message one test leaves in a queue reaches the next test; duplicate keys fail.

Behavior change (2.0 line): a test suite whose consumed containers share a connection string key now fails at fixture initialization instead of running against one of them. No public API change, so no compatibility suppressions.

Verification

  • dotnet build Vulthil.SharedKernel.slnx: 0 warnings, 0 errors.
  • All 18 unit test projects pass on net10.0 and net9.0. Vulthil.xUnit.Tests has 12 new in-process TestHostScopeTests (container filter and one scope id, run-once, connection strings, duplicate keys incl. case and an excluded container, start order, pause and resume, an older host stopping first, reset of the running host with the newest host's services, no live host, a failed pause, disposal), and the 8 TestHostResetTests run on the new signature.
  • Vulthil.IntegrationTests (Docker): 70/70 on net10.0 and net9.0, including the new LiveTestHostPauseTests: the shared host's probe stops when the test's host starts, stays stopped through the reset, and starts again after the test host stops. RestartableProbe now tags each event with its host, and FixtureResetRoutingTests asserts that every event belongs to the one host the test built.
  • Aspire MessagingIntegrationTests: 13/13 on net10.0.
  • dotnet pack package validation against 1.2.0 passes for Vulthil.xUnit.
  • Break checks (each made tests fail, then reverted):
    • the scope never pauses the other hosts: 4 unit tests and the Docker test fail;
    • the scope never resumes: 1 test fails;
    • the reset also restarts paused hosts: 2 tests fail;
    • the duplicate-key check misses pairs: 1 test fails;
    • keys compare case-sensitively: 1 test fails;
    • resources reset with the oldest host: 1 test fails.

Backport to v1.0: no

…ns its restartable services, the reset covers every live host, and duplicate connection string keys fail fast
@Vulthil
Vulthil merged commit 5e15355 into main Oct 2, 2026
7 checks passed
@Vulthil
Vulthil deleted the refactor/test-host-scope branch October 2, 2026 12:18
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.

1 participant