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..edbda16 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,47 @@ public static async Task GetAsync() InitialisationLock.Release(); } } + + private static void RegisterCleanupHandlers() + { + if (CleanupRegistered) + { + return; + } + + CleanupRegistered = true; + AppDomain.CurrentDomain.ProcessExit += (_, _) => CleanupSafely().GetAwaiter().GetResult(); + AssemblyLoadContext.Default.Unloading += _ => CleanupSafely().GetAwaiter().GetResult(); + } + + private static async Task CleanupSafely() + { + if (Instance == null) + { + return; + } + + await InitialisationLock.WaitAsync().ConfigureAwait(false); + try + { + if (Instance == null) + { + return; + } + + 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(); + } + } }