yavsc/src/Yavsc.Tests.Shared/InMemoryDatabaseName.cs

30 lines
1.4 KiB
C#
Raw Normal View History

test(org): isolate in-memory store per fixture TestWebApplicationFactory instances shared the same in-memory database because EF Core's UseInMemoryDatabase("InMemory") returns the same backing store to every DbContext that asks for it under the same connection string, in the same process. Whichever fixture started first defined the state, and every subsequent fixture inherited it, making tests silently order-dependent and flaky. Fix: - Yavsc.Tests.Shared/InMemoryDatabaseName: helper that suffixes the in-memory connection string with a per-fixture GUID. - TestWebApplicationFactory: instance GUID + ConnectionStrings__ YavscConnection set as an environment variable in the constructor and cleared in Dispose, so each factory gets its own backing store. Env var is needed because IdentityServer8.EntityFramework exposes ConfigureDbContext as Action<DbContextOptionsBuilder> with no service-provider access, so the connection string is captured at registration time. AddEnvironmentVariables is the last provider in the config pipeline and wins regardless. - WebServerFixture: process-static GUID (WebHostFixture is a per-process singleton by design, so the test collection shares one store; the GUID still isolates from TestWebApplicationFactory). - AddIdentityDBAndStores: read the connection string at DbContext construction time via the (sp, options) overload of AddDbContext, so test fixtures can override it via the host's IConfiguration. IdentityServer stores cannot do the same without subclassing the framework's DbContexts; the env var path is the documented escape hatch in HostingExtensions.AddIdentityServer. - UsesInMemoryProvider: StartsWith instead of equality, so 'InMemory-{guid}' is still recognised as an in-memory connection string. Regression sentinel in Controllers/TestWebApplicationFactoryIsolationTests: two factories seed a marker client in the first, the second must not see it. Suite: 45/45 over 3 stable runs, 13-15s each.
2026-08-22 04:36:52 +01:00
namespace Yavsc.Tests.Shared;
/// <summary>
/// Helpers for the in-memory connection string used by test fixtures.
///
/// EF Core's <c>UseInMemoryDatabase(name)</c> returns the same backing
/// store to every <c>DbContext</c> that asks for it under the same
/// <paramref name="name"/>, in the same process. That means every
/// fixture that uses the bare <c>"InMemory"</c> connection string
/// shares the same in-memory database — which leaks state between
/// fixtures that are supposed to be independent, and silently makes
/// tests order-dependent.
///
/// The fix is to give each fixture its own suffix. <see cref="For"/>
/// returns a stable, fixture-scoped connection string. The fixture
/// stores the suffix in an instance field so successive calls within
/// the same fixture always resolve to the same database.
/// </summary>
public static class InMemoryDatabaseName
{
/// <summary>Base connection string for the in-memory provider,
/// as it appears in <c>appsettings-org.Testing.json</c>.</summary>
public const string Base = "InMemory";
/// <summary>Builds a per-fixture connection string. Two calls
/// with the same <paramref name="fixtureId"/> return the same
/// string; two calls with different ids return different
/// strings, isolating the underlying in-memory stores.</summary>
public static string For(string fixtureId) => $"{Base}-{fixtureId}";
}