From 99bc7916e518382712978023e591783cd923577b Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 31 Aug 2026 08:54:17 +0100 Subject: [PATCH 1/2] Clean up integration test resources on shutdown --- .../ControllerTestsBase.cs | 13 ++++- .../IntegrationTestEnvironment.cs | 47 +++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs index 7733496..1577b67 100644 --- a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs +++ b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs @@ -55,7 +55,18 @@ public virtual async ValueTask InitializeAsync() await this.SetupStandingData(); } - public virtual ValueTask DisposeAsync() => default; + public virtual async ValueTask DisposeAsync() + { + this.Dispose(); + + if (this.context != null) + { + await this.context.DisposeAsync(); + } + + this.Client?.Dispose(); + this.factory?.Dispose(); + } protected EstateManagementContext context; diff --git a/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs index e84becc..3f8e08c 100644 --- a/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs +++ b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.Runtime.Loader; using System.Threading; using System.Threading.Tasks; using Shared.IntegrationTesting; @@ -12,6 +14,8 @@ internal sealed class IntegrationTestEnvironment { private static readonly SemaphoreSlim InitialisationLock = new(1, 1); private static IntegrationTestEnvironment? Instance; + private static readonly string ScenarioName = $"IntegrationTests-{Process.GetCurrentProcess().Id}-{Guid.NewGuid():N}"; + private static bool CleanupRegistered; private IntegrationTestEnvironment(TestDockerHelper dockerHelper, int sqlPort) { @@ -38,15 +42,16 @@ public static async Task GetAsync() return Instance; } - string scenarioName = "IntegrationTests"; + RegisterCleanupHandlers(); + NlogLogger logger = new(); - logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName); + logger.Initialise(LogManager.GetLogger(ScenarioName), ScenarioName); LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly); TestDockerHelper dockerHelper = new(); dockerHelper.Logger = logger; - await dockerHelper.StartContainersForScenarioRun(scenarioName, DockerServices.SqlServer); + await dockerHelper.StartContainersForScenarioRun(ScenarioName, DockerServices.SqlServer); int sqlPort = dockerHelper.GetSqlPort() ?? throw new InvalidOperationException("SQL Server container did not expose port 1433."); @@ -59,4 +64,40 @@ public static async Task GetAsync() InitialisationLock.Release(); } } + + private static void RegisterCleanupHandlers() + { + if (CleanupRegistered) + { + return; + } + + CleanupRegistered = true; + AppDomain.CurrentDomain.ProcessExit += (_, _) => Cleanup().GetAwaiter().GetResult(); + AssemblyLoadContext.Default.Unloading += _ => Cleanup().GetAwaiter().GetResult(); + } + + private static async Task Cleanup() + { + if (Instance == null) + { + return; + } + + await InitialisationLock.WaitAsync().ConfigureAwait(false); + try + { + if (Instance == null) + { + return; + } + + await Instance.DockerHelper.StopContainersForScenarioRun(DockerServices.None).ConfigureAwait(false); + Instance = null; + } + finally + { + InitialisationLock.Release(); + } + } } From fdef65c72d05486c872902da9c86fa9de8284e46 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 31 Aug 2026 10:15:56 +0100 Subject: [PATCH 2/2] Safely clean up integration test containers during shutdown --- .../IntegrationTestEnvironment.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs index 3f8e08c..edbda16 100644 --- a/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs +++ b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs @@ -73,11 +73,11 @@ private static void RegisterCleanupHandlers() } CleanupRegistered = true; - AppDomain.CurrentDomain.ProcessExit += (_, _) => Cleanup().GetAwaiter().GetResult(); - AssemblyLoadContext.Default.Unloading += _ => Cleanup().GetAwaiter().GetResult(); + AppDomain.CurrentDomain.ProcessExit += (_, _) => CleanupSafely().GetAwaiter().GetResult(); + AssemblyLoadContext.Default.Unloading += _ => CleanupSafely().GetAwaiter().GetResult(); } - private static async Task Cleanup() + private static async Task CleanupSafely() { if (Instance == null) { @@ -92,11 +92,18 @@ private static async Task Cleanup() return; } - await Instance.DockerHelper.StopContainersForScenarioRun(DockerServices.None).ConfigureAwait(false); - Instance = null; + try + { + await Instance.DockerHelper.StopContainersForScenarioRun(DockerServices.None).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger(ScenarioName).Error(ex, "Failed to stop integration test containers during shutdown."); + } } finally { + Instance = null; InitialisationLock.Release(); } }