Repository navigation
Extensions.AspNetCore.Tests: flaky ObjectDisposedException from UseInboxHandler<T> static logger #4565
Description
Symptom
tests/Paramore.Brighter.Extensions.AspNetCore.Tests fails intermittently. Each failing run loses 1–6 tests, and a rerun often passes 65/65. These tests fail most often:
RequestScopeRegistrationEntryPointTests.When_add_consumers_func_alone_calls_the_extension_the_affinity_should_reach_the_options_and_adoption_should_workRequestScopeRegistrationEntryPointTests.When_add_consumers_action_alone_calls_the_extension_the_affinity_should_reach_the_options_and_adoption_should_workRequestScopeRegistrationEntryPointTests.When_add_consumers_func_already_sets_join_ambient_the_extensions_always_new_should_still_winRequestScopeRegistrationEntryPointTests.When_add_consumers_action_already_sets_join_ambient_the_extensions_always_new_should_still_winPackageReferencedButExtensionNotCalledTests.When_the_package_is_referenced_but_the_extension_is_not_called_the_accessor_should_not_be_touched(fails withConfigurationException: Error when building pipeline)
System.TypeInitializationException : The type initializer for 'Paramore.Brighter.Inbox.Handlers.UseInboxHandler`1' threw an exception.
---- System.ObjectDisposedException : Cannot access a disposed object.
Reproduced on unmodified master (f2b93125a): of three runs on each of net9.0 and net10.0, one passed, and the other two failed 3–5 tests per framework.
Cause
This is the race that tests/Paramore.Brighter.Extensions.AspNetCore.Tests/Initializer.cs already describes in its remarks:
- Each
PlaceOrderWebApplicationFactoryhost reassignsApplicationLogging.LoggerFactoryto its ownILoggerFactory, then disposes that factory when the test ends. - A closed generic Brighter type initialises its static
ILoggeronce, on first touch, against whatever factory the property holds at that moment. - If that is a factory a finished test has already disposed, the type initializer throws
ObjectDisposedException.
The [ModuleInitializer] avoids this by warming every closed type the tests use. It has 52 RunClassConstructor calls, but none for UseInboxHandler<T>. The consumer pipelines in these tests add that handler implicitly, so its closed types are first touched at whatever moment a test happens to build one.
Suggested fix
In Initializer.Initialize(), add RuntimeHelpers.RunClassConstructor(typeof(UseInboxHandler<T>).TypeHandle) for each request type whose consumer pipeline gets the inbox handler, and the async variant too if those pipelines use it. Then check for any other implicitly added handler types that are also missing.
Found while verifying #4562.
Activity
fix: stabilize ASP.NET Core test logger initialization