diff --git a/backend/Menu.AppHost/Menu.AppHost.csproj b/backend/Menu.AppHost/Menu.AppHost.csproj index 0d9d0193..172a82cd 100644 --- a/backend/Menu.AppHost/Menu.AppHost.csproj +++ b/backend/Menu.AppHost/Menu.AppHost.csproj @@ -1,4 +1,4 @@ - + Exe @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/backend/Menu.MigrationService/Menu.MigrationService.csproj b/backend/Menu.MigrationService/Menu.MigrationService.csproj index 88089d98..360fe491 100644 --- a/backend/Menu.MigrationService/Menu.MigrationService.csproj +++ b/backend/Menu.MigrationService/Menu.MigrationService.csproj @@ -9,7 +9,7 @@ - + diff --git a/backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs b/backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs index eb6ea3b3..16ec3613 100644 --- a/backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs +++ b/backend/MenuApi.Integration.Tests/Factory/ApiTestFixture.cs @@ -19,7 +19,7 @@ public async Task GetHttpClient() { var httpClient = app.CreateHttpClient("apiservice"); - cachedAuthHeader ??= await new ApiAuthentication().GetAuthenticationHeaderValue(); + cachedAuthHeader ??= await new ApiAuthentication().GetAuthenticationHeaderValue().ConfigureAwait(false); httpClient.DefaultRequestHeaders.Authorization = cachedAuthHeader; return httpClient; @@ -45,7 +45,31 @@ async ValueTask IAsyncLifetime.InitializeAsync() app = await appHost.BuildAsync(); - await app.StartAsync(); + // Retry app startup to handle Docker daemon health checks in CI environments + const int maxRetries = 3; + const int delayMs = 2000; + Exception lastException = null; + for (int attempt = 1; attempt <= maxRetries; attempt++) + { + try + { + await app.StartAsync().ConfigureAwait(false); + break; + } + catch (Exception ex) when (ex.Message.Contains("unhealthy")) + { + lastException = ex; + if (attempt < maxRetries) + { + await Task.Delay(delayMs).ConfigureAwait(false); + } + } + } + + if (lastException != null) + { + throw lastException; + } var resourceNotificationService = app.Services .GetRequiredService(); @@ -53,18 +77,18 @@ await resourceNotificationService.WaitForResourceAsync( "migrations", KnownResourceStates.Finished ) - .WaitAsync(TimeSpan.FromSeconds(120)); + .WaitAsync(TimeSpan.FromSeconds(120)).ConfigureAwait(false); await resourceNotificationService.WaitForResourceAsync( "apiservice", KnownResourceStates.Running ) - .WaitAsync(TimeSpan.FromSeconds(30)); + .WaitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false); } async ValueTask IAsyncDisposable.DisposeAsync() { - await app.StopAsync(); - await app.DisposeAsync(); + await app.StopAsync().ConfigureAwait(false); + await app.DisposeAsync().ConfigureAwait(false); } } @@ -102,7 +126,7 @@ public static async Task ShouldHaveStatusCode(this HttpResponseMessage response, { if (response.StatusCode != expected) { - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new Xunit.Sdk.XunitException( $"Expected status code {(int)expected} {expected} but received {(int)response.StatusCode} {response.StatusCode}.\n\nResponse body:\n{body}"); } diff --git a/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj b/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj index 9a68402d..e4c4e915 100644 --- a/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj +++ b/backend/MenuApi.Integration.Tests/MenuApi.Integration.Tests.csproj @@ -21,7 +21,7 @@ - + diff --git a/backend/MenuApi/MenuApi.csproj b/backend/MenuApi/MenuApi.csproj index 6521bb12..7ed46443 100644 --- a/backend/MenuApi/MenuApi.csproj +++ b/backend/MenuApi/MenuApi.csproj @@ -30,7 +30,7 @@ - +