Background
StrongTypes.Api.IntegrationTests is currently one project whose test classes are all tagged [Collection("Integration")]. That collection shares a single TestWebApplicationFactory, which in InitializeAsync boots both a SQL Server and a PostgreSQL Testcontainer and runs EnsureCreatedAsync on both before any test runs.
Consequence: every test in the project transitively depends on both containers starting — even the tests that touch no database at all.
The problem
On a host that cannot run the mcr.microsoft.com/mssql/server image (it is amd64-only; on ARM64 such as a Snapdragon dev box it starts under emulation and sqlservr segfaults), the shared fixture never becomes ready, so the entire project is unrunnable locally — not just the SQL-Server-specific assertions. See #102 for the stop-gap (a 45s fail-fast timeout) that turns the hang into a fast, clear failure but does not make any of these tests runnable on such a host.
The other two integration projects (StrongTypes.OpenApi.IntegrationTests, StrongTypes.AspNetCore.IntegrationTests) use no containers and already run fine locally — only StrongTypes.Api.IntegrationTests is affected.
What this project currently checks
For reference (so the split preserves coverage), the project verifies three distinct concerns:
Tests/ApiTests/ — full wire→DB round-trip for each strong type: strings, emails, collections, and the entire numeric matrix (Positive / NonNegative / NonPositive / Negative × int / long / short / decimal / double / float). Each test POSTs then GETs and asserts persisted state on both SqlSet and PgSet, exercising the JSON converter + ASP.NET Core pipeline + EF Core value converter together. Needs both DBs.
Tests/ConverterTests/ — converter / MVC-filter behavior (strings, numerics, email MailAddress). Also persists to both DBs. Needs both DBs.
Tests/BindingTests/ — model binding from query / route / header / form, plus 400 / ValidationProblemDetails behavior. HTTP only — touches no database.
So today the binding tests (which need no DB) are blocked purely because they share the both-containers fixture.
Goal
A developer on a machine that cannot run SQL Server should be able to run everything except the SQL-Server-backed tests (binding tests + PostgreSQL round-trips), while CI retains full coverage on both providers.
Proposed direction
Split StrongTypes.Api.IntegrationTests into smaller projects, each with a fixture that boots only what it needs:
- Binding / no-DB project — boots no container; hosts the
BindingTests. Always runnable.
- PostgreSQL project — boots the PostgreSQL container only; runs the round-trip + converter tests against PostgreSQL. Runnable on ARM.
- SQL Server project — boots the SQL Server container only; runs the same logical tests against SQL Server. This is the one that cannot run on ARM and is skipped locally there.
Locally on an ARM box you run the first two; CI runs all three.
Open questions / considerations
- Shared test logic. The round-trip tests currently assert against both providers in a single method, and
IntegrationTestBase exposes SqlDb / PgDb / SqlSet / PgSet. To run once per provider, this needs to become provider-parametrized. Decide where shared code lives — a shared *.TestKit library, linked files, or a shared base class referenced by both provider projects.
- Container-less host build.
Program.cs registers both DbContexts. Registering a DbContext does not require a live connection (the only live access is the fixture's EnsureCreatedAsync), so a no-container fixture should be able to build the host and run binding tests by simply skipping EnsureCreated and supplying placeholder/empty connection strings — worth confirming during implementation.
- Alternative (smaller, less clean). Keep a single project but have the fixture start only the providers it can (capability detection or an env var) and
Assert.Skip the SQL Server assertions when SQL Server is unavailable. No project boundary, smaller diff, but mixes provider concerns and relies on runtime skipping.
- Docs.
testing.md is the single source of truth for test conventions and CLAUDE.md has a StrongTypes.Api — purpose section; both should be updated to describe the new layout.
Acceptance criteria
- On a host without SQL Server support,
dotnet test of the binding and PostgreSQL projects passes without hanging and without requiring SQL Server.
- The SQL Server project is independently runnable (and runs in CI).
- No loss of coverage vs today: both providers are still exercised in CI across the same set of strong types.
testing.md (and CLAUDE.md if needed) updated to document the new project layout.
References
Background
StrongTypes.Api.IntegrationTestsis currently one project whose test classes are all tagged[Collection("Integration")]. That collection shares a singleTestWebApplicationFactory, which inInitializeAsyncboots both a SQL Server and a PostgreSQL Testcontainer and runsEnsureCreatedAsyncon both before any test runs.Consequence: every test in the project transitively depends on both containers starting — even the tests that touch no database at all.
The problem
On a host that cannot run the
mcr.microsoft.com/mssql/serverimage (it is amd64-only; on ARM64 such as a Snapdragon dev box it starts under emulation andsqlservrsegfaults), the shared fixture never becomes ready, so the entire project is unrunnable locally — not just the SQL-Server-specific assertions. See #102 for the stop-gap (a 45s fail-fast timeout) that turns the hang into a fast, clear failure but does not make any of these tests runnable on such a host.The other two integration projects (
StrongTypes.OpenApi.IntegrationTests,StrongTypes.AspNetCore.IntegrationTests) use no containers and already run fine locally — onlyStrongTypes.Api.IntegrationTestsis affected.What this project currently checks
For reference (so the split preserves coverage), the project verifies three distinct concerns:
Tests/ApiTests/— full wire→DB round-trip for each strong type: strings, emails, collections, and the entire numeric matrix (Positive/NonNegative/NonPositive/Negative×int/long/short/decimal/double/float). Each test POSTs then GETs and asserts persisted state on bothSqlSetandPgSet, exercising the JSON converter + ASP.NET Core pipeline + EF Core value converter together. Needs both DBs.Tests/ConverterTests/— converter / MVC-filter behavior (strings, numerics, emailMailAddress). Also persists to both DBs. Needs both DBs.Tests/BindingTests/— model binding from query / route / header / form, plus 400 /ValidationProblemDetailsbehavior. HTTP only — touches no database.So today the binding tests (which need no DB) are blocked purely because they share the both-containers fixture.
Goal
A developer on a machine that cannot run SQL Server should be able to run everything except the SQL-Server-backed tests (binding tests + PostgreSQL round-trips), while CI retains full coverage on both providers.
Proposed direction
Split
StrongTypes.Api.IntegrationTestsinto smaller projects, each with a fixture that boots only what it needs:BindingTests. Always runnable.Locally on an ARM box you run the first two; CI runs all three.
Open questions / considerations
IntegrationTestBaseexposesSqlDb/PgDb/SqlSet/PgSet. To run once per provider, this needs to become provider-parametrized. Decide where shared code lives — a shared*.TestKitlibrary, linked files, or a shared base class referenced by both provider projects.Program.csregisters both DbContexts. Registering a DbContext does not require a live connection (the only live access is the fixture'sEnsureCreatedAsync), so a no-container fixture should be able to build the host and run binding tests by simply skippingEnsureCreatedand supplying placeholder/empty connection strings — worth confirming during implementation.Assert.Skipthe SQL Server assertions when SQL Server is unavailable. No project boundary, smaller diff, but mixes provider concerns and relies on runtime skipping.testing.mdis the single source of truth for test conventions andCLAUDE.mdhas aStrongTypes.Api — purposesection; both should be updated to describe the new layout.Acceptance criteria
dotnet testof the binding and PostgreSQL projects passes without hanging and without requiring SQL Server.testing.md(andCLAUDE.mdif needed) updated to document the new project layout.References
StrongTypesgroup in Docker Desktop