diff --git a/Aspire.sln b/Aspire.sln index 6ab4e67edd8..39de6f4b9d2 100644 --- a/Aspire.sln +++ b/Aspire.sln @@ -515,7 +515,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.PostgreSQL.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Qdrant.Tests", "tests\Aspire.Hosting.Qdrant.Tests\Aspire.Hosting.Qdrant.Tests.csproj", "{8E2AA85E-C351-47B4-AF91-58557FAD5840}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Milvus.Tests", "tests\Aspire.Hosting.Milvus.Tests\Aspire.Hosting.Milvus.Tests.csproj", "{986886B7-0E38-4890-92C3-5B46DE322DAF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Milvus.Tests", "tests\Aspire.Hosting.Milvus.Tests\Aspire.Hosting.Milvus.Tests.csproj", "{986886B7-0E38-4890-92C3-5B46DE322DAF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.MySql.Tests", "tests\Aspire.Hosting.MySql.Tests\Aspire.Hosting.MySql.Tests.csproj", "{F3F33CF8-A2BB-4351-8501-A6884C5126FE}" EndProject @@ -545,6 +545,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.MongoDB.Test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.Nats.Tests", "tests\Aspire.Hosting.Nats.Tests\Aspire.Hosting.Nats.Tests.csproj", "{F492357C-682E-4CBB-A374-1A124B3976A3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Hosting.SqlServer.Tests", "tests\Aspire.Hosting.SqlServer.Tests\Aspire.Hosting.SqlServer.Tests.csproj", "{D705FE42-CD54-4575-BA18-0431256B40B2}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Azure.Tests", "tests\Aspire.Hosting.Azure.Tests\Aspire.Hosting.Azure.Tests.csproj", "{8691F993-7B19-496E-B8E1-EF1199ACF2E1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestShop.AppHost", "playground\TestShop\TestShop.AppHost\TestShop.AppHost.csproj", "{DB3E1AD8-87F6-414D-B46F-A0DC334AECCD}" @@ -1447,6 +1449,10 @@ Global {F492357C-682E-4CBB-A374-1A124B3976A3}.Debug|Any CPU.Build.0 = Debug|Any CPU {F492357C-682E-4CBB-A374-1A124B3976A3}.Release|Any CPU.ActiveCfg = Release|Any CPU {F492357C-682E-4CBB-A374-1A124B3976A3}.Release|Any CPU.Build.0 = Release|Any CPU + {D705FE42-CD54-4575-BA18-0431256B40B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D705FE42-CD54-4575-BA18-0431256B40B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D705FE42-CD54-4575-BA18-0431256B40B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D705FE42-CD54-4575-BA18-0431256B40B2}.Release|Any CPU.Build.0 = Release|Any CPU {8691F993-7B19-496E-B8E1-EF1199ACF2E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8691F993-7B19-496E-B8E1-EF1199ACF2E1}.Debug|Any CPU.Build.0 = Debug|Any CPU {8691F993-7B19-496E-B8E1-EF1199ACF2E1}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1741,6 +1747,7 @@ Global {48FF09E9-7D33-4A3F-9FF2-4C43A219C7B7} = {C424395C-1235-41A4-BF55-07880A04368C} {DD9BC533-8072-481C-9A7E-F95DC36B34C0} = {830A89EC-4029-4753-B25A-068BAE37DEC7} {F492357C-682E-4CBB-A374-1A124B3976A3} = {830A89EC-4029-4753-B25A-068BAE37DEC7} + {D705FE42-CD54-4575-BA18-0431256B40B2} = {830A89EC-4029-4753-B25A-068BAE37DEC7} {8691F993-7B19-496E-B8E1-EF1199ACF2E1} = {830A89EC-4029-4753-B25A-068BAE37DEC7} {DB3E1AD8-87F6-414D-B46F-A0DC334AECCD} = {A68BA1A5-1604-433D-9778-DC0199831C2A} {D537CF4D-DF30-41C7-B4F9-FEB152A19BE7} = {57A42144-739E-49A7-BADB-BB8F5F20FA17} diff --git a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs index f15ac9734d1..9a5128ff27c 100644 --- a/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs +++ b/src/Aspire.Hosting.SqlServer/SqlServerBuilderExtensions.cs @@ -71,5 +71,21 @@ public static IResourceBuilder WithDataVolume(this IRes /// A flag that indicates if this is a read-only mount. /// The . public static IResourceBuilder WithDataBindMount(this IResourceBuilder builder, string source, bool isReadOnly = false) - => builder.WithBindMount(source, "/var/opt/mssql", isReadOnly); + { + // c.f. https://learn.microsoft.com/sql/linux/sql-server-linux-docker-container-configure?view=sql-server-ver15&pivots=cs1-bash#mount-a-host-directory-as-data-volume + + foreach (var dir in new string[] { "data", "log", "secrets" }) + { + var path = Path.Combine(source, dir); + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + builder.WithBindMount(path, $"/var/opt/mssql/{dir}", isReadOnly); + } + + return builder; + } } diff --git a/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs b/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs index 03b1a0fa4c9..e09401b27a3 100644 --- a/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs +++ b/tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs @@ -107,7 +107,6 @@ public Task DumpComponentLogsAsync(TestResourceNames resource, ITestOutputHelper TestResourceNames.oracledatabase => "oracledatabase", TestResourceNames.postgres or TestResourceNames.efnpgsql => "postgres", TestResourceNames.redis => "redis", - TestResourceNames.sqlserver or TestResourceNames.efsqlserver => "sqlserver", _ => throw new ArgumentException($"Unknown resource: {resource}") }; @@ -142,9 +141,7 @@ private static TestResourceNames GetResourcesToSkip() "eventhubs" => TestResourceNames.eventhubs, "basicservices" => TestResourceNames.redis | TestResourceNames.postgres - | TestResourceNames.efnpgsql - | TestResourceNames.sqlserver - | TestResourceNames.efsqlserver, + | TestResourceNames.efnpgsql, "" or null => TestResourceNames.All, _ => throw new ArgumentException($"Unknown test scenario '{TestScenario}'") }; diff --git a/tests/Aspire.EndToEnd.Tests/IntegrationServicesTests.cs b/tests/Aspire.EndToEnd.Tests/IntegrationServicesTests.cs index b99281a60c1..4ed977e9c8c 100644 --- a/tests/Aspire.EndToEnd.Tests/IntegrationServicesTests.cs +++ b/tests/Aspire.EndToEnd.Tests/IntegrationServicesTests.cs @@ -26,8 +26,6 @@ public IntegrationServicesTests(ITestOutputHelper testOutput, IntegrationService [InlineData(TestResourceNames.postgres)] [InlineData(TestResourceNames.efnpgsql)] [InlineData(TestResourceNames.redis)] - [InlineData(TestResourceNames.sqlserver)] - [InlineData(TestResourceNames.efsqlserver)] public Task VerifyComponentWorks(TestResourceNames resourceName) => RunTestAsync(async () => { diff --git a/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs b/tests/Aspire.Hosting.SqlServer.Tests/AddSqlServerTests.cs similarity index 96% rename from tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs rename to tests/Aspire.Hosting.SqlServer.Tests/AddSqlServerTests.cs index d5f94554c2f..1e0162fbdd3 100644 --- a/tests/Aspire.Hosting.Tests/SqlServer/AddSqlServerTests.cs +++ b/tests/Aspire.Hosting.SqlServer.Tests/AddSqlServerTests.cs @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Tests.Utils; using Aspire.Hosting.Utils; using Microsoft.Extensions.DependencyInjection; using System.Net.Sockets; using Xunit; -namespace Aspire.Hosting.Tests.SqlServer; +namespace Aspire.Hosting.SqlServer.Tests; public class AddSqlServerTests { @@ -18,7 +19,7 @@ public void AddSqlServerAddsGeneratedPasswordParameterWithUserSecretsParameterDe var sql = appBuilder.AddSqlServer("sql"); - Assert.IsType(sql.Resource.PasswordParameter.Default); + Assert.Equal("Aspire.Hosting.ApplicationModel.UserSecretsParameterDefault", sql.Resource.PasswordParameter.Default?.GetType().FullName); } [Fact] @@ -28,7 +29,7 @@ public void AddSqlServerDoesNotAddGeneratedPasswordParameterWithUserSecretsParam var sql = appBuilder.AddSqlServer("sql"); - Assert.IsNotType(sql.Resource.PasswordParameter.Default); + Assert.NotEqual("Aspire.Hosting.ApplicationModel.UserSecretsParameterDefault", sql.Resource.PasswordParameter.Default?.GetType().FullName); } [Fact] diff --git a/tests/Aspire.Hosting.SqlServer.Tests/Aspire.Hosting.SqlServer.Tests.csproj b/tests/Aspire.Hosting.SqlServer.Tests/Aspire.Hosting.SqlServer.Tests.csproj new file mode 100644 index 00000000000..204543d2b55 --- /dev/null +++ b/tests/Aspire.Hosting.SqlServer.Tests/Aspire.Hosting.SqlServer.Tests.csproj @@ -0,0 +1,19 @@ + + + + $(NetCurrent) + + + + + + + + + + + + + + + diff --git a/tests/Aspire.Hosting.SqlServer.Tests/SqlServerFunctionalTests.cs b/tests/Aspire.Hosting.SqlServer.Tests/SqlServerFunctionalTests.cs new file mode 100644 index 00000000000..1075fa13b08 --- /dev/null +++ b/tests/Aspire.Hosting.SqlServer.Tests/SqlServerFunctionalTests.cs @@ -0,0 +1,282 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Components.Common.Tests; +using Aspire.Hosting.Utils; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Polly; +using Xunit; +using Xunit.Abstractions; + +namespace Aspire.Hosting.SqlServer.Tests; + +public class SqlServerFunctionalTests(ITestOutputHelper testOutputHelper) +{ + [Fact] + [RequiresDocker] + public async Task VerifySqlServerResource() + { + var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); + var pipeline = new ResiliencePipelineBuilder() + .AddRetry(new() { MaxRetryAttempts = int.MaxValue, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2) }) + .Build(); + + using var builder = TestDistributedApplicationBuilder.Create(o => { }, testOutputHelper); + + var sqlserver = builder.AddSqlServer("sqlserver"); + var tempDb = sqlserver.AddDatabase("tempdb"); + + using var app = builder.Build(); + + await app.StartAsync(cts.Token); + + var hb = Host.CreateApplicationBuilder(); + + hb.Configuration[$"ConnectionStrings:{tempDb.Resource.Name}"] = await tempDb.Resource.ConnectionStringExpression.GetValueAsync(default); + + hb.AddSqlServerDbContext(tempDb.Resource.Name); + hb.AddSqlServerClient(tempDb.Resource.Name); + + using var host = hb.Build(); + + await host.StartAsync(); + + // Test SqlConnection + await pipeline.ExecuteAsync(async token => + { + var conn = host.Services.GetRequiredService(); + + if (conn.State != System.Data.ConnectionState.Open) + { + await conn.OpenAsync(token); + } + + var selectCommand = conn.CreateCommand(); + selectCommand.CommandText = $"SELECT 1"; + var results = await selectCommand.ExecuteReaderAsync(token); + + Assert.True(results.HasRows); + }, cts.Token); + + var dbContext = host.Services.GetRequiredService(); + + await dbContext.Database.EnsureCreatedAsync(cts.Token); + dbContext.Cars.Add(new TestDbContext.Car { Brand = "BatMobile" }); + await dbContext.SaveChangesAsync(cts.Token); + var cars = await dbContext.Cars.ToListAsync(cts.Token); + + Assert.Single(cars); + Assert.Equal("BatMobile", cars[0].Brand); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + [RequiresDocker] + public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume) + { + + string? volumeName = null; + string? bindMountPath = null; + + var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10)); + var pipeline = new ResiliencePipelineBuilder() + .AddRetry(new() { MaxRetryAttempts = int.MaxValue, BackoffType = DelayBackoffType.Linear, Delay = TimeSpan.FromSeconds(2) }) + .Build(); + + try + { + using var builder1 = TestDistributedApplicationBuilder.Create(o => { }, testOutputHelper); + + var sqlserver1 = builder1.AddSqlServer("sqlserver"); + var masterdb1 = sqlserver1.AddDatabase("master"); + + var password = sqlserver1.Resource.PasswordParameter.Value; + + if (useVolume) + { + // Use a deterministic volume name to prevent them from exhausting the machines if deletion fails + volumeName = VolumeNameGenerator.CreateVolumeName(sqlserver1, nameof(WithDataShouldPersistStateBetweenUsages)); + + // If the volume already exists (because of a crashing previous run), try to delete it + DockerUtils.AttemptDeleteDockerVolume(volumeName); + sqlserver1.WithDataVolume(volumeName); + } + else + { + bindMountPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(bindMountPath); + + sqlserver1.WithDataBindMount(bindMountPath); + + if (!OperatingSystem.IsWindows()) + { + // Change permissions for non-root accounts (container user account) + // c.f. https://learn.microsoft.com/sql/linux/sql-server-linux-docker-container-security?view=sql-server-ver16#storagepermissions + + // This is the minimal set to get the tests passing. + const UnixFileMode MsSqlPermissions = + UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | + UnixFileMode.OtherRead | UnixFileMode.OtherWrite | UnixFileMode.OtherExecute; + + File.SetUnixFileMode(bindMountPath, MsSqlPermissions); + File.SetUnixFileMode($"{bindMountPath}/data", MsSqlPermissions); + File.SetUnixFileMode($"{bindMountPath}/log", MsSqlPermissions); + File.SetUnixFileMode($"{bindMountPath}/secrets", MsSqlPermissions); + } + } + + using var app1 = builder1.Build(); + + await app1.StartAsync(); + + try + { + var hb1 = Host.CreateApplicationBuilder(); + + hb1.Configuration.AddInMemoryCollection(new Dictionary + { + [$"ConnectionStrings:{masterdb1.Resource.Name}"] = await masterdb1.Resource.ConnectionStringExpression.GetValueAsync(default), + }); + + hb1.AddSqlServerClient(masterdb1.Resource.Name); + + using var host1 = hb1.Build(); + + await host1.StartAsync(); + + await pipeline.ExecuteAsync(async token => + { + var conn = host1.Services.GetRequiredService(); + + if (conn.State != System.Data.ConnectionState.Open) + { + await conn.OpenAsync(token); + } + + var command = conn.CreateCommand(); + command.CommandText = """ + DROP TABLE IF EXISTS [Cars]; + CREATE TABLE [Cars] ([Brand] nvarchar(max) NOT NULL); + INSERT INTO [Cars] ([Brand]) VALUES ('BatMobile'); + SELECT * FROM [Cars]; + """; + + var results = await command.ExecuteReaderAsync(token); + + Assert.True(results.HasRows); + }, cts.Token); + + await app1.StopAsync(); + + await pipeline.ExecuteAsync(async token => + { + var conn = host1.Services.GetRequiredService(); + + try + { + await conn.OpenAsync(token); + } + catch + { + // Failing means the database is correctly down + return; + } + + Assert.Fail("Waiting for database to be down"); + }, cts.Token); + } + finally + { + // Stops the container, or the Volume/mount would still be in use + await app1.StopAsync(); + } + + using var builder2 = TestDistributedApplicationBuilder.Create(o => { }, testOutputHelper); + var passwordParameter2 = builder2.AddParameter("pwd"); + + builder2.Configuration["Parameters:pwd"] = password; + + var sqlserver2 = builder2.AddSqlServer("sqlserver", passwordParameter2); + var masterdb2 = sqlserver2.AddDatabase("master"); + + if (useVolume) + { + sqlserver2.WithDataVolume(volumeName); + } + else + { + sqlserver2.WithDataBindMount(bindMountPath!); + } + + using (var app2 = builder2.Build()) + { + await app2.StartAsync(); + try + { + var hb2 = Host.CreateApplicationBuilder(); + + hb2.Configuration.AddInMemoryCollection(new Dictionary + { + [$"ConnectionStrings:{masterdb2.Resource.Name}"] = await masterdb2.Resource.ConnectionStringExpression.GetValueAsync(default), + }); + + hb2.AddSqlServerClient(masterdb2.Resource.Name); + + using (var host2 = hb2.Build()) + { + await host2.StartAsync(); + + await pipeline.ExecuteAsync(async token => + { + var conn = host2.Services.GetRequiredService(); + + if (conn.State != System.Data.ConnectionState.Open) + { + await conn.OpenAsync(token); + } + }, cts.Token); + + var conn = host2.Services.GetRequiredService(); + var command = conn.CreateCommand(); + command.CommandText = $"SELECT * FROM [Cars];"; + var results = await command.ExecuteReaderAsync(cts.Token); + + Assert.True(await results.ReadAsync(cts.Token)); + Assert.Equal("BatMobile", results.GetString(0)); + Assert.False(await results.ReadAsync(cts.Token)); + } + } + finally + { + // Stops the container, or the Volume/mount would still be in use + await app2.StopAsync(); + } + } + + } + finally + { + if (volumeName is not null) + { + DockerUtils.AttemptDeleteDockerVolume(volumeName); + } + + if (bindMountPath is not null) + { + try + { + Directory.Delete(bindMountPath, recursive: true); + } + catch + { + // Don't fail test if we can't clean the temporary folder + } + } + } + } +} diff --git a/tests/Aspire.Hosting.SqlServer.Tests/TestDbContext.cs b/tests/Aspire.Hosting.SqlServer.Tests/TestDbContext.cs new file mode 100644 index 00000000000..5df20cae69a --- /dev/null +++ b/tests/Aspire.Hosting.SqlServer.Tests/TestDbContext.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore; + +namespace Aspire.Hosting.SqlServer.Tests; + +public class TestDbContext : DbContext +{ + public TestDbContext(DbContextOptions options) : base(options) + { + Options = options; + } + + public DbContextOptions Options { get; } + + public DbSet Cars => Set(); + + public class Car + { + public int Id { get; set; } + public required string Brand { get; set; } + } +} diff --git a/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj b/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj index bde3fdb6373..7e14fe72ce5 100644 --- a/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj +++ b/tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj @@ -17,7 +17,6 @@ - diff --git a/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs b/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs index 608b2614d38..74fddb19fa3 100644 --- a/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs +++ b/tests/Aspire.Hosting.Tests/DistributedApplicationTests.cs @@ -564,7 +564,6 @@ public async Task KubernetesHasResourceNameForContainersAndExes() "mongodb", "oracledatabase", "cosmos", - "sqlserver", "mysql", "rabbitmq", "kafka" diff --git a/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs b/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs index 00da133143f..5766fc1455c 100644 --- a/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs +++ b/tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs @@ -416,7 +416,6 @@ public void VerifyTestProgramFullManifest() "ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true", "HTTP_PORTS": "{integrationservicea.bindings.http.targetPort}", "SKIP_RESOURCES": "None", - "ConnectionStrings__tempdb": "{tempdb.connectionString}", "ConnectionStrings__redis": "{redis.connectionString}", "ConnectionStrings__postgresdb": "{postgresdb.connectionString}", "ConnectionStrings__freepdb1": "{freepdb1.connectionString}", @@ -436,27 +435,6 @@ public void VerifyTestProgramFullManifest() } } }, - "sqlserver": { - "type": "container.v0", - "connectionString": "Server={sqlserver.bindings.tcp.host},{sqlserver.bindings.tcp.port};User ID=sa;Password={sqlserver-password.value};TrustServerCertificate=true", - "image": "{{SqlServerContainerImageTags.Registry}}/{{SqlServerContainerImageTags.Image}}:{{SqlServerContainerImageTags.Tag}}", - "env": { - "ACCEPT_EULA": "Y", - "MSSQL_SA_PASSWORD": "{sqlserver-password.value}" - }, - "bindings": { - "tcp": { - "scheme": "tcp", - "protocol": "tcp", - "transport": "tcp", - "targetPort": 1433 - } - } - }, - "tempdb": { - "type": "value.v0", - "connectionString": "{sqlserver.connectionString};Database=tempdb" - }, "redis": { "type": "container.v0", "connectionString": "{redis.bindings.tcp.host}:{redis.bindings.tcp.port}", @@ -531,24 +509,6 @@ public void VerifyTestProgramFullManifest() "principalType": "" } }, - "sqlserver-password": { - "type": "parameter.v0", - "value": "{sqlserver-password.inputs.value}", - "inputs": { - "value": { - "type": "string", - "secret": true, - "default": { - "generate": { - "minLength": 22, - "minLower": 1, - "minUpper": 1, - "minNumeric": 1 - } - } - } - } - }, "postgres-password": { "type": "parameter.v0", "value": "{postgres-password.inputs.value}", diff --git a/tests/testproject/Common/TestResourceNames.cs b/tests/testproject/Common/TestResourceNames.cs index b1820c354e7..cba02950f4b 100644 --- a/tests/testproject/Common/TestResourceNames.cs +++ b/tests/testproject/Common/TestResourceNames.cs @@ -12,12 +12,10 @@ public enum TestResourceNames oracledatabase = 1 << 5, postgres = 1 << 7, redis = 1 << 9, - sqlserver = 1 << 10, efnpgsql = 1 << 11, eventhubs = 1 << 13, - efsqlserver = 1 << 16, efcosmos = 1 << 17, - All = cosmos | dashboard | oracledatabase | postgres | redis | sqlserver | efnpgsql | eventhubs | efsqlserver | efcosmos + All = cosmos | dashboard | oracledatabase | postgres | redis | efnpgsql | eventhubs | efcosmos } public static class TestResourceNamesExtensions diff --git a/tests/testproject/TestProject.AppHost/TestProgram.cs b/tests/testproject/TestProject.AppHost/TestProgram.cs index 8799328940b..e12c5071c7c 100644 --- a/tests/testproject/TestProject.AppHost/TestProgram.cs +++ b/tests/testproject/TestProject.AppHost/TestProgram.cs @@ -67,13 +67,6 @@ private TestProgram( IntegrationServiceABuilder = AppBuilder.AddProject("integrationservicea"); IntegrationServiceABuilder = IntegrationServiceABuilder.WithEnvironment("SKIP_RESOURCES", string.Join(',', resourcesToSkip)); - if (!resourcesToSkip.HasFlag(TestResourceNames.sqlserver) || !resourcesToSkip.HasFlag(TestResourceNames.efsqlserver)) - { - var sqlserverDbName = "tempdb"; - var sqlserver = AppBuilder.AddSqlServer("sqlserver") - .AddDatabase(sqlserverDbName); - IntegrationServiceABuilder = IntegrationServiceABuilder.WithReference(sqlserver); - } if (!resourcesToSkip.HasFlag(TestResourceNames.redis)) { var redis = AppBuilder.AddRedis("redis") diff --git a/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj b/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj index 07af2e71f98..b55b0133792 100644 --- a/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj +++ b/tests/testproject/TestProject.AppHost/TestProject.AppHost.csproj @@ -18,7 +18,6 @@ - diff --git a/tests/testproject/TestProject.IntegrationServiceA/Cosmos/EFCoreCosmosDbContext.cs b/tests/testproject/TestProject.IntegrationServiceA/Cosmos/EFCoreCosmosDbContext.cs index 316594207d4..3d5b0acbc5c 100644 --- a/tests/testproject/TestProject.IntegrationServiceA/Cosmos/EFCoreCosmosDbContext.cs +++ b/tests/testproject/TestProject.IntegrationServiceA/Cosmos/EFCoreCosmosDbContext.cs @@ -7,3 +7,8 @@ public class EFCoreCosmosDbContext(DbContextOptions optio { public DbSet Entries { get; set; } } + +public record Entry +{ + public Guid Id { get; set; } = Guid.NewGuid(); +} diff --git a/tests/testproject/TestProject.IntegrationServiceA/Program.cs b/tests/testproject/TestProject.IntegrationServiceA/Program.cs index 9204ab554a0..137f54803e7 100644 --- a/tests/testproject/TestProject.IntegrationServiceA/Program.cs +++ b/tests/testproject/TestProject.IntegrationServiceA/Program.cs @@ -9,14 +9,6 @@ ? TestResourceNamesExtensions.Parse(skipResourcesValue.Split(',', StringSplitOptions.RemoveEmptyEntries)) : TestResourceNames.None; -if (!resourcesToSkip.HasFlag(TestResourceNames.sqlserver)) -{ - builder.AddSqlServerClient("tempdb"); -} -if (!resourcesToSkip.HasFlag(TestResourceNames.efsqlserver)) -{ - builder.AddSqlServerDbContext("tempdb"); -} if (!resourcesToSkip.HasFlag(TestResourceNames.redis)) { builder.AddKeyedRedisClient("redis"); @@ -88,16 +80,6 @@ app.MapNpgsqlEFCoreApi(); } -if (!resourcesToSkip.HasFlag(TestResourceNames.sqlserver)) -{ - app.MapSqlServerApi(); -} - -if (!resourcesToSkip.HasFlag(TestResourceNames.efsqlserver)) -{ - app.MapEFCoreSqlServerApi(); -} - if (!resourcesToSkip.HasFlag(TestResourceNames.oracledatabase)) { app.MapOracleDatabaseApi(); diff --git a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/EFCoreSqlServerExtensions.cs b/tests/testproject/TestProject.IntegrationServiceA/SqlServer/EFCoreSqlServerExtensions.cs deleted file mode 100644 index e84992f4e21..00000000000 --- a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/EFCoreSqlServerExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.EntityFrameworkCore; - -public static class EFCoreSqlServerExtensions -{ - public static void MapEFCoreSqlServerApi(this WebApplication app) - { - app.MapGet("/efsqlserver/verify", VerifyEFCoreSqlServerAsync); - } - - private static async Task VerifyEFCoreSqlServerAsync(EFCoreSqlServerDbContext dbContext) - { - try - { - await dbContext.Database.EnsureCreatedAsync(); - - var entry = new Entry(); - await dbContext.Entries.AddAsync(entry); - await dbContext.SaveChangesAsync(); - - var entries = await dbContext.Entries.ToListAsync(); - return entries.Count == 1 ? Results.Ok("Success!") : Results.Problem($"Failed, got {entries.Count} entries"); - } - catch (Exception e) - { - return Results.Problem(e.ToString()); - } - } -} diff --git a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerDbContext.cs b/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerDbContext.cs deleted file mode 100644 index a2b59b65b66..00000000000 --- a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerDbContext.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.EntityFrameworkCore; - -public class EFCoreSqlServerDbContext(DbContextOptions options) : DbContext(options) -{ - public DbSet Entries { get; set; } -} -public record Entry -{ - public Guid Id { get; set; } = Guid.NewGuid(); -} diff --git a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerExtensions.cs b/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerExtensions.cs deleted file mode 100644 index bb6fa20198f..00000000000 --- a/tests/testproject/TestProject.IntegrationServiceA/SqlServer/SqlServerExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Text; -using Aspire.TestProject; -using Microsoft.Data.SqlClient; -using Polly; - -public static class SqlServerExtensions -{ - public static void MapSqlServerApi(this WebApplication app) - { - app.MapGet("/sqlserver/verify", VerifySqlServerAsync); - } - - private static async Task VerifySqlServerAsync(SqlConnection connection) - { - StringBuilder errorMessageBuilder = new(); - try - { - ResiliencePipeline pipeline = ResilienceUtils.GetDefaultResiliencePipelineBuilder(args => - { - errorMessageBuilder.AppendLine($"{Environment.NewLine}Service retry #{args.AttemptNumber} due to {args.Outcome.Exception}"); - return ValueTask.CompletedTask; - }).Build(); - - await pipeline.ExecuteAsync(async token => await connection.OpenAsync(token)); - - var command = connection.CreateCommand(); - command.CommandText = $"SELECT 1"; - var results = await command.ExecuteReaderAsync(); - - return results.HasRows ? Results.Ok("Success!") : Results.Problem("Failed"); - } - catch (Exception e) - { - return Results.Problem($"Error: {e}{Environment.NewLine}** Previous retries: {errorMessageBuilder}"); - } - } -} diff --git a/tests/testproject/TestProject.IntegrationServiceA/TestProject.IntegrationServiceA.csproj b/tests/testproject/TestProject.IntegrationServiceA/TestProject.IntegrationServiceA.csproj index 31ea345c040..4c5d780f51c 100644 --- a/tests/testproject/TestProject.IntegrationServiceA/TestProject.IntegrationServiceA.csproj +++ b/tests/testproject/TestProject.IntegrationServiceA/TestProject.IntegrationServiceA.csproj @@ -12,13 +12,11 @@ - -