From 3a2b575f584f061e126aa50e17eb77d47a4cdb21 Mon Sep 17 00:00:00 2001 From: Alireza Baloochi Date: Thu, 25 Jul 2024 23:49:50 +0330 Subject: [PATCH 1/2] Fix Elasticsearch Failing Tests --- .../ElasticsearchFunctionalTests.cs | 337 ++++++++---------- 1 file changed, 150 insertions(+), 187 deletions(-) diff --git a/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs b/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs index 8d0dcbc495c..ce5686712bb 100644 --- a/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs +++ b/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs @@ -7,17 +7,32 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Polly; using Xunit; +using Xunit.Abstractions; namespace Aspire.Hosting.Elasticsearch.Tests; -public class ElasticsearchFunctionalTests +public class ElasticsearchFunctionalTests(ITestOutputHelper testOutputHelper) { + private const string IndexName = "people"; + private static readonly Person s_person = new() + { + Id = 1, + FirstName = "Alireza", + LastName = "Baloochi" + }; + [Fact] [RequiresDocker] public async Task VerifyElasticsearchResource() { + var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10)); + var pipeline = new ResiliencePipelineBuilder() + .AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(10) }) + .Build(); + var builder = CreateDistributedApplicationBuilder(); var elasticsearch = builder.AddElasticsearch("elasticsearch"); @@ -30,7 +45,7 @@ public async Task VerifyElasticsearchResource() hb.Configuration.AddInMemoryCollection(new Dictionary { - [$"ConnectionStrings:{elasticsearch.Resource.Name}"] = await elasticsearch.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) + [$"ConnectionStrings:{elasticsearch.Resource.Name}"] = await elasticsearch.Resource.ConnectionStringExpression.GetValueAsync(default) }); hb.AddElasticsearchClient(elasticsearch.Resource.Name); @@ -39,238 +54,186 @@ public async Task VerifyElasticsearchResource() await host.StartAsync(); - var elasticsearchClient = host.Services.GetRequiredService(); - - var person = new Person - { - Id = 1, - FirstName = "Alireza", - LastName = "Baloochi" - }; - - var pipeline = new ResiliencePipelineBuilder() - .AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(3) }) - .AddTimeout(TimeSpan.FromSeconds(5)) - .Build(); - await pipeline.ExecuteAsync( async token => { - var indexResponse = await elasticsearchClient.IndexAsync(person, "people", "1",CancellationToken.None); - var getResponse = await elasticsearchClient.GetAsync("people", "1", CancellationToken.None); + var elasticsearchClient = host.Services.GetRequiredService(); - Assert.True(indexResponse.IsSuccess()); - Assert.True(getResponse.IsSuccess()); - Assert.NotNull(getResponse.Source); - Assert.Equal(person.Id, getResponse.Source?.Id); - }); + await CreateTestData(elasticsearchClient, testOutputHelper, token); + }, cts.Token); } - [Fact] - [SkipOnCI("https://github.com/dotnet/aspire/issues/4968")] + [Theory] + [InlineData(true)] + [InlineData(false)] [RequiresDocker] - public async Task WithDataVolumeShouldPersistStateBetweenUsages() + public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume) { - var builder1 = CreateDistributedApplicationBuilder(); - var elasticsearch1 = builder1.AddElasticsearch("elasticsearch"); - - // Use a deterministic volume name to prevent them from exhausting the machines if deletion fails - var volumeName = VolumeNameGenerator.CreateVolumeName(elasticsearch1, nameof(WithDataVolumeShouldPersistStateBetweenUsages)); - elasticsearch1.WithDataVolume(volumeName); - - var person = new Person - { - Id = 1, - FirstName = "Alireza", - LastName = "Baloochi" - }; - + var cts = new CancellationTokenSource(TimeSpan.FromMinutes(10)); var pipeline = new ResiliencePipelineBuilder() - .AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(3) }) - .AddTimeout(TimeSpan.FromSeconds(5)) - .Build(); + .AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(10) }) + .Build(); - using (var app = builder1.Build()) - { - await app.StartAsync(); + string? volumeName = null; + string? bindMountPath = null; - var hb = Host.CreateApplicationBuilder(); + try + { + var builder1 = CreateDistributedApplicationBuilder(); + var password = "passw0rd1"; - hb.Configuration.AddInMemoryCollection(new Dictionary + var passwordParameter = builder1.AddParameter("pwd"); + builder1.Configuration["Parameters:pwd"] = password; + var elasticsearch1 = builder1.AddElasticsearch("elasticsearch", passwordParameter); + if (useVolume) { - [$"ConnectionStrings:{elasticsearch1.Resource.Name}"] = await elasticsearch1.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) - }); + // Use a deterministic volume name to prevent them from exhausting the machines if deletion fails + volumeName = VolumeNameGenerator.CreateVolumeName(elasticsearch1, nameof(WithDataShouldPersistStateBetweenUsages)); - hb.AddElasticsearchClient(elasticsearch1.Resource.Name); - - using (var host = hb.Build()) + // if the volume already exists (because of a crashing previous run), try to delete it + DockerUtils.AttemptDeleteDockerVolume(volumeName); + elasticsearch1.WithDataVolume(volumeName); + } + else { - await host.StartAsync(); - - await pipeline.ExecuteAsync( - async token => - { - var elasticsearchClient = host.Services.GetRequiredService(); - - var indexResponse = await elasticsearchClient.IndexAsync(person, "people", "1", CancellationToken.None); - - Assert.True(indexResponse.IsSuccess()); - }); + bindMountPath = Directory.CreateTempSubdirectory().FullName; + elasticsearch1.WithDataBindMount(bindMountPath); } - // Stops the container, or the Volume would still be in use - await app.StopAsync(); - } - - var builder2 = CreateDistributedApplicationBuilder(); - var elasticsearch2 = builder2.AddElasticsearch("elasticsearch").WithDataVolume(volumeName); - - using (var app = builder2.Build()) - { - await app.StartAsync(); - - var hb = Host.CreateApplicationBuilder(); - - hb.Configuration.AddInMemoryCollection(new Dictionary + using (var app = builder1.Build()) { - [$"ConnectionStrings:{elasticsearch2.Resource.Name}"] = await elasticsearch2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) - }); + await app.StartAsync(); - hb.AddElasticsearchClient(elasticsearch2.Resource.Name); + try + { + var hb = Host.CreateApplicationBuilder(); - using (var host = hb.Build()) - { - await host.StartAsync(); - await pipeline.ExecuteAsync( - async token => + hb.Configuration.AddInMemoryCollection(new Dictionary { - var elasticsearchClient = host.Services.GetRequiredService(); - - var getResponse = await elasticsearchClient.GetAsync("people", "1", CancellationToken.None); - - Assert.True(getResponse.IsSuccess()); - Assert.NotNull(getResponse.Source); - Assert.Equal(person.Id, getResponse.Source?.Id); + [$"ConnectionStrings:{elasticsearch1.Resource.Name}"] = await elasticsearch1.Resource.ConnectionStringExpression.GetValueAsync(default) }); - } - - // Stops the container, or the Volume would still be in use - await app.StopAsync(); - } - - DockerUtils.AttemptDeleteDockerVolume(volumeName); - } - - [Fact] - [SkipOnCI("https://github.com/dotnet/aspire/issues/4968")] - [RequiresDocker] - public async Task WithDataBindMountShouldPersistStateBetweenUsages() - { - var bindMountPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - - if (!Directory.Exists(bindMountPath)) - { - Directory.CreateDirectory(bindMountPath); - } - - var builder1 = CreateDistributedApplicationBuilder(); - var elasticsearch1 = builder1.AddElasticsearch("elasticsearch").WithDataBindMount(bindMountPath); - - var person = new Person - { - Id = 1, - FirstName = "Alireza", - LastName = "Baloochi" - }; - - var pipeline = new ResiliencePipelineBuilder() - .AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(3) }) - .AddTimeout(TimeSpan.FromSeconds(5)) - .Build(); - - using (var app = builder1.Build()) - { - await app.StartAsync(); + hb.AddElasticsearchClient(elasticsearch1.Resource.Name); - var hb = Host.CreateApplicationBuilder(); + using (var host = hb.Build()) + { + await host.StartAsync(); + + await pipeline.ExecuteAsync( + async token => + { + var elasticsearchClient = host.Services.GetRequiredService(); + await CreateTestData(elasticsearchClient, testOutputHelper, token); + }, cts.Token); + } + } + finally + { + // Stops the container, or the Volume would still be in use + await app.StopAsync(); + } + } + var builder2 = CreateDistributedApplicationBuilder(); + passwordParameter = builder2.AddParameter("pwd"); + builder2.Configuration["Parameters:pwd"] = password; + var elasticsearch2 = builder2.AddElasticsearch("elasticsearch", passwordParameter); - hb.Configuration.AddInMemoryCollection(new Dictionary + if (useVolume) { - [$"ConnectionStrings:{elasticsearch1.Resource.Name}"] = await elasticsearch1.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) - }); - - hb.AddElasticsearchClient(elasticsearch1.Resource.Name); + elasticsearch2.WithDataVolume(volumeName); + } + else + { + elasticsearch2.WithDataBindMount(bindMountPath!); + } - using (var host = hb.Build()) + using (var app = builder2.Build()) { - await host.StartAsync(); + await app.StartAsync(); + + try + { + var hb = Host.CreateApplicationBuilder(); - await pipeline.ExecuteAsync( - async token => + hb.Configuration.AddInMemoryCollection(new Dictionary { - var elasticsearchClient = host.Services.GetRequiredService(); + [$"ConnectionStrings:{elasticsearch2.Resource.Name}"] = await elasticsearch2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) + }); - var indexResponse = await elasticsearchClient.IndexAsync(person, "people", "1", CancellationToken.None); + hb.AddElasticsearchClient(elasticsearch2.Resource.Name); - Assert.True(indexResponse.IsSuccess()); - }); + using (var host = hb.Build()) + { + await host.StartAsync(); + await pipeline.ExecuteAsync( + async token => + { + var elasticsearchClient = host.Services.GetRequiredService(); + + var getResponse = await elasticsearchClient.GetAsync(IndexName, s_person.Id, token); + + Assert.True(getResponse.IsSuccess()); + Assert.NotNull(getResponse.Source); + Assert.Equal(s_person.Id, getResponse.Source?.Id); + }, cts.Token); + + } + } + finally + { + // Stops the container, or the Volume would still be in use + await app.StopAsync(); + } } - // Stops the container, or the Volume would still be in use - await app.StopAsync(); } - - var builder2 = CreateDistributedApplicationBuilder(); - var elasticsearch2 = builder2.AddElasticsearch("elasticsearch").WithDataBindMount(bindMountPath); - - using (var app = builder2.Build()) + finally { - await app.StartAsync(); - - var hb = Host.CreateApplicationBuilder(); - - hb.Configuration.AddInMemoryCollection(new Dictionary + if (volumeName is not null) { - [$"ConnectionStrings:{elasticsearch2.Resource.Name}"] = await elasticsearch2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) - }); - - hb.AddElasticsearchClient(elasticsearch2.Resource.Name); + DockerUtils.AttemptDeleteDockerVolume(volumeName); + } - using (var host = hb.Build()) + if (bindMountPath is not null) { - await host.StartAsync(); - await pipeline.ExecuteAsync( - async token => - { - var elasticsearchClient = host.Services.GetRequiredService(); + try + { + Directory.Delete(bindMountPath, recursive: true); + } + catch + { + // Don't fail test if we can't clean the temporary folder + } + } + } + } - var getResponse = await elasticsearchClient.GetAsync("people", "1", CancellationToken.None); +#pragma warning disable IDE0060 // Remove unused parameter + private static async Task CreateTestData(ElasticsearchClient elasticsearchClient, ITestOutputHelper testOutputHelper, CancellationToken cancellationToken) +#pragma warning restore IDE0060 // Remove unused parameter + { + var indexResponse = await elasticsearchClient.IndexAsync(s_person, IndexName, s_person.Id, cancellationToken); - Assert.True(getResponse.IsSuccess()); - Assert.NotNull(getResponse.Source); - Assert.Equal(person.Id, getResponse.Source?.Id); - }); + var getResponse = await elasticsearchClient.GetAsync(IndexName, s_person.Id, cancellationToken); - } + testOutputHelper.WriteLine(indexResponse.DebugInformation); + testOutputHelper.WriteLine(getResponse.DebugInformation); - // Stops the container, or the Volume would still be in use - await app.StopAsync(); - } + Assert.True(indexResponse.IsSuccess()); + Assert.True(getResponse.IsSuccess()); + Assert.NotNull(getResponse.Source); + Assert.Equal(s_person.Id, getResponse.Source?.Id); - try - { - Directory.Delete(bindMountPath); - } - catch - { - // Don't fail test if we can't clean the temporary folder - } + //Assert.True(indexResponse.IsSuccess()); } - private static TestDistributedApplicationBuilder CreateDistributedApplicationBuilder() => - TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(); + private TestDistributedApplicationBuilder CreateDistributedApplicationBuilder() + { + var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry(); + builder.Services.AddXunitLogging(testOutputHelper); + return builder; + } private sealed class Person { From 39cc31b118a8b766bd4a07d7f36140fab3e3139f Mon Sep 17 00:00:00 2001 From: Alireza Baloochi Date: Sat, 3 Aug 2024 00:30:46 +0330 Subject: [PATCH 2/2] Clean code --- .../ElasticsearchFunctionalTests.cs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs b/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs index ce5686712bb..13caa9fbafb 100644 --- a/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs +++ b/tests/Aspire.Hosting.Elasticsearch.Tests/ElasticsearchFunctionalTests.cs @@ -4,7 +4,6 @@ using Aspire.Components.Common.Tests; using Aspire.Hosting.Utils; using Elastic.Clients.Elasticsearch; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -43,10 +42,7 @@ public async Task VerifyElasticsearchResource() var hb = Host.CreateApplicationBuilder(); - hb.Configuration.AddInMemoryCollection(new Dictionary - { - [$"ConnectionStrings:{elasticsearch.Resource.Name}"] = await elasticsearch.Resource.ConnectionStringExpression.GetValueAsync(default) - }); + hb.Configuration[$"ConnectionStrings:{elasticsearch.Resource.Name}"] = await elasticsearch.Resource.ConnectionStringExpression.GetValueAsync(default); hb.AddElasticsearchClient(elasticsearch.Resource.Name); @@ -81,11 +77,11 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume) try { var builder1 = CreateDistributedApplicationBuilder(); - var password = "passw0rd1"; + + var elasticsearch1 = builder1.AddElasticsearch("elasticsearch"); + + var password = elasticsearch1.Resource.PasswordParameter.Value; - var passwordParameter = builder1.AddParameter("pwd"); - builder1.Configuration["Parameters:pwd"] = password; - var elasticsearch1 = builder1.AddElasticsearch("elasticsearch", passwordParameter); if (useVolume) { // Use a deterministic volume name to prevent them from exhausting the machines if deletion fails @@ -109,10 +105,7 @@ public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume) { var hb = Host.CreateApplicationBuilder(); - hb.Configuration.AddInMemoryCollection(new Dictionary - { - [$"ConnectionStrings:{elasticsearch1.Resource.Name}"] = await elasticsearch1.Resource.ConnectionStringExpression.GetValueAsync(default) - }); + hb.Configuration[$"ConnectionStrings:{elasticsearch1.Resource.Name}"] = await elasticsearch1.Resource.ConnectionStringExpression.GetValueAsync(default); hb.AddElasticsearchClient(elasticsearch1.Resource.Name); @@ -134,10 +127,11 @@ await pipeline.ExecuteAsync( await app.StopAsync(); } } + var builder2 = CreateDistributedApplicationBuilder(); - passwordParameter = builder2.AddParameter("pwd"); + var passwordParameter2 = builder2.AddParameter("pwd"); builder2.Configuration["Parameters:pwd"] = password; - var elasticsearch2 = builder2.AddElasticsearch("elasticsearch", passwordParameter); + var elasticsearch2 = builder2.AddElasticsearch("elasticsearch", passwordParameter2); if (useVolume) { @@ -156,10 +150,7 @@ await pipeline.ExecuteAsync( { var hb = Host.CreateApplicationBuilder(); - hb.Configuration.AddInMemoryCollection(new Dictionary - { - [$"ConnectionStrings:{elasticsearch2.Resource.Name}"] = await elasticsearch2.Resource.ConnectionStringExpression.GetValueAsync(CancellationToken.None) - }); + hb.Configuration[$"ConnectionStrings:{elasticsearch2.Resource.Name}"] = await elasticsearch2.Resource.ConnectionStringExpression.GetValueAsync(default); hb.AddElasticsearchClient(elasticsearch2.Resource.Name); @@ -209,11 +200,9 @@ await pipeline.ExecuteAsync( } } -#pragma warning disable IDE0060 // Remove unused parameter private static async Task CreateTestData(ElasticsearchClient elasticsearchClient, ITestOutputHelper testOutputHelper, CancellationToken cancellationToken) -#pragma warning restore IDE0060 // Remove unused parameter { - var indexResponse = await elasticsearchClient.IndexAsync(s_person, IndexName, s_person.Id, cancellationToken); + var indexResponse = await elasticsearchClient.IndexAsync(s_person, IndexName, s_person.Id, cancellationToken); var getResponse = await elasticsearchClient.GetAsync(IndexName, s_person.Id, cancellationToken); @@ -224,8 +213,6 @@ private static async Task CreateTestData(ElasticsearchClient elasticsearchClient Assert.True(getResponse.IsSuccess()); Assert.NotNull(getResponse.Source); Assert.Equal(s_person.Id, getResponse.Source?.Id); - - //Assert.True(indexResponse.IsSuccess()); } private TestDistributedApplicationBuilder CreateDistributedApplicationBuilder()