From 03853463317244a59b2258fc74fb63fe99cecb56 Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 31 Oct 2025 07:48:32 -0400 Subject: [PATCH 1/3] Migrate Npgsql/Postgres integration tests to TestContainers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement ContainersRegistry with reference counting for container lifecycle management - Create PostgresFixture using DotNet.Testcontainers library - Add ContainersCollection for xUnit collection-level disposal - Migrate NpgsqlCommandTests, DapperTests, and IAST tests to use PostgresFixture - Remove postgres service from docker-compose.yml as it's now managed by TestContainers - Update test scrubbers to handle TestContainers dynamic IP addresses This improves test isolation and reduces dependency on docker-compose for integration tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docker-compose.yml | 48 ++----- .../AdoNet/DapperTests.cs | 7 +- .../AdoNet/NpgsqlCommandTests.cs | 10 +- .../AerospikeTests.cs | 1 + .../ContainersCollection.cs | 48 +++++++ .../ContainersCollection.cs | 48 +++++++ .../IAST/AspNetCore2IastTests.cs | 15 ++- .../IAST/AspNetCore5IastDbTests.cs | 7 +- .../IAST/AspNetCore5IastTests.cs | 1 + .../Containers/ContainerFixture.cs | 8 +- .../Containers/ContainersRegistry.cs | 117 ++++++++++++++++-- .../Containers/PostgresFixture.cs | 50 ++++++++ 12 files changed, 304 insertions(+), 56 deletions(-) create mode 100644 tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs create mode 100644 tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs create mode 100644 tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/PostgresFixture.cs 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..2dd166f27aa9 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")] - public class DapperTests : TracingIntegrationTest + [Collection(ContainersCollection.Name)] + public class DapperTests : TracingIntegrationTest, IClassFixture { - 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..1595bd28e579 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(ContainersCollection.Name)] [UsesVerify] - public class NpgsqlCommandTests : TracingIntegrationTest + public class NpgsqlCommandTests : TracingIntegrationTest, IClassFixture { - 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..1325529ac356 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs @@ -20,6 +20,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "2")] + [Collection(ContainersCollection.Name)] [UsesVerify] public class AerospikeTests : TracingIntegrationTest, IClassFixture { 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..86b94d54e700 --- /dev/null +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs @@ -0,0 +1,48 @@ +// +// 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 System; +using System.Threading.Tasks; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; +using Xunit; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests; + +/// +/// Collection definition for TestContainers. +/// This ensures that all containers are properly disposed when all tests complete. +/// +[CollectionDefinition(Name)] +public class ContainersCollection : ICollectionFixture +{ + public const string Name = "TestContainers"; +} + +/// +/// Cleanup fixture that disposes all TestContainers when the test collection completes. +/// This implements the optimization mentioned in PR #5031: "stop the docker images as soon as they're not needed anymore" +/// +#pragma warning disable SA1402 // File may only contain a single type +public class ContainersCleanup : IAsyncLifetime +#pragma warning restore SA1402 // File may only contain a single type +{ + public Task InitializeAsync() + { + // No initialization needed + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + try + { + await ContainersRegistry.DisposeAll(); + } + catch + { + // Don't throw - we don't want to fail tests due to cleanup errors + } + } +} 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..2e523dbe9aa2 --- /dev/null +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs @@ -0,0 +1,48 @@ +// +// 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 System; +using System.Threading.Tasks; +using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; +using Xunit; + +namespace Datadog.Trace.Security.IntegrationTests; + +/// +/// Collection definition for TestContainers. +/// This ensures that all containers are properly disposed when all tests complete. +/// +[CollectionDefinition(Name)] +public class ContainersCollection : ICollectionFixture +{ + public const string Name = "TestContainers"; +} + +/// +/// Cleanup fixture that disposes all TestContainers when the test collection completes. +/// This implements the optimization mentioned in PR #5031: "stop the docker images as soon as they're not needed anymore" +/// +#pragma warning disable SA1402 // File may only contain a single type +public class ContainersCleanup : IAsyncLifetime +#pragma warning restore SA1402 // File may only contain a single type +{ + public Task InitializeAsync() + { + // No initialization needed + return Task.CompletedTask; + } + + public async Task DisposeAsync() + { + try + { + await ContainersRegistry.DisposeAll(); + } + catch + { + // Don't throw - we don't want to fail tests due to cleanup errors + } + } +} diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs index ed298610c86d..f1f2ce8ea20f 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; @@ -650,9 +652,15 @@ protected override async Task TryStartApp() } } -public abstract class AspNetCore2IastTests : AspNetBase, IClassFixture +[Collection(ContainersCollection.Name)] +public abstract class AspNetCore2IastTests : AspNetBase, IClassFixture, 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 +672,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..3d31f1ffa88c 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")] -public class AspNetCore5IastDbTests : AspNetCore5IastTests +[Collection(ContainersCollection.Name)] +public class AspNetCore5IastDbTests : AspNetCore5IastTests, IClassFixture { - 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..24ce78b87c4d 100644 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs +++ b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs @@ -22,8 +22,12 @@ public async Task InitializeAsync() _resources = await ContainersRegistry.GetOrAdd(GetType(), InitializeResources); } - // Do not implement, the ContainersRegistry is responsible for disposing the containers - public Task DisposeAsync() => Task.CompletedTask; + public async Task DisposeAsync() + { + // Release the reference to this fixture type + // When the reference count reaches zero, the containers will be disposed + await ContainersRegistry.Release(GetType()); + } public virtual IEnumerable> GetEnvironmentVariables() => Enumerable.Empty>(); diff --git a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs index 7c7d6f85e530..ab12d4426229 100644 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs +++ b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs @@ -8,16 +8,33 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; +/// +/// Registry for TestContainers resources. Ensures containers are started once and reused across test classes. +/// Containers are disposed when is called (typically at end of test run). +/// public static class ContainersRegistry { private static readonly ConcurrentDictionary>> Resources = new(); + private static readonly ConcurrentDictionary ReferenceCounts = new(); + private static readonly ConcurrentDictionary DisposingFixtures = new(); + private static readonly object DisposeLock = new(); public static async Task> GetOrAdd(Type type, Func>> createResources) { + // Prevent getting resources that are being disposed to avoid race conditions + if (DisposingFixtures.TryGetValue(type, out var disposing) && disposing) + { + throw new InvalidOperationException($"Cannot acquire {type.Name} - it is being disposed"); + } + + // Increment reference count for this fixture type + ReferenceCounts.AddOrUpdate(type, 1, (_, count) => count + 1); + if (!Resources.TryGetValue(type, out var task)) { var tcs = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously); @@ -41,15 +58,93 @@ public static async Task> GetOrAdd(Type type return await task.ConfigureAwait(false); } - public static async Task DisposeAll() + /// + /// Releases a reference to a fixture type. When the reference count reaches zero, the resources are disposed. + /// This should be called from ContainerFixture.DisposeAsync() when a test class completes. + /// + public static async Task Release(Type type) { - foreach (var resourceGroup in Resources.Values) + bool shouldDispose; + + lock (DisposeLock) + { + if (!ReferenceCounts.TryGetValue(type, out var currentCount)) + { + return; + } + + var newCount = currentCount - 1; + + if (newCount <= 0) + { + ReferenceCounts.TryRemove(type, out _); + // Mark as disposing to prevent new GetOrAdd calls during disposal + DisposingFixtures[type] = true; + shouldDispose = true; + } + else + { + ReferenceCounts[type] = newCount; + shouldDispose = false; + } + } + + // Dispose outside the lock to avoid holding it during async operations + if (shouldDispose) { try { - var resources = await resourceGroup.ConfigureAwait(false); + await DisposeFixtureResources(type).ConfigureAwait(false); + } + finally + { + // Always clear the disposing flag, even if disposal fails + DisposingFixtures.TryRemove(type, out _); + } + } + } + + /// + /// Disposes all containers in the registry. + /// This should be called at the end of the test run to clean up any remaining containers. + /// Use xUnit's ICollectionFixture to ensure this is called when all tests complete. + /// + public static async Task DisposeAll() + { + var fixtureTypes = Resources.Keys.ToArray(); - foreach (var resource in resources.Values) + // Mark all fixtures as disposing to prevent new GetOrAdd calls + foreach (var fixtureType in fixtureTypes) + { + DisposingFixtures[fixtureType] = true; + } + + foreach (var fixtureType in fixtureTypes) + { + await DisposeFixtureResources(fixtureType).ConfigureAwait(false); + } + + Resources.Clear(); + ReferenceCounts.Clear(); + DisposingFixtures.Clear(); + } + + private static async Task DisposeFixtureResources(Type fixtureType) + { + if (!Resources.TryRemove(fixtureType, out var resourceGroupTask)) + { + return; + } + + try + { + var resources = await resourceGroupTask.ConfigureAwait(false); + + foreach (var resourceKvp in resources) + { + var resource = resourceKvp.Value; + + try { if (resource is IAsyncDisposable asyncDisposable) { @@ -60,13 +155,15 @@ public static async Task DisposeAll() disposable.Dispose(); } } - } - catch - { - // Exceptions are expected here, if the container failed to initialize + catch + { + // Continue disposing other resources even if one fails + } } } - - Resources.Clear(); + catch + { + // Exceptions are expected here, if the container failed to initialize + } } } 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); + } +} From 2525c0693ba46b8eae43aefab44f1465a0cefe26 Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 31 Oct 2025 08:30:30 -0400 Subject: [PATCH 2/3] Attempt to use CollectionFixture instead of ClassFixture I think there may be race conditions if we reuse containers. --- .../AdoNet/DapperTests.cs | 2 +- .../AdoNet/NpgsqlCommandTests.cs | 2 +- .../AerospikeTests.cs | 2 +- .../ContainersCollection.cs | 37 +--- .../ContainersCollection.cs | 33 +--- .../IAST/AspNetCore5IastDbTests.cs | 2 +- .../Containers/ContainerFixture.cs | 44 +++-- .../Containers/ContainersRegistry.cs | 169 ------------------ 8 files changed, 44 insertions(+), 247 deletions(-) delete mode 100644 tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs index 2dd166f27aa9..d00b15348bd5 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs @@ -14,7 +14,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] [Collection(ContainersCollection.Name)] - public class DapperTests : TracingIntegrationTest, IClassFixture + public class DapperTests : TracingIntegrationTest { public DapperTests(ITestOutputHelper output, PostgresFixture postgresFixture) : base("Dapper", output) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs index 1595bd28e579..1d88eea932f7 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs @@ -23,7 +23,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet [Trait("DockerGroup", "1")] [Collection(ContainersCollection.Name)] [UsesVerify] - public class NpgsqlCommandTests : TracingIntegrationTest, IClassFixture + public class NpgsqlCommandTests : TracingIntegrationTest { public NpgsqlCommandTests(ITestOutputHelper output, PostgresFixture postgresFixture) : base("Npgsql", output) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs index 1325529ac356..e34ed047f278 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs @@ -22,7 +22,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests [Trait("DockerGroup", "2")] [Collection(ContainersCollection.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 index 86b94d54e700..5454354066d9 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs @@ -3,8 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -using System; -using System.Threading.Tasks; using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using Xunit; @@ -12,37 +10,14 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests; /// /// Collection definition for TestContainers. -/// This ensures that all containers are properly disposed when all tests complete. +/// Using ICollectionFixture ensures that ONE instance of each fixture is shared across all test classes +/// in the collection, and disposed when all tests complete. This prevents race conditions and +/// eliminates the need for reference counting. /// [CollectionDefinition(Name)] -public class ContainersCollection : ICollectionFixture +public class ContainersCollection : + ICollectionFixture, + ICollectionFixture { public const string Name = "TestContainers"; } - -/// -/// Cleanup fixture that disposes all TestContainers when the test collection completes. -/// This implements the optimization mentioned in PR #5031: "stop the docker images as soon as they're not needed anymore" -/// -#pragma warning disable SA1402 // File may only contain a single type -public class ContainersCleanup : IAsyncLifetime -#pragma warning restore SA1402 // File may only contain a single type -{ - public Task InitializeAsync() - { - // No initialization needed - return Task.CompletedTask; - } - - public async Task DisposeAsync() - { - try - { - await ContainersRegistry.DisposeAll(); - } - catch - { - // Don't throw - we don't want to fail tests due to cleanup errors - } - } -} diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs index 2e523dbe9aa2..9c02a238c957 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs @@ -12,37 +12,12 @@ namespace Datadog.Trace.Security.IntegrationTests; /// /// Collection definition for TestContainers. -/// This ensures that all containers are properly disposed when all tests complete. +/// Using ICollectionFixture ensures that ONE instance of each fixture is shared across all test classes +/// in the collection, and disposed when all tests complete. This prevents race conditions and +/// eliminates the need for reference counting. /// [CollectionDefinition(Name)] -public class ContainersCollection : ICollectionFixture +public class ContainersCollection : ICollectionFixture { public const string Name = "TestContainers"; } - -/// -/// Cleanup fixture that disposes all TestContainers when the test collection completes. -/// This implements the optimization mentioned in PR #5031: "stop the docker images as soon as they're not needed anymore" -/// -#pragma warning disable SA1402 // File may only contain a single type -public class ContainersCleanup : IAsyncLifetime -#pragma warning restore SA1402 // File may only contain a single type -{ - public Task InitializeAsync() - { - // No initialization needed - return Task.CompletedTask; - } - - public async Task DisposeAsync() - { - try - { - await ContainersRegistry.DisposeAll(); - } - catch - { - // Don't throw - we don't want to fail tests due to cleanup errors - } - } -} diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs index 3d31f1ffa88c..55a6e37b8b1a 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs @@ -17,7 +17,7 @@ namespace Datadog.Trace.Security.IntegrationTests.IAST; [Trait("RequiresDockerDependency", "true")] [Collection(ContainersCollection.Name)] -public class AspNetCore5IastDbTests : AspNetCore5IastTests, IClassFixture +public class AspNetCore5IastDbTests : AspNetCore5IastTests { public AspNetCore5IastDbTests(AspNetCoreTestFixture fixture, PostgresFixture postgresFixture, ITestOutputHelper outputHelper) : base(fixture, outputHelper, enableIast: true, testName: "AspNetCore5IastDbTestsIastEnabled", samplingRate: 100, vulnerabilitiesPerRequest: 200, isIastDeduplicationEnabled: false, sampleName: "AspNetCore5") diff --git a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs index 24ce78b87c4d..4a1c930b6ada 100644 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs +++ b/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainerFixture.cs @@ -15,18 +15,43 @@ 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); } public async Task DisposeAsync() { - // Release the reference to this fixture type - // When the reference count reaches zero, the containers will be disposed - await ContainersRegistry.Release(GetType()); + 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>(); @@ -34,13 +59,4 @@ public async Task DisposeAsync() 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 ab12d4426229..000000000000 --- a/tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/Containers/ContainersRegistry.cs +++ /dev/null @@ -1,169 +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.Linq; -using System.Threading.Tasks; - -namespace Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; - -/// -/// Registry for TestContainers resources. Ensures containers are started once and reused across test classes. -/// Containers are disposed when is called (typically at end of test run). -/// -public static class ContainersRegistry -{ - private static readonly ConcurrentDictionary>> Resources = new(); - private static readonly ConcurrentDictionary ReferenceCounts = new(); - private static readonly ConcurrentDictionary DisposingFixtures = new(); - private static readonly object DisposeLock = new(); - - public static async Task> GetOrAdd(Type type, Func>> createResources) - { - // Prevent getting resources that are being disposed to avoid race conditions - if (DisposingFixtures.TryGetValue(type, out var disposing) && disposing) - { - throw new InvalidOperationException($"Cannot acquire {type.Name} - it is being disposed"); - } - - // Increment reference count for this fixture type - ReferenceCounts.AddOrUpdate(type, 1, (_, count) => count + 1); - - 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); - } - - /// - /// Releases a reference to a fixture type. When the reference count reaches zero, the resources are disposed. - /// This should be called from ContainerFixture.DisposeAsync() when a test class completes. - /// - public static async Task Release(Type type) - { - bool shouldDispose; - - lock (DisposeLock) - { - if (!ReferenceCounts.TryGetValue(type, out var currentCount)) - { - return; - } - - var newCount = currentCount - 1; - - if (newCount <= 0) - { - ReferenceCounts.TryRemove(type, out _); - // Mark as disposing to prevent new GetOrAdd calls during disposal - DisposingFixtures[type] = true; - shouldDispose = true; - } - else - { - ReferenceCounts[type] = newCount; - shouldDispose = false; - } - } - - // Dispose outside the lock to avoid holding it during async operations - if (shouldDispose) - { - try - { - await DisposeFixtureResources(type).ConfigureAwait(false); - } - finally - { - // Always clear the disposing flag, even if disposal fails - DisposingFixtures.TryRemove(type, out _); - } - } - } - - /// - /// Disposes all containers in the registry. - /// This should be called at the end of the test run to clean up any remaining containers. - /// Use xUnit's ICollectionFixture to ensure this is called when all tests complete. - /// - public static async Task DisposeAll() - { - var fixtureTypes = Resources.Keys.ToArray(); - - // Mark all fixtures as disposing to prevent new GetOrAdd calls - foreach (var fixtureType in fixtureTypes) - { - DisposingFixtures[fixtureType] = true; - } - - foreach (var fixtureType in fixtureTypes) - { - await DisposeFixtureResources(fixtureType).ConfigureAwait(false); - } - - Resources.Clear(); - ReferenceCounts.Clear(); - DisposingFixtures.Clear(); - } - - private static async Task DisposeFixtureResources(Type fixtureType) - { - if (!Resources.TryRemove(fixtureType, out var resourceGroupTask)) - { - return; - } - - try - { - var resources = await resourceGroupTask.ConfigureAwait(false); - - 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 - } - } - } - catch - { - // Exceptions are expected here, if the container failed to initialize - } - } -} From 3c5ca70d5e0aaf87fb5b3cfefda3bc1351f2012b Mon Sep 17 00:00:00 2001 From: Steven Bouwkamp Date: Fri, 31 Oct 2025 15:20:34 -0400 Subject: [PATCH 3/3] Use ICollectionFixture for each one --- .../AdoNet/DapperTests.cs | 2 +- .../AdoNet/NpgsqlCommandTests.cs | 2 +- .../AerospikeTests.cs | 2 +- .../ContainersCollection.cs | 26 +++++++++++++------ .../ContainersCollection.cs | 13 ++++------ .../IAST/AspNetCore2IastTests.cs | 3 +-- .../IAST/AspNetCore5IastDbTests.cs | 2 +- 7 files changed, 28 insertions(+), 22 deletions(-) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs index d00b15348bd5..0f1c9d41f00f 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/DapperTests.cs @@ -13,7 +13,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] - [Collection(ContainersCollection.Name)] + [Collection(PostgresCollection.Name)] public class DapperTests : TracingIntegrationTest { public DapperTests(ITestOutputHelper output, PostgresFixture postgresFixture) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs index 1d88eea932f7..a74c44276b3c 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AdoNet/NpgsqlCommandTests.cs @@ -21,7 +21,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests.AdoNet { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] - [Collection(ContainersCollection.Name)] + [Collection(PostgresCollection.Name)] [UsesVerify] public class NpgsqlCommandTests : TracingIntegrationTest { diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs index e34ed047f278..80e112f5705a 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AerospikeTests.cs @@ -20,7 +20,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests { [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "2")] - [Collection(ContainersCollection.Name)] + [Collection(AerospikeCollection.Name)] [UsesVerify] public class AerospikeTests : TracingIntegrationTest { diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs index 5454354066d9..0516e988f4d6 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/ContainersCollection.cs @@ -9,15 +9,25 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests; /// -/// Collection definition for TestContainers. -/// Using ICollectionFixture ensures that ONE instance of each fixture is shared across all test classes -/// in the collection, and disposed when all tests complete. This prevents race conditions and -/// eliminates the need for reference counting. +/// 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 ContainersCollection : - ICollectionFixture, - ICollectionFixture +public class PostgresCollection : ICollectionFixture { - public const string Name = "TestContainers"; + 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 index 9c02a238c957..14bab165add1 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/ContainersCollection.cs @@ -3,21 +3,18 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -using System; -using System.Threading.Tasks; using Datadog.Trace.TestHelpers.AutoInstrumentation.Containers; using Xunit; namespace Datadog.Trace.Security.IntegrationTests; /// -/// Collection definition for TestContainers. -/// Using ICollectionFixture ensures that ONE instance of each fixture is shared across all test classes -/// in the collection, and disposed when all tests complete. This prevents race conditions and -/// eliminates the need for reference counting. +/// 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 ContainersCollection : ICollectionFixture +public class PostgresCollection : ICollectionFixture { - public const string Name = "TestContainers"; + 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 f1f2ce8ea20f..77f8d6970539 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore2IastTests.cs @@ -652,8 +652,7 @@ protected override async Task TryStartApp() } } -[Collection(ContainersCollection.Name)] -public abstract class AspNetCore2IastTests : AspNetBase, IClassFixture, IClassFixture +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) diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs index 55a6e37b8b1a..9264b2876192 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/IAST/AspNetCore5IastDbTests.cs @@ -16,7 +16,7 @@ namespace Datadog.Trace.Security.IntegrationTests.IAST; [Trait("RequiresDockerDependency", "true")] -[Collection(ContainersCollection.Name)] +[Collection(PostgresCollection.Name)] public class AspNetCore5IastDbTests : AspNetCore5IastTests { public AspNetCore5IastDbTests(AspNetCoreTestFixture fixture, PostgresFixture postgresFixture, ITestOutputHelper outputHelper)