diff --git a/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs b/src/StrongTypes.Api.IntegrationTests/Infrastructure/TestWebApplicationFactory.cs index e2f70a9..36b2cb3 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(45); + 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) =>