From eebe906da5673619f9f50543af16fa1e213269ec Mon Sep 17 00:00:00 2001 From: KaliCZ Date: Thu, 28 May 2026 23:56:36 +0200 Subject: [PATCH 1/2] Fail fast when a test container does not start Wrap each Testcontainer start in a 120s timeout instead of letting the default wait strategy poll indefinitely. On timeout, throw a TimeoutException naming the container and pointing at its logs, so a container that comes up but never accepts connections produces a clear failure rather than an indefinite hang. Co-Authored-By: Claude Opus 4.8 --- .../TestWebApplicationFactory.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs b/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs index e2f70a9..aa26921 100644 --- a/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs +++ b/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs @@ -1,3 +1,4 @@ +using DotNet.Testcontainers.Containers; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Configuration; @@ -19,6 +20,8 @@ public sealed class TestWebApplicationFactory : WebApplicationFactory, private const string DockerGroupLabel = "com.docker.compose.project"; private const string DockerGroupName = "StrongTypes"; + private static readonly TimeSpan ContainerStartTimeout = TimeSpan.FromSeconds(120); + private readonly MsSqlContainer _sqlContainer = new MsSqlBuilder() .WithLabel(DockerGroupLabel, DockerGroupName) .Build(); @@ -31,7 +34,9 @@ async ValueTask IAsyncLifetime.InitializeAsync() { // Start both containers in parallel; containers must be up before the // host is built so that ConfigureAppConfiguration can read their connection strings. - await Task.WhenAll(_sqlContainer.StartAsync(), _pgContainer.StartAsync()); + await Task.WhenAll( + StartContainerAsync(_sqlContainer, "SQL Server"), + StartContainerAsync(_pgContainer, "PostgreSQL")); // Accessing Services triggers the lazy host build. using var scope = Services.CreateScope(); @@ -40,6 +45,22 @@ async ValueTask IAsyncLifetime.InitializeAsync() await sp.GetRequiredService().Database.EnsureCreatedAsync(); } + private static async Task StartContainerAsync(IContainer container, string name) + { + using var cts = new CancellationTokenSource(ContainerStartTimeout); + try + { + await container.StartAsync(cts.Token); + } + catch (OperationCanceledException) when (cts.IsCancellationRequested) + { + throw new TimeoutException( + $"The {name} test container did not start within {ContainerStartTimeout.TotalSeconds:0}s. " + + "It either failed to start or never began accepting connections — check the container logs. " + + "On ARM64 hosts this can also happen when the image has no native ARM build, as the emulated process may crash on startup."); + } + } + protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((_, config) => From f72b4d0c82054b2dee10716d673a11844c086d64 Mon Sep 17 00:00:00 2001 From: KaliCZ Date: Thu, 28 May 2026 23:59:08 +0200 Subject: [PATCH 2/2] Lower container start timeout to 45s 45s sits comfortably above SQL Server's real cold-boot time while failing fast on a genuine hang, without risking flaky CI the way a tighter ceiling would. Co-Authored-By: Claude Opus 4.8 --- .../Infrastructure/TestWebApplicationFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs b/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs index aa26921..36b2cb3 100644 --- a/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs +++ b/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs @@ -20,7 +20,7 @@ public sealed class TestWebApplicationFactory : WebApplicationFactory, private const string DockerGroupLabel = "com.docker.compose.project"; private const string DockerGroupName = "StrongTypes"; - private static readonly TimeSpan ContainerStartTimeout = TimeSpan.FromSeconds(120); + private static readonly TimeSpan ContainerStartTimeout = TimeSpan.FromSeconds(45); private readonly MsSqlContainer _sqlContainer = new MsSqlBuilder() .WithLabel(DockerGroupLabel, DockerGroupName)