From bdc5ea408b00dffb054c9f24778362b01f67775f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 8 Apr 2024 23:49:22 -0400 Subject: [PATCH] [tests] Use existing container for tests executed with RemoteExecutor (#3444) The tracing tests in `ConformanceTests` for some components are run with `RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose()` .. where `RunWithFixtureAsync` starts up a `testcontainer` in the remote process, and then runs the test. But unlike other instances of the tracing tests, here starting up the container can take ~1min causing the `RemoteExecutor` to fail after the default 1 min timeout. Instead pass the connection string from the fixture to the `RemoteExecutor`, so a new container doesn't need to be spun up. Example log from the remote execution: ``` [testcontainers.org 00:00:02.97] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:12.09] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:21.15] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:30.22] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:39.29] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:48.35] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:57.42] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:58.52] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [testcontainers.org 00:00:59.60] Execute "/opt/mssql-tools/bin/sqlcmd -Q SELECT 1;" at Docker container d69a9831c425 [xUnit.net 00:02:38.60] Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests.ConformanceTests_TypeSpecificConfig.TracingEnablesTheRightActivitySource [FAIL] [xUnit.net 00:02:38.60] Microsoft.DotNet.RemoteExecutor.RemoteExecutionException : Half-way through waiting for remote process. [xUnit.net 00:02:38.60] Timed out at 3/27/2024 5:35:56 PM after 60000ms waiting for remote process. [xUnit.net 00:02:38.60] Process ID: 36471 [xUnit.net 00:02:38.60] Handle: 5036 [xUnit.net 00:02:38.60] Name: dotnet [xUnit.net 00:02:38.60] MainModule: /datadisks/disk1/work/B5B5097E/p/dotnet-cli/dotnet [xUnit.net 00:02:38.60] StartTime: 3/27/2024 5:34:56 PM [xUnit.net 00:02:38.60] TotalProcessorTime: 00:00:02.1500000 [xUnit.net 00:02:38.60] [xUnit.net 00:02:38.60] Stack Trace: [xUnit.net 00:02:38.60] /_/src/Microsoft.DotNet.RemoteExecutor/src/RemoteInvokeHandle.cs(225,0): at Microsoft.DotNet.RemoteExecutor.RemoteInvokeHandle.Dispose(Boolean disposing) [xUnit.net 00:02:38.60] /_/src/Microsoft.DotNet.RemoteExecutor/src/RemoteInvokeHandle.cs(55,0): at Microsoft.DotNet.RemoteExecutor.RemoteInvokeHandle.Dispose() [xUnit.net 00:02:38.60] /_/tests/Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests/ConformanceTests.cs(116,0): at Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests.ConformanceTests.TracingEnabl> [xUnit.net 00:02:38.60] at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) [xUnit.net 00:02:38.60] at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) ``` Fixes https://github.com/dotnet/aspire/issues/3221 --- .../ConformanceTests.cs | 29 ++++++++++--------- .../ConformanceTests.cs | 26 ++++++++--------- .../ConformanceTests.cs | 29 ++++++++++--------- .../ConformanceTests.cs | 27 +++++++++-------- tests/Aspire.Npgsql.Tests/ConformanceTests.cs | 29 ++++++++++--------- .../ConformanceTests.cs | 26 ++++++++--------- 6 files changed, 84 insertions(+), 82 deletions(-) diff --git a/tests/Aspire.Microsoft.Data.SqlClient.Tests/ConformanceTests.cs b/tests/Aspire.Microsoft.Data.SqlClient.Tests/ConformanceTests.cs index a3e68e0899..3fc6ffd1f1 100644 --- a/tests/Aspire.Microsoft.Data.SqlClient.Tests/ConformanceTests.cs +++ b/tests/Aspire.Microsoft.Data.SqlClient.Tests/ConformanceTests.cs @@ -14,11 +14,9 @@ namespace Aspire.Microsoft.Data.SqlClient.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { - private readonly SqlServerContainerFixture _containerFixture; + private readonly SqlServerContainerFixture? _containerFixture; + private string ConnectionString { get; set; } protected override bool CanConnectToServer => RequiresDockerTheoryAttribute.IsSupported; - private string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Data Source=fake;Database=master"; protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Scoped; // https://github.com/open-telemetry/opentelemetry-dotnet/blob/031ed48714e16ba4a5b099b6e14647994a0b9c1b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlActivitySourceHelper.cs#L31 @@ -49,8 +47,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "Microsoft": { "Data" : { "SqlClient":{ "ConnectionString": "Con", "HealthChecks": "false"}}}}}""", "Value is \"string\" but should be \"boolean\"") }; - public ConformanceTests(SqlServerContainerFixture containerFixture) - => _containerFixture = containerFixture; + public ConformanceTests(SqlServerContainerFixture? containerFixture) + { + _containerFixture = containerFixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[1] @@ -90,17 +93,15 @@ protected override void TriggerActivity(SqlConnection service) [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); [RequiresDockerFact] public void TracingEnablesTheRightActivitySource_Keyed() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: "key"))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: "key")), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new SqlServerContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); } diff --git a/tests/Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests/ConformanceTests.cs b/tests/Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests/ConformanceTests.cs index 371bd5edd7..6d2851a4e0 100644 --- a/tests/Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests/ConformanceTests.cs +++ b/tests/Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests/ConformanceTests.cs @@ -15,10 +15,8 @@ namespace Aspire.Microsoft.EntityFrameworkCore.SqlServer.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { - private readonly SqlServerContainerFixture _containerFixture; - protected string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Host=fake;Database=catalog"; + private readonly SqlServerContainerFixture? _containerFixture; + protected string ConnectionString { get; private set; } protected override bool CanConnectToServer => RequiresDockerTheoryAttribute.IsSupported; protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton; @@ -64,8 +62,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "Microsoft": { "EntityFrameworkCore":{ "SqlServer": { "Tracing": "false"}}}}}""", "Value is \"string\" but should be \"boolean\""), }; - public ConformanceTests(SqlServerContainerFixture fixture) - => _containerFixture = fixture; + public ConformanceTests(SqlServerContainerFixture? fixture) + { + _containerFixture = fixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[1] @@ -113,12 +116,9 @@ public void DbContextCanBeAlwaysResolved() [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new SqlServerContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); } diff --git a/tests/Aspire.MySqlConnector.Tests/ConformanceTests.cs b/tests/Aspire.MySqlConnector.Tests/ConformanceTests.cs index a3a0f2acf3..7183697dff 100644 --- a/tests/Aspire.MySqlConnector.Tests/ConformanceTests.cs +++ b/tests/Aspire.MySqlConnector.Tests/ConformanceTests.cs @@ -15,10 +15,8 @@ namespace Aspire.MySqlConnector.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { - private readonly MySqlContainerFixture _containerFixture; - private string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + private readonly MySqlContainerFixture? _containerFixture; + private string ConnectionString { get; set; } protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton; // https://github.com/mysql-net/MySqlConnector/blob/d895afc013a5849d33a123a7061442e2cbb9ce76/src/MySqlConnector/Utilities/ActivitySourceHelper.cs#L61 @@ -55,8 +53,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "MySqlConnector":{ "ConnectionString": "Con", "HealthChecks": "false"}}}""", "Value is \"string\" but should be \"boolean\"") }; - public ConformanceTests(MySqlContainerFixture containerFixture) - => _containerFixture = containerFixture; + public ConformanceTests(MySqlContainerFixture? containerFixture) + { + _containerFixture = containerFixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[1] @@ -120,16 +123,14 @@ public void BothDataSourceAndConnectionCanBeResolved(string? key) [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); [RequiresDockerFact] public void TracingEnablesTheRightActivitySource_Keyed() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: "key"))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: "key")), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new MySqlContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); } diff --git a/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs b/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs index aaaaf492e6..d235f593d7 100644 --- a/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs +++ b/tests/Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests/ConformanceTests.cs @@ -16,11 +16,8 @@ namespace Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { // in the future it can become a static property that reads the value from Env Var - private readonly PostgreSQLContainerFixture _containerFixture; - protected string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Host=localhost;Database=test;Username=postgres;Password=postgres"; - + private readonly PostgreSQLContainerFixture? _containerFixture; + protected string ConnectionString { get; private set; } protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton; // https://github.com/npgsql/npgsql/blob/ef9db1ffe9e432c1562d855b46dfac3514726b1b/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs#L18 @@ -79,8 +76,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "Npgsql": { "EntityFrameworkCore":{ "PostgreSQL": { "Metrics": "false"}}}}}""", "Value is \"string\" but should be \"boolean\""), }; - public ConformanceTests(PostgreSQLContainerFixture containerFixture) - => _containerFixture = containerFixture; + public ConformanceTests(PostgreSQLContainerFixture? containerFixture) + { + _containerFixture = containerFixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[1] @@ -131,12 +133,9 @@ public void DbContextCanBeAlwaysResolved() [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new PostgreSQLContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); } diff --git a/tests/Aspire.Npgsql.Tests/ConformanceTests.cs b/tests/Aspire.Npgsql.Tests/ConformanceTests.cs index 3d20061214..32209b91de 100644 --- a/tests/Aspire.Npgsql.Tests/ConformanceTests.cs +++ b/tests/Aspire.Npgsql.Tests/ConformanceTests.cs @@ -15,10 +15,8 @@ namespace Aspire.Npgsql.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { - private readonly PostgreSQLContainerFixture _containerFixture; - private string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Host=localhost;Database=test_aspire_npgsql;Username=postgres;Password=postgres"; + private readonly PostgreSQLContainerFixture? _containerFixture; + protected string ConnectionString { get; private set; } protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton; // https://github.com/npgsql/npgsql/blob/ef9db1ffe9e432c1562d855b46dfac3514726b1b/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs#L18 @@ -57,8 +55,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "Npgsql":{ "ConnectionString": "Con", "HealthChecks": "false"}}}""", "Value is \"string\" but should be \"boolean\"") }; - public ConformanceTests(PostgreSQLContainerFixture containerFixture) - => _containerFixture = containerFixture; + public ConformanceTests(PostgreSQLContainerFixture? containerFixture) + { + _containerFixture = containerFixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[1] @@ -122,16 +125,14 @@ public void BothDataSourceAndConnectionCanBeResolved(string? key) [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); [RequiresDockerFact] public void TracingEnablesTheRightActivitySource_Keyed() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: "key"))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: "key")), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new PostgreSQLContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); } diff --git a/tests/Aspire.Pomelo.EntityFrameworkCore.MySql.Tests/ConformanceTests.cs b/tests/Aspire.Pomelo.EntityFrameworkCore.MySql.Tests/ConformanceTests.cs index 0a7cc1c405..05d8000b4b 100644 --- a/tests/Aspire.Pomelo.EntityFrameworkCore.MySql.Tests/ConformanceTests.cs +++ b/tests/Aspire.Pomelo.EntityFrameworkCore.MySql.Tests/ConformanceTests.cs @@ -16,10 +16,8 @@ namespace Aspire.Pomelo.EntityFrameworkCore.MySql.Tests; public class ConformanceTests : ConformanceTests, IClassFixture { - private readonly MySqlContainerFixture _containerFixture; - protected string ConnectionString => RequiresDockerTheoryAttribute.IsSupported - ? _containerFixture.GetConnectionString() - : "Server=localhost;User ID=root;Password=pass;Database=test"; + private readonly MySqlContainerFixture? _containerFixture; + protected string ConnectionString { get; private set; } protected readonly string ServerVersion = $"{MySqlContainerImageTags.Tag}-mysql"; protected override ServiceLifetime ServiceLifetime => ServiceLifetime.Singleton; @@ -74,8 +72,13 @@ protected override (string json, string error)[] InvalidJsonToErrorMessage => ne ("""{"Aspire": { "Pomelo": { "EntityFrameworkCore":{ "MySql": { "Metrics": "false"}}}}}""", "Value is \"string\" but should be \"boolean\""), }; - public ConformanceTests(MySqlContainerFixture containerFixture) - => _containerFixture = containerFixture; + public ConformanceTests(MySqlContainerFixture? containerFixture) + { + _containerFixture = containerFixture; + ConnectionString = (_containerFixture is not null && RequiresDockerTheoryAttribute.IsSupported) + ? _containerFixture.GetConnectionString() + : "Server=localhost;User ID=root;Password=password;Database=test_aspire_mysql"; + } protected override void PopulateConfiguration(ConfigurationManager configuration, string? key = null) => configuration.AddInMemoryCollection(new KeyValuePair[2] @@ -126,12 +129,9 @@ public void DbContextCanBeAlwaysResolved() [RequiresDockerFact] public void TracingEnablesTheRightActivitySource() - => RemoteExecutor.Invoke(() => RunWithFixtureAsync(obj => obj.ActivitySourceTest(key: null))).Dispose(); + => RemoteExecutor.Invoke(static connectionStringToUse => RunWithConnectionString(connectionStringToUse, obj => obj.ActivitySourceTest(key: null)), + ConnectionString).Dispose(); - private static async Task RunWithFixtureAsync(Action test) - { - await using var fixture = new MySqlContainerFixture(); - await fixture.InitializeAsync(); - test(new ConformanceTests(fixture)); - } + private static void RunWithConnectionString(string connectionString, Action test) + => test(new ConformanceTests(null) { ConnectionString = connectionString }); }