diff --git a/docker-compose.yml b/docker-compose.yml index 6ad21b6f6740..b4695ac37a2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,15 +35,6 @@ services: ports: - "3306" - postgres_arm64: - image: postgres:10.5-alpine - environment: - - POSTGRES_PASSWORD=postgres - - POSTGRES_USER=postgres - - POSTGRES_DB=postgres - ports: - - "5432" - rabbitmq_arm64: image: rabbitmq:3-management command: rabbitmq-server @@ -183,16 +174,6 @@ services: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - postgres: - image: postgres:10.5-alpine - profiles: ["group1"] - environment: - - POSTGRES_PASSWORD=postgres - - POSTGRES_USER=postgres - - POSTGRES_DB=postgres - ports: - - "127.0.0.1:5432:5432" - mysql: image: mysql/mysql-server:8.0 profiles: ["group1"] @@ -477,7 +458,6 @@ services: - ELASTICSEARCH6_HOST=elasticsearch6:9200 - ELASTICSEARCH5_HOST=elasticsearch5:9200 - SQLSERVER_CONNECTION_STRING=Server=sqlserver;User=sa;Password=Strong!Passw0rd;TrustServerCertificate=true - - POSTGRES_HOST=postgres - MYSQL_HOST=mysql - MYSQL_PORT=3306 - MYSQL57_HOST=mysql57 @@ -689,7 +669,6 @@ services: - stackexchangeredis-replica - stackexchangeredis-single - sqlserver - - postgres - mysql - mysql57 - rabbitmq @@ -699,7 +678,7 @@ services: - test-agent environment: - TIMEOUT_LENGTH=120 - command: servicestackredis:6379 stackexchangeredis:6379 stackexchangeredis-replica:6379 stackexchangeredis-single:6379 sqlserver:1433 postgres:5432 mysql:3306 mysql57:3306 rabbitmq:5672 kafka-broker:9092 kafka-zookeeper:2181 couchbase:11210 test-agent:8126 + command: servicestackredis:6379 stackexchangeredis:6379 stackexchangeredis-replica:6379 stackexchangeredis-single:6379 sqlserver:1433 mysql:3306 mysql57:3306 rabbitmq:5672 kafka-broker:9092 kafka-zookeeper:2181 couchbase:11210 test-agent:8126 StartDependencies.Group2: image: andrewlock/wait-for-dependencies @@ -729,6 +708,7 @@ services: command: dotnet /build/bin/Debug/_build.dll RunIntegrationTests volumes: - ./:/project + - /var/run/docker.sock:/var/run/docker.sock cap_add: - SYS_PTRACE environment: @@ -754,7 +734,6 @@ services: - ELASTICSEARCH6_HOST=elasticsearch7_arm64:9200 - ELASTICSEARCH5_HOST=elasticsearch7_arm64:9200 - SQLSERVER_CONNECTION_STRING=Server=sqledge_arm64;User=sa;Password=Strong!Passw0rd;TrustServerCertificate=true - - POSTGRES_HOST=postgres_arm64 - MYSQL_HOST=mysql_arm64 - MYSQL_PORT=3306 - RABBITMQ_HOST=rabbitmq_arm64 @@ -795,7 +774,6 @@ services: - elasticsearch7_arm64 - sqledge_arm64 - mongo_arm64 - - postgres_arm64 - mysql_arm64 - rabbitmq_arm64 - localstack_arm64 @@ -811,14 +789,13 @@ services: - elasticsearch7_arm64 - sqledge_arm64 - mongo_arm64 - - postgres_arm64 - mysql_arm64 - rabbitmq_arm64 - localstack_arm64 - test-agent environment: - TIMEOUT_LENGTH=120 - command: servicestackredis_arm64:6379 stackexchangeredis_arm64:6379 stackexchangeredis_arm64-replica:6379 stackexchangeredis_arm64-single:6379 elasticsearch7_arm64:9200 sqledge_arm64:1433 mongo_arm64:27017 postgres_arm64:5432 mysql_arm64:3306 rabbitmq_arm64:5672 localstack_arm64:4566 test-agent:8126 + command: servicestackredis_arm64:6379 stackexchangeredis_arm64:6379 stackexchangeredis_arm64-replica:6379 stackexchangeredis_arm64-single:6379 elasticsearch7_arm64:9200 sqledge_arm64:1433 mongo_arm64:27017 mysql_arm64:3306 rabbitmq_arm64:5672 localstack_arm64:4566 test-agent:8126 IntegrationTests.ARM64.Debugger: build: @@ -830,6 +807,7 @@ services: command: dotnet /build/bin/Debug/_build.dll RunDebuggerIntegrationTests volumes: - ./:/project + - /var/run/docker.sock:/var/run/docker.sock cap_add: - SYS_PTRACE environment: @@ -1154,13 +1132,12 @@ services: - elasticsearch7_osx_arm64 - sqledge_osx_arm64 - mongo_osx_arm64 - - postgres_osx_arm64 - mysql_osx_arm64 - rabbitmq_osx_arm64 - localstack_osx_arm64 environment: - TIMEOUT_LENGTH=120 - command: servicestackredis_osx_arm64:6379 stackexchangeredis_osx_arm64:6379 stackexchangeredis_osx_arm64-replica:6379 stackexchangeredis_osx_arm64-single:6379 elasticsearch7_osx_arm64:9200 sqledge_osx_arm64:1433 mongo_osx_arm64:27017 postgres_osx_arm64:5432 mysql_osx_arm64:3306 rabbitmq_osx_arm64:5672 localstack_osx_arm64:4566 + command: servicestackredis_osx_arm64:6379 stackexchangeredis_osx_arm64:6379 stackexchangeredis_osx_arm64-replica:6379 stackexchangeredis_osx_arm64-single:6379 elasticsearch7_osx_arm64:9200 sqledge_osx_arm64:1433 mongo_osx_arm64:27017 mysql_osx_arm64:3306 rabbitmq_osx_arm64:5672 localstack_osx_arm64:4566 # OSX ARM64 dependencies @@ -1201,15 +1178,6 @@ services: ports: - "3306:3306" - postgres_osx_arm64: - image: postgres:10.5-alpine - environment: - - POSTGRES_PASSWORD=postgres - - POSTGRES_USER=postgres - - POSTGRES_DB=postgres - ports: - - "5432:5432" - rabbitmq_osx_arm64: image: rabbitmq:3-management command: rabbitmq-server @@ -1252,6 +1220,12 @@ services: - ACCEPT_EULA=Y - SA_PASSWORD=Strong!Passw0rd +### PRE PULL TEST CONTAINER IMAGES BELOW HERE TO GET THEM INTO THE VM TO MAKE IT GO FASTER ### + # keep syncronized image version with tracer\test\Datadog.Trace.TestHelpers.AutoInstrumentation\Containers\AerospikeFixture.cs aerospike: image: aerospike/aerospike-server:6.2.0.6 + + # keep syncronized image version with tracer\test\Datadog.Trace.TestHelpers.AutoInstrumentation\Containers\PostgresFixture.cs + postgres: + image: postgres:10.5-alpine diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs index 630e7175ee4c..0f1c9d41f00f 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Datadog.Trace.TestHelpers; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using Xunit; using Xunit.Abstractions; @@ -12,12 +13,14 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] + [Collection(PostgresCollection.Name)] public class DapperTests : TracingIntegrationTest { - public DapperTests(ITestOutputHelper output) + public DapperTests(ITestOutputHelper output, PostgresFixture postgresFixture) : base("Dapper", output) { SetServiceVersion("1.0.0"); + ConfigureContainers(postgresFixture); } // Assert Npgsql because the Dapper application uses Postgres for the actual client diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs index 56a3508b9982..a74c44276b3c 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs @@ -11,6 +11,7 @@ using Datadog.Trace.ClrProfiler.IntegrationTests.Helpers; using Datadog.Trace.Configuration; using Datadog.Trace.TestHelpers; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using FluentAssertions; using VerifyXunit; using Xunit; @@ -20,13 +21,15 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] + [Collection(PostgresCollection.Name)] [UsesVerify] public class NpgsqlCommandTests : TracingIntegrationTest { - public NpgsqlCommandTests(ITestOutputHelper output) + public NpgsqlCommandTests(ITestOutputHelper output, PostgresFixture postgresFixture) : base("Npgsql", output) { SetServiceVersion("1.0.0"); + ConfigureContainers(postgresFixture); } public override Result ValidateIntegrationSpan(MockSpan span, string metadataSchemaVersion) => span.IsNpgsql(metadataSchemaVersion); @@ -78,7 +81,10 @@ public async Task SubmitsTraces( var settings = VerifyHelper.GetSpanVerifierSettings(); settings.AddRegexScrubber(new Regex("Npgsql-Test-[a-zA-Z0-9]{32}"), "Npgsql-Test-GUID"); settings.AddSimpleScrubber("out.host: localhost", "out.host: postgres"); + settings.AddSimpleScrubber("out.host: 127.0.0.1", "out.host: postgres"); settings.AddSimpleScrubber("out.host: postgres_arm64", "out.host: postgres"); + // TestContainers uses Docker bridge IP (e.g., 172.17.0.1) + settings.AddRegexScrubber(new Regex(@"out\.host: \d+\.\d+\.\d+\.\d+"), "out.host: postgres"); var fileName = nameof(NpgsqlCommandTests); #if NETFRAMEWORK diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs index 63cb6c7aa8bd..80e112f5705a 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs @@ -20,8 +20,9 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "2")] + [Collection(AerospikeCollection.Name)] [UsesVerify] - public class AerospikeTests : TracingIntegrationTest, IClassFixture + public class AerospikeTests : TracingIntegrationTest { public AerospikeTests(ITestOutputHelper output, AerospikeFixture aerospikeFixture) : base("Aerospike", output) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs new file mode 100644 index 000000000000..0516e988f4d6 --- /dev/null +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs @@ -0,0 +1,33 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; +using Xunit; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests; + +/// +/// Collection definition for Postgres tests. +/// Using ICollectionFixture ensures that ONE PostgresFixture instance is shared across all test classes +/// in this collection. The container starts when the first test runs and stops when the last test finishes. +/// +[CollectionDefinition(Name)] +public class PostgresCollection : ICollectionFixture +{ + public const string Name = "Postgres"; +} + +/// +/// Collection definition for Aerospike tests. +/// Using ICollectionFixture ensures that ONE AerospikeFixture instance is shared across all test classes +/// in this collection. The container starts when the first test runs and stops when the last test finishes. +/// +#pragma warning disable SA1402 // File may only contain a single type +[CollectionDefinition(Name)] +public class AerospikeCollection : ICollectionFixture +{ + public const string Name = "Aerospike"; +} +#pragma warning restore SA1402 // File may only contain a single type diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs new file mode 100644 index 000000000000..14bab165add1 --- /dev/null +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs @@ -0,0 +1,20 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; +using Xunit; + +namespace Datadog.Trace.Security.IntegrationTests; + +/// +/// Collection definition for Postgres tests. +/// Using ICollectionFixture ensures that ONE PostgresFixture instance is shared across all test classes +/// in this collection. The container starts when the first test runs and stops when the last test finishes. +/// +[CollectionDefinition(Name)] +public class PostgresCollection : ICollectionFixture +{ + public const string Name = "Postgres"; +} diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs index ed298610c86d..77f8d6970539 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs @@ -12,10 +12,12 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Datadog.Trace.ClrProfiler.IntegrationTests; using Datadog.Trace.Configuration; using Datadog.Trace.Iast.Telemetry; using Datadog.Trace.Security.IntegrationTests.IAST; using Datadog.Trace.TestHelpers; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using Xunit; using Xunit.Abstractions; @@ -653,6 +655,11 @@ protected override async Task TryStartApp() public abstract class AspNetCore2IastTests : AspNetBase, IClassFixture { public AspNetCore2IastTests(AspNetCoreTestFixture fixture, ITestOutputHelper outputHelper, bool enableIast, string testName, bool? isIastDeduplicationEnabled = null, int? samplingRate = null, int? vulnerabilitiesPerRequest = null, bool? redactionEnabled = false, int iastTelemetryLevel = (int)IastMetricsVerbosityLevel.Off) + : this(fixture, null, outputHelper, enableIast, testName, isIastDeduplicationEnabled, samplingRate, vulnerabilitiesPerRequest, redactionEnabled, iastTelemetryLevel) + { + } + + public AspNetCore2IastTests(AspNetCoreTestFixture fixture, PostgresFixture postgresFixture, ITestOutputHelper outputHelper, bool enableIast, string testName, bool? isIastDeduplicationEnabled = null, int? samplingRate = null, int? vulnerabilitiesPerRequest = null, bool? redactionEnabled = false, int iastTelemetryLevel = (int)IastMetricsVerbosityLevel.Off) : base("AspNetCore2", outputHelper, "/shutdown", testName: testName) { Fixture = fixture; @@ -664,6 +671,11 @@ public AspNetCore2IastTests(AspNetCoreTestFixture fixture, ITestOutputHelper out SamplingRate = samplingRate; IastTelemetryLevel = iastTelemetryLevel; SetEnvironmentVariable(ConfigurationKeys.AppSec.StackTraceEnabled, "false"); + + if (postgresFixture != null) + { + ConfigureContainers(postgresFixture); + } } protected AspNetCoreTestFixture Fixture { get; } diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs index a72344f37ad8..9264b2876192 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs @@ -9,17 +9,20 @@ using System.Threading.Tasks; using Datadog.Trace.Security.IntegrationTests.Iast; using Datadog.Trace.TestHelpers; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using Xunit; using Xunit.Abstractions; namespace Datadog.Trace.Security.IntegrationTests.IAST; [Trait("RequiresDockerDependency", "true")] +[Collection(PostgresCollection.Name)] public class AspNetCore5IastDbTests : AspNetCore5IastTests { - public AspNetCore5IastDbTests(AspNetCoreTestFixture fixture, ITestOutputHelper outputHelper) + public AspNetCore5IastDbTests(AspNetCoreTestFixture fixture, PostgresFixture postgresFixture, ITestOutputHelper outputHelper) : base(fixture, outputHelper, enableIast: true, testName: "AspNetCore5IastDbTestsIastEnabled", samplingRate: 100, vulnerabilitiesPerRequest: 200, isIastDeduplicationEnabled: false, sampleName: "AspNetCore5") { + ConfigureContainers(postgresFixture); } [SkippableTheory] diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastTests.cs index 1f84155e5bc0..61cc5a66fd86 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastTests.cs @@ -17,6 +17,7 @@ using Datadog.Trace.Iast.Telemetry; using Datadog.Trace.Security.IntegrationTests.IAST; using Datadog.Trace.TestHelpers; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using FluentAssertions; using Xunit; using Xunit.Abstractions; diff --git a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs index 649251d7d202..4a1c930b6ada 100644 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs +++ b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs @@ -15,28 +15,48 @@ namespace Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; public abstract class ContainerFixture : IAsyncLifetime { - private IReadOnlyDictionary? _resources; + private Dictionary? _resources; public async Task InitializeAsync() { - _resources = await ContainersRegistry.GetOrAdd(GetType(), InitializeResources); + _resources = new Dictionary(); + await InitializeResources(_resources.Add).ConfigureAwait(false); } - // Do not implement, the ContainersRegistry is responsible for disposing the containers - public Task DisposeAsync() => Task.CompletedTask; + public async Task DisposeAsync() + { + if (_resources == null) + { + return; + } + + foreach (var resourceKvp in _resources) + { + var resource = resourceKvp.Value; + + try + { + if (resource is IAsyncDisposable asyncDisposable) + { + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + } + else if (resource is IDisposable disposable) + { + disposable.Dispose(); + } + } + catch + { + // Continue disposing other resources even if one fails + } + } + + _resources.Clear(); + } public virtual IEnumerable> GetEnvironmentVariables() => Enumerable.Empty>(); protected abstract Task InitializeResources(Action registerResource); protected T GetResource(string key) => (T)_resources![key]; - - private async Task> InitializeResources() - { - var resources = new Dictionary(); - - await InitializeResources(resources.Add).ConfigureAwait(false); - - return resources; - } } diff --git a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs deleted file mode 100644 index 7c7d6f85e530..000000000000 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. -// - -#nullable enable - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; - -public static class ContainersRegistry -{ - private static readonly ConcurrentDictionary>> Resources = new(); - - public static async Task> GetOrAdd(Type type, Func>> createResources) - { - if (!Resources.TryGetValue(type, out var task)) - { - var tcs = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously); - - task = Resources.GetOrAdd(type, tcs.Task); - - if (task == tcs.Task) - { - try - { - var resources = await createResources().ConfigureAwait(false); - tcs.SetResult(resources); - } - catch (Exception ex) - { - tcs.SetException(ex); - } - } - } - - return await task.ConfigureAwait(false); - } - - public static async Task DisposeAll() - { - foreach (var resourceGroup in Resources.Values) - { - try - { - var resources = await resourceGroup.ConfigureAwait(false); - - foreach (var resource in resources.Values) - { - if (resource is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync().ConfigureAwait(false); - } - else if (resource is IDisposable disposable) - { - disposable.Dispose(); - } - } - } - catch - { - // Exceptions are expected here, if the container failed to initialize - } - } - - Resources.Clear(); - } -} diff --git a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/PostgresFixture.cs b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/PostgresFixture.cs new file mode 100644 index 000000000000..daa426a8da91 --- /dev/null +++ b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/PostgresFixture.cs @@ -0,0 +1,50 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Containers; + +namespace Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; + +public class PostgresFixture : ContainerFixture +{ + private const int PostgresPort = 5432; + private const string PostgresPassword = "postgres"; + private const string PostgresUser = "postgres"; + private const string PostgresDatabase = "postgres"; + + protected IContainer Container => GetResource("container"); + + public override IEnumerable> GetEnvironmentVariables() + { + var host = Container.Hostname; + var port = Container.GetMappedPublicPort(PostgresPort); + var connectionString = $"Host={host};Port={port};Username={PostgresUser};Password={PostgresPassword};Database={PostgresDatabase}"; + + yield return new("POSTGRES_CONNECTION_STRING", connectionString); + yield return new("POSTGRES_HOST", host); + } + + protected override async Task InitializeResources(Action registerResource) + { + var container = new ContainerBuilder() + .WithImage("postgres:10.5-alpine") + .WithPortBinding(PostgresPort, true) + .WithEnvironment("POSTGRES_PASSWORD", PostgresPassword) + .WithEnvironment("POSTGRES_USER", PostgresUser) + .WithEnvironment("POSTGRES_DB", PostgresDatabase) + .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(PostgresPort)) + .Build(); + + await container.StartAsync(); + + registerResource("container", container); + } +}