From 48a3deeac98f191182518c35901529ebf268dcfd Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Mon, 31 Aug 2026 08:41:31 +0100 Subject: [PATCH] Update dependencies and reuse shared SQL integration environment --- Directory.Packages.props | 26 ++++---- .../ControllerTestsBase.cs | 26 +++----- .../IntegrationTestEnvironment.cs | 62 +++++++++++++++++++ 3 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 0b136cb..4387e8e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,23 +13,23 @@ - + - - - + + + - + - - - + + + - + @@ -42,14 +42,14 @@ - - + + - + - + \ No newline at end of file diff --git a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs index 1117904..7733496 100644 --- a/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs +++ b/EstateReportingAPI.IntegrationTests/ControllerTestsBase.cs @@ -28,7 +28,7 @@ public abstract class ControllerTestsBase : IAsyncLifetime protected Dictionary> contractProducts; protected DatabaseHelper helper; protected ITestOutputHelper TestOutputHelper; - protected DockerHelper DockerHelper; + protected Int32 SqlPort; protected static void AssertDecimalMatchesTo4Places(decimal actual, decimal expected) { @@ -38,16 +38,10 @@ protected static void AssertDecimalMatchesTo4Places(decimal actual, decimal expe public virtual async ValueTask InitializeAsync() { - this.TestId = Guid.NewGuid(); - String scenarioName = this.TestId.ToString(); - NlogLogger logger = new NlogLogger(); - logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName); - LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly); + IntegrationTestEnvironment environment = await IntegrationTestEnvironment.GetAsync(); + this.SqlPort = environment.SqlPort; - this.DockerHelper = new TestDockerHelper(); - this.DockerHelper.Logger = logger; - - await this.DockerHelper.StartContainersForScenarioRun(scenarioName, DockerServices.SqlServer); + this.TestId = Guid.NewGuid(); String dbConnString = GetLocalConnectionString($"TransactionProcessorReadModel-{this.TestId}"); @@ -61,10 +55,7 @@ public virtual async ValueTask InitializeAsync() await this.SetupStandingData(); } - public virtual async ValueTask DisposeAsync() - { - await this.DockerHelper.StopContainersForScenarioRun(DockerServices.None); - } + public virtual ValueTask DisposeAsync() => default; protected EstateManagementContext context; @@ -112,10 +103,7 @@ public virtual async ValueTask DisposeAsync() public static (String usename, String password) SqlCredentials = ("sa", "thisisalongpassword123!"); public String GetLocalConnectionString(String databaseName) { - var dockerHelper = this.DockerHelper as TestDockerHelper; - Int32? databaseHostPort = dockerHelper.GetSqlPort(); - - return $"server=localhost,{databaseHostPort};database={databaseName};user id={SqlCredentials.usename};password={SqlCredentials.password};Encrypt=false"; + return $"server=localhost,{this.SqlPort};database={databaseName};user id={SqlCredentials.usename};password={SqlCredentials.password};Encrypt=false"; } //internal async Task StartSqlContainer(){ @@ -169,7 +157,7 @@ public override async Task CreateSubscriptions(){ } public Int32? GetSqlPort() { - var sqlContainer = this.Containers.SingleOrDefault(c => c.Item1 == DockerServices.SqlServer); + var sqlContainer = this.Containers.FirstOrDefault(c => c.Item1 == DockerServices.SqlServer); if (sqlContainer == default) { return null; } diff --git a/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs new file mode 100644 index 0000000..e84becc --- /dev/null +++ b/EstateReportingAPI.IntegrationTests/IntegrationTestEnvironment.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Shared.IntegrationTesting; +using Shared.IntegrationTesting.TestContainers; +using Shared.Logger; +using NLog; + +namespace EstateReportingAPI.IntegrationTests; + +internal sealed class IntegrationTestEnvironment +{ + private static readonly SemaphoreSlim InitialisationLock = new(1, 1); + private static IntegrationTestEnvironment? Instance; + + private IntegrationTestEnvironment(TestDockerHelper dockerHelper, int sqlPort) + { + this.DockerHelper = dockerHelper; + this.SqlPort = sqlPort; + } + + public TestDockerHelper DockerHelper { get; } + + public int SqlPort { get; } + + public static async Task GetAsync() + { + if (Instance != null) + { + return Instance; + } + + await InitialisationLock.WaitAsync().ConfigureAwait(false); + try + { + if (Instance != null) + { + return Instance; + } + + string scenarioName = "IntegrationTests"; + NlogLogger logger = new(); + logger.Initialise(LogManager.GetLogger(scenarioName), scenarioName); + LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly); + + TestDockerHelper dockerHelper = new(); + dockerHelper.Logger = logger; + + await dockerHelper.StartContainersForScenarioRun(scenarioName, DockerServices.SqlServer); + + int sqlPort = dockerHelper.GetSqlPort() + ?? throw new InvalidOperationException("SQL Server container did not expose port 1433."); + + Instance = new IntegrationTestEnvironment(dockerHelper, sqlPort); + return Instance; + } + finally + { + InitialisationLock.Release(); + } + } +}