From 649dd9537536853f87598858930345e520fd6cda Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 21 Aug 2026 13:17:52 +0200 Subject: [PATCH] fix(test): stop parallel tests racing the shared Bogus Noda cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NaiveFixture held one static Faker, and f.Noda() caches its dataset in that Faker's Bogus.Premium context — a plain Dictionary. TUnit runs tests in parallel, so several could reach the cache on its first, still-empty use and corrupt it, failing the suite with "Operations that change non-concurrent collections must have exclusive access" from inside Bogus. Builds the Faker per call instead, so each generation gets its own context. That is already how DomainFixture is written in the Persistence, Redis, KurrentDB and MongoDB suites, which is why none of them flake. Measured with a standalone harness of 200 cold rounds, 32 threads each: the shared-instance shape failed 70 rounds, the per-call shape none. Fixes #566 Co-Authored-By: Claude Opus 5 --- src/Core/test/Eventuous.Tests/Fixtures/NaiveFixture.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Core/test/Eventuous.Tests/Fixtures/NaiveFixture.cs b/src/Core/test/Eventuous.Tests/Fixtures/NaiveFixture.cs index c53a5cdcd..99c013eb3 100644 --- a/src/Core/test/Eventuous.Tests/Fixtures/NaiveFixture.cs +++ b/src/Core/test/Eventuous.Tests/Fixtures/NaiveFixture.cs @@ -8,7 +8,10 @@ namespace Eventuous.Tests.Fixtures; public class NaiveFixture { protected IEventStore EventStore { get; } = new InMemoryEventStore(); - static readonly Faker Faker = new Faker() + // A fresh Faker per call rather than one shared instance: f.Noda() caches its dataset in that + // Faker's Bogus.Premium context, a plain Dictionary, so tests running in parallel would race + // its first write and corrupt it. Same shape as DomainFixture in the other test suites. + static Faker Faker => new Faker() .CustomInstantiator( f => { var checkin = f.Noda().LocalDate.Soon();