using Microsoft.Data.SqlClient; using Testcontainers.MsSql; namespace SQLInsight.IntegrationTests; /// /// One SQL Server container for the test run, with the agent login set up by the real least-privilege script. /// The image is chosen with SQLINSIGHT_MSSQL_IMAGE so CI can run the same tests against 2017–2025. /// public sealed class SqlServerFixture : IAsyncLifetime { public const string DefaultImage = "mcr.microsoft.com/mssql/server:2022-latest"; public const string AgentLogin = "sqlinsight_agent"; public const string AgentPassword = "Agent-Pa55word!"; public const string TestDatabase = "SQLInsightQsTest"; public static string Image => Environment.GetEnvironmentVariable("SQLINSIGHT_MSSQL_IMAGE") ?? DefaultImage; private readonly MsSqlContainer _container = new MsSqlBuilder(Image).Build(); public string SaConnectionString => _container.GetConnectionString(); public string Server => new SqlConnectionStringBuilder(SaConnectionString).DataSource; public string SaPassword => new SqlConnectionStringBuilder(SaConnectionString).Password; public string AgentConnectionString => new SqlConnectionStringBuilder(SaConnectionString) { UserID = AgentLogin, Password = AgentPassword, InitialCatalog = "master", }.ConnectionString; public string DatabaseConnectionString(string database) => new SqlConnectionStringBuilder(SaConnectionString) { InitialCatalog = database }.ConnectionString; public async Task InitializeAsync() { await _container.StartAsync(); await ExecuteAsSaAsync($""" CREATE LOGIN [{AgentLogin}] WITH PASSWORD = N'{AgentPassword}', CHECK_POLICY = OFF; CREATE DATABASE [{TestDatabase}]; """); await ExecuteAsSaAsync($""" USE [{TestDatabase}]; ALTER DATABASE [{TestDatabase}] SET QUERY_STORE = ON; CREATE TABLE dbo.CustomerData (Id int NOT NULL PRIMARY KEY, Secret nvarchar(50) NOT NULL); INSERT dbo.CustomerData VALUES (1, N'fortroligt'), (2, N'hemmeligt'); """); await ExecuteAsSaAsync(await GrantScriptAsync()); } public static async Task GrantScriptAsync() { var script = await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Scripts", "grant-least-privilege.sql")); return script.Replace(@"N'DOMÆNE\svc-sqlinsight$'", $"N'{AgentLogin}'", StringComparison.Ordinal); } public Task DisposeAsync() => _container.DisposeAsync().AsTask(); public async Task ExecuteAsSaAsync(string sql) { await using var connection = new SqlConnection(SaConnectionString); await connection.OpenAsync(); await using var command = connection.CreateCommand(); command.CommandText = sql; command.CommandTimeout = 120; await command.ExecuteNonQueryAsync(); } } [CollectionDefinition(Name)] public sealed class SqlServerTestGroup : ICollectionFixture { public const string Name = "SQL Server"; }