Skip to content

Commit

Permalink
Extract Aspire.Hosting.Milvus.Tests project (#4875)
Browse files Browse the repository at this point in the history
* Extract Aspire.Hosting.Milvus.Tests project

Contributes to #3185
Contributes to #4294

* Update tests/Aspire.Hosting.Milvus.Tests/MilvusFunctionalTests.cs

Co-authored-by: Ankit Jain <[email protected]>

* Update tests/Aspire.Hosting.Milvus.Tests/Aspire.Hosting.Milvus.Tests.csproj

Co-authored-by: Ankit Jain <[email protected]>

* Apply feedback

* remove launch profile

* Remove milvus from the EndToEnd tests

---------

Co-authored-by: Eric Erhardt <[email protected]>
Co-authored-by: Ankit Jain <[email protected]>
Co-authored-by: Sébastien Ros <[email protected]>
  • Loading branch information
4 people authored Jul 22, 2024
1 parent 0600f06 commit 2576de3
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 98 deletions.
20 changes: 18 additions & 2 deletions Aspire.sln
Original file line number Diff line number Diff line change
Expand Up @@ -505,10 +505,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dockerfile", "dockerfile",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "withdockerfile", "withdockerfile", "{5B5DC91B-5754-4CE5-B3C0-FA1584B916A0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WithDockerfile.AppHost", "playground\withdockerfile\WithDockerfile.AppHost\WithDockerfile.AppHost.csproj", "{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Containers.Tests", "tests\Aspire.Hosting.Containers.Tests\Aspire.Hosting.Containers.Tests.csproj", "{588CD2D7-EE70-43C1-8233-330854BDF53C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WithDockerfile.AppHost", "playground\withdockerfile\WithDockerfile.AppHost\WithDockerfile.AppHost.csproj", "{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Elastic.Clients.Elasticsearch.Tests", "tests\Aspire.Elastic.Clients.Elasticsearch.Tests\Aspire.Elastic.Clients.Elasticsearch.Tests.csproj", "{D5B392A4-29CD-41F9-8847-0C211C832713}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "python", "python", "{7123AB7A-A4FD-4F64-8B05-D2DD0C3E2ABC}"
Expand All @@ -527,6 +526,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.MySql.Tests", "tests\Aspire.Hosting.MySql.Tests\Aspire.Hosting.MySql.Tests.csproj", "{F3F33CF8-A2BB-4351-8501-A6884C5126FE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aspire.Hosting.Kafka.Tests", "tests\Aspire.Hosting.Kafka.Tests\Aspire.Hosting.Kafka.Tests.csproj", "{0A83AA67-221E-44B4-9BA9-DC64DC17949E}"
Expand Down Expand Up @@ -1377,6 +1377,10 @@ Global
{588CD2D7-EE70-43C1-8233-330854BDF53C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{588CD2D7-EE70-43C1-8233-330854BDF53C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{588CD2D7-EE70-43C1-8233-330854BDF53C}.Release|Any CPU.Build.0 = Release|Any CPU
{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9}.Release|Any CPU.Build.0 = Release|Any CPU
{D5B392A4-29CD-41F9-8847-0C211C832713}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5B392A4-29CD-41F9-8847-0C211C832713}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5B392A4-29CD-41F9-8847-0C211C832713}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -1393,6 +1397,14 @@ Global
{1BC02557-B78B-48CE-9D3C-488A6B7672F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BC02557-B78B-48CE-9D3C-488A6B7672F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BC02557-B78B-48CE-9D3C-488A6B7672F4}.Release|Any CPU.Build.0 = Release|Any CPU
{8E2AA85E-C351-47B4-AF91-58557FAD5840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E2AA85E-C351-47B4-AF91-58557FAD5840}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E2AA85E-C351-47B4-AF91-58557FAD5840}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E2AA85E-C351-47B4-AF91-58557FAD5840}.Release|Any CPU.Build.0 = Release|Any CPU
{986886B7-0E38-4890-92C3-5B46DE322DAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{986886B7-0E38-4890-92C3-5B46DE322DAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{986886B7-0E38-4890-92C3-5B46DE322DAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{986886B7-0E38-4890-92C3-5B46DE322DAF}.Release|Any CPU.Build.0 = Release|Any CPU
{7425E5B2-BC47-4521-AC40-B8CECA329E08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7425E5B2-BC47-4521-AC40-B8CECA329E08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7425E5B2-BC47-4521-AC40-B8CECA329E08}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -1680,6 +1692,8 @@ Global
{9FAE1602-2C69-4D24-8655-A164489441E8} = {C424395C-1235-41A4-BF55-07880A04368C}
{DF00FDA3-D3EC-4E07-B4EC-0EBB57A813A4} = {77CFE74A-32EE-400C-8930-5025E8555256}
{5CB63205-24F4-4388-A41B-BAF3BEA59866} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{588CD2D7-EE70-43C1-8233-330854BDF53C} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{E6BE41D3-872C-47D2-B5B1-78C37AFAEAF9} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
{9357EC71-823B-433A-9993-B7CB2FA082D1} = {B80354C7-BE58-43F6-8928-9F3A74AB7F47}
{3F7B206E-5457-458F-AA81-9449FA3C1B5C} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{6C71A90C-30AE-45D7-9347-D66F9B257CBE} = {D173887B-AF42-4576-B9C1-96B9E9B3D9C0}
Expand All @@ -1695,6 +1709,8 @@ Global
{C424395C-1235-41A4-BF55-07880A04368C} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{830A89EC-4029-4753-B25A-068BAE37DEC7} = {4981B3A5-4AFD-4191-BF7D-8692D9783D60}
{1BC02557-B78B-48CE-9D3C-488A6B7672F4} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{8E2AA85E-C351-47B4-AF91-58557FAD5840} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{986886B7-0E38-4890-92C3-5B46DE322DAF} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{7425E5B2-BC47-4521-AC40-B8CECA329E08} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{8E2AA85E-C351-47B4-AF91-58557FAD5840} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
{F3F33CF8-A2BB-4351-8501-A6884C5126FE} = {830A89EC-4029-4753-B25A-068BAE37DEC7}
Expand Down
4 changes: 1 addition & 3 deletions tests/Aspire.EndToEnd.Tests/IntegrationServicesFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ public Task DumpComponentLogsAsync(TestResourceNames resource, ITestOutputHelper
TestResourceNames.cosmos or TestResourceNames.efcosmos => "cosmos",
TestResourceNames.eventhubs => "eventhubs",
TestResourceNames.garnet => "garnet",
TestResourceNames.milvus => "milvus",
TestResourceNames.mongodb => "mongodb",
TestResourceNames.oracledatabase => "oracledatabase",
TestResourceNames.postgres or TestResourceNames.efnpgsql => "postgres",
Expand Down Expand Up @@ -151,8 +150,7 @@ private static TestResourceNames GetResourcesToSkip()
| TestResourceNames.postgres
| TestResourceNames.efnpgsql
| TestResourceNames.sqlserver
| TestResourceNames.efsqlserver
| TestResourceNames.milvus,
| TestResourceNames.efsqlserver,
"" or null => TestResourceNames.All,
_ => throw new ArgumentException($"Unknown test scenario '{TestScenario}'")
};
Expand Down
1 change: 0 additions & 1 deletion tests/Aspire.EndToEnd.Tests/IntegrationServicesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public IntegrationServicesTests(ITestOutputHelper testOutput, IntegrationService
[InlineData(TestResourceNames.garnet)]
[InlineData(TestResourceNames.sqlserver)]
[InlineData(TestResourceNames.efsqlserver)]
[InlineData(TestResourceNames.milvus)]
public Task VerifyComponentWorks(TestResourceNames resourceName)
=> RunTestAsync(async () =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net.Sockets;
using Aspire.Hosting.Milvus;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Tests.Utils;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Aspire.Hosting.Tests.Milvus;
namespace Aspire.Hosting.Milvus.Tests;
public class AddMilvusTests
{
private const int MilvusPortGrpc = 19530;
Expand Down Expand Up @@ -104,7 +104,7 @@ public async Task MilvusClientAppWithReferenceContainsConnectionStrings()
var milvus = appBuilder.AddMilvus("my-milvus", pass)
.WithEndpoint("grpc", e => e.AllocatedEndpoint = new AllocatedEndpoint(e, "localhost", MilvusPortGrpc));

var projectA = appBuilder.AddProject<ProjectA>("projecta")
var projectA = appBuilder.AddProject<ProjectA>("projecta", o => o.ExcludeLaunchProfile = true)
.WithReference(milvus);

// Call environment variable callbacks.
Expand Down Expand Up @@ -209,7 +209,5 @@ public void AddMilvusWithSpecifyingPorts()
private sealed class ProjectA : IProjectMetadata
{
public string ProjectPath => "projectA";

public LaunchSettings LaunchSettings { get; } = new();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetCurrent)</TargetFramework>
<NoWarn>$(NoWarn);CS8002</NoWarn> <!-- Milvus.Client packages are not signed -->
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Aspire.Hosting.AppHost\Aspire.Hosting.AppHost.csproj" />
<ProjectReference Include="..\..\src\Aspire.Hosting.Milvus\Aspire.Hosting.Milvus.csproj" />
<ProjectReference Include="..\..\src\Components\Aspire.Milvus.Client\Aspire.Milvus.Client.csproj" />
<ProjectReference Include="..\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
</ItemGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)src\Aspire.Hosting.Milvus\MilvusContainerImageTags.cs" />
<Compile Include="$(SharedDir)VolumeNameGenerator.cs" Link="Utils\VolumeNameGenerator.cs" />
</ItemGroup>
</Project>
244 changes: 244 additions & 0 deletions tests/Aspire.Hosting.Milvus.Tests/MilvusFunctionalTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// 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 Grpc.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Milvus.Client;
using Polly;
using Xunit;
using Xunit.Abstractions;

namespace Aspire.Hosting.Milvus.Tests;

public class MilvusFunctionalTests(ITestOutputHelper testOutputHelper)
{
// Right now can not set user and password for super user of Milvus at startup. default user and password is root:Milvus.
// https://github.com/milvus-io/milvus/issues/33058
private const string MilvusToken = "root:Milvus";
private const string CollectionName = "book";

[Fact]
[RequiresDocker]
public async Task VerifyMilvusResource()
{
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(3), ShouldHandle = new PredicateBuilder().Handle<RpcException>() })
.Build();

var builder = CreateDistributedApplicationBuilder();

builder.Configuration["Parameters:apikey"] = MilvusToken;
var apiKey = builder.AddParameter("apikey");
var milvus = builder.AddMilvus("milvus", apiKey: apiKey);
var db = milvus.AddDatabase("milvusdb", "db1");

using var app = builder.Build();

await app.StartAsync();

var hb = Host.CreateApplicationBuilder();

hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
[$"ConnectionStrings:{db.Resource.Name}"] = await db.Resource.ConnectionStringExpression.GetValueAsync(default)
});

hb.AddMilvusClient(db.Resource.Name);

using var host = hb.Build();

await host.StartAsync();

await pipeline.ExecuteAsync(
async token =>
{
var milvusClient = host.Services.GetRequiredService<MilvusClient>();

await milvusClient.CreateDatabaseAsync("db1", token);
await CreateTestDataAsync(milvusClient, token);

}, cts.Token);

}

private static async Task CreateTestDataAsync(MilvusClient milvusClient, CancellationToken token)
{
var collection = await milvusClient.CreateCollectionAsync(
CollectionName,
[
FieldSchema.Create<long>("book_id", isPrimaryKey:true),
FieldSchema.Create<long>("word_count"),
FieldSchema.CreateVarchar("book_name", 256),
FieldSchema.CreateFloatVector("book_intro", 2)
]
, cancellationToken: token);

var collections = await milvusClient.ListCollectionsAsync(cancellationToken: token);
Assert.Single(collections, c => c.Name == CollectionName);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
[RequiresDocker]
public async Task WithDataShouldPersistStateBetweenUsages(bool useVolume)
{
var dbname = "milvusdbtest";
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
var pipeline = new ResiliencePipelineBuilder()
.AddRetry(new() { MaxRetryAttempts = 10, Delay = TimeSpan.FromSeconds(3), ShouldHandle = new PredicateBuilder().Handle<RpcException>() })
.Build();

string? volumeName = null;
string? bindMountPath = null;

try
{
var builder1 = CreateDistributedApplicationBuilder();
builder1.Configuration["Parameters:apikey"] = MilvusToken;
var apiKey1 = builder1.AddParameter("apikey");
var milvus1 = builder1.AddMilvus("milvus1", apiKey1);
var db1 = milvus1.AddDatabase("milvusdb1", dbname);

if (useVolume)
{
// Use a deterministic volume name to prevent them from exhausting the machines if deletion fails
volumeName = VolumeNameGenerator.CreateVolumeName(milvus1, nameof(WithDataShouldPersistStateBetweenUsages));

// if the volume already exists (because of a crashing previous run), try to delete it
DockerUtils.AttemptDeleteDockerVolume(volumeName);
milvus1.WithDataVolume(volumeName);
}
else
{
bindMountPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
milvus1.WithDataBindMount(bindMountPath);
}

using (var app = builder1.Build())
{
await app.StartAsync();

try
{
var hb = Host.CreateApplicationBuilder();

hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
[$"ConnectionStrings:{db1.Resource.Name}"] = await db1.Resource.ConnectionStringExpression.GetValueAsync(default)
});

hb.AddMilvusClient(db1.Resource.Name);

using (var host = hb.Build())
{
await host.StartAsync();

await pipeline.ExecuteAsync(
async token =>
{
var milvusClient = host.Services.GetRequiredService<MilvusClient>();

await milvusClient.CreateDatabaseAsync(dbname, token);
await CreateTestDataAsync(milvusClient, token);

}, cts.Token);

}
}
finally
{
// Stops the container, or the Volume would still be in use
await app.StopAsync();
}
}

var builder2 = CreateDistributedApplicationBuilder();
builder2.Configuration["Parameters:apikey"] = MilvusToken;
var apiKey2 = builder2.AddParameter("apikey");
var milvus2 = builder2.AddMilvus("milvus2", apiKey2);
var db2 = milvus2.AddDatabase("milvusdb2", dbname);

if (useVolume)
{
milvus2.WithDataVolume(volumeName);
}
else
{
milvus2.WithDataBindMount(bindMountPath!);
}

using (var app = builder2.Build())
{
await app.StartAsync();

try
{
var hb = Host.CreateApplicationBuilder();

hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
[$"ConnectionStrings:{db2.Resource.Name}"] = await db2.Resource.ConnectionStringExpression.GetValueAsync(default)
});

hb.AddMilvusClient(db2.Resource.Name);

using (var host = hb.Build())
{
await host.StartAsync();

await pipeline.ExecuteAsync(
async token =>
{
var milvusClient = host.Services.GetRequiredService<MilvusClient>();

var collections = await milvusClient.ListCollectionsAsync(cancellationToken: token);

Assert.Single(collections, c => c.Name == CollectionName);

}, cts.Token);
}
}
finally
{
// Stops the container, or the Volume would still be in use
await app.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
}
}
}
}

private TestDistributedApplicationBuilder CreateDistributedApplicationBuilder()
{
var builder = TestDistributedApplicationBuilder.CreateWithTestContainerRegistry();
builder.Services.AddXunitLogging(testOutputHelper);
return builder;
}
}
Loading

0 comments on commit 2576de3

Please sign in to comment.