Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 52 additions & 38 deletions Aspire.sln

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Aspire.Hosting.Redis/Aspire.Hosting.Redis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

<ItemGroup>
<InternalsVisibleTo Include="Aspire.Hosting.Tests" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should aim to remove this.

<InternalsVisibleTo Include="Aspire.Hosting.Redis.Tests" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sebastienros - can we remove this? What is it needed for?

Copy link
Member

@sebastienros sebastienros Jul 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RedisContainerImageTags
RedisCommanderConfigWriterHook

Are used in these tests and internal.
This is why Aspire.Hosting.Tests was already listed, because these tests are coming from this project (moved to this new Aspire.Hosting.Redis.Tests)

</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/Aspire.Hosting/Aspire.Hosting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@

<ItemGroup>
<InternalsVisibleTo Include="Aspire.Hosting.Tests" />
<InternalsVisibleTo Include="Aspire.Hosting.Redis.Tests" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace Aspire.Hosting.Tests.Redis;
namespace Aspire.Hosting.Redis.Tests;

public class AddRedisTests
{
Expand Down Expand Up @@ -125,7 +124,8 @@ public void WithRedisCommanderAddsRedisCommanderResource()
public void WithRedisCommanderSupportsChangingContainerImageValues()
{
var builder = DistributedApplication.CreateBuilder();
builder.AddRedis("myredis").WithRedisCommander(c => {
builder.AddRedis("myredis").WithRedisCommander(c =>
{
c.WithImageRegistry("example.mycompany.com");
c.WithImage("customrediscommander");
c.WithImageTag("someothertag");
Expand All @@ -142,7 +142,8 @@ public void WithRedisCommanderSupportsChangingContainerImageValues()
public void WithRedisCommanderSupportsChangingHostPort()
{
var builder = DistributedApplication.CreateBuilder();
builder.AddRedis("myredis").WithRedisCommander(c => {
builder.AddRedis("myredis").WithRedisCommander(c =>
{
c.WithHostPort(1000);
});

Expand Down Expand Up @@ -218,7 +219,7 @@ public void WithDataVolumeAddsVolumeAnnotation(bool? isReadOnly)

var volumeAnnotation = redis.Resource.Annotations.OfType<ContainerMountAnnotation>().Single();

Assert.Equal("Aspire.Hosting.Tests-myRedis-data", volumeAnnotation.Source);
Assert.Equal("Aspire.Hosting.Redis.Tests-myRedis-data", volumeAnnotation.Source);
Assert.Equal("/data", volumeAnnotation.Target);
Assert.Equal(ContainerMountType.Volume, volumeAnnotation.Type);
Assert.Equal(isReadOnly ?? false, volumeAnnotation.IsReadOnly);
Expand Down
38 changes: 38 additions & 0 deletions tests/Aspire.Hosting.Redis.Tests/Aspire.Hosting.Redis.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetCurrent)</TargetFramework>
<IsAspireHost>true</IsAspireHost>
<!--
CS8002: Referenced assembly does not have a strong name. MongoDB.Driver package is unsigned, we ignore that warning on purpose
-->
<NoWarn>$(NoWarn);CS8002</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Aspire.Hosting.AppHost\Aspire.Hosting.AppHost.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\..\src\Aspire.Hosting.Redis\Aspire.Hosting.Redis.csproj" IsAspireProjectResource="false" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<ProjectReference Include="..\..\src\Aspire.Hosting.AppHost\Aspire.Hosting.AppHost.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\..\src\Aspire.Hosting.Redis\Aspire.Hosting.Redis.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\..\src\Aspire.Hosting.AppHost\Aspire.Hosting.AppHost.csproj" />
<ProjectReference Include="..\..\src\Aspire.Hosting.Redis\Aspire.Hosting.Redis.csproj" />

Are these needed? This project doesn't have <IsAspireHost>true</IsAspireHost>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as you suggest

<ProjectReference Include="..\..\src\Components\Aspire.StackExchange.Redis\Aspire.StackExchange.Redis.csproj" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Newtonsoft.Json.Schema" />

<Compile Include="$(TestsSharedDir)Logging\*.cs" LinkBase="shared/Logging" />

<Compile Include="..\Aspire.Hosting.Tests\Utils\ArgumentEvaluator.cs" Link="Utils\ArgumentEvaluator.cs" />
<Compile Include="..\Aspire.Hosting.Tests\Utils\EnvironmentVariableEvaluator.cs" Link="Utils\EnvironmentVariableEvaluator.cs" />
<Compile Include="..\Aspire.Hosting.Tests\Utils\FileUtil.cs" Link="Utils\FileUtil.cs" />
<Compile Include="..\Aspire.Hosting.Tests\Utils\ManifestUtils.cs" Link="Utils\ManifestUtils.cs" />
<Compile Include="..\Aspire.Hosting.Tests\Utils\TestDistributedApplicationBuilder.cs" Link="Utils\TestDistributedApplicationBuilder.cs" />
<Compile Include="..\Aspire.Hosting.Tests\XunitAttributes.cs" Link="XunitAttributes.cs" />

<Content Include="$(RepoRoot)src\Schema\aspire-8.0.json" Link="Schema\aspire-8.0.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<Folder Include="Utils\" />
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions tests/Aspire.Hosting.Redis.Tests/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: This line is only required because we are using P2P references, not NuGet. It will not exist in real apps. -->
<Import Project="../../src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.props" />

</Project>
8 changes: 8 additions & 0 deletions tests/Aspire.Hosting.Redis.Tests/Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />

<!-- NOTE: This line is only required because we are using P2P references, not NuGet. It will not exist in real apps. -->
<Import Project="..\..\src\Aspire.Hosting.AppHost\build\Aspire.Hosting.AppHost.targets" />

</Project>
49 changes: 49 additions & 0 deletions tests/Aspire.Hosting.Redis.Tests/RedisFunctionalTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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.Utils;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;
using Xunit;

namespace Aspire.Hosting.Redis.Tests;

public class RedisFunctionalTests
{
[Fact]
public async Task VerifyRedisResource()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test adds value over top of the other Redis Hosting tests, since it actually connects a client to it.

It seems like a "middle of the ground" test. Not a unit test, but not a full end-to-end functional test like

[InlineData(TestResourceNames.redis)]
[InlineData(TestResourceNames.garnet)]
[InlineData(TestResourceNames.valkey)]
[InlineData(TestResourceNames.sqlserver)]
[InlineData(TestResourceNames.milvus)]
public Task VerifyComponentWorks(TestResourceNames resourceName)
=> RunTestAsync(async () =>
{
_integrationServicesFixture.EnsureAppHasResources(resourceName);
try
{
var response = await _integrationServicesFixture.IntegrationServiceA.HttpGetAsync("http", $"/{resourceName}/verify");
var responseContent = await response.Content.ReadAsStringAsync();
Assert.True(response.IsSuccessStatusCode, responseContent);
}
catch
{
await _integrationServicesFixture.DumpComponentLogsAsync(resourceName, _testOutput);
throw;
}
});

It is definitely simpler than the end-to-end tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these tests, they are a good smoke test. I do think they need to be broken up though. That way if I've made some changes in "redis" land I can run everything related to Redis just by pointing at a particular test assembly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's testing less than the current E2E tests, at least when looking from the Redis POV. The E2E is only creating a distinct web api project to use the component, that's the difference with this local functional test.

Do we still need the Redis portion of the E2E test if so? Or should we extract the current portion of the E2E test into a separate project?

For instance, could we not add more volumes testing with this new functional test project already?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's testing less than the current E2E tests

The part that the current E2E tests cover that aren't covered here is the "WithReference" portion that connects a separate project with a resource. This is being simulated in the current test with:

        hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
        {
            ["ConnectionStrings:redis"] = await redis.Resource.GetConnectionStringAsync()
        });

Do we still need the Redis portion of the E2E test if so? Or should we extract the current portion of the E2E test into a separate project?

I think once we have all the AppHost resource tests split into separate test assemblies, and FunctionalTests like this one for each resource, we can remove the bulk of the resources from the E2E test, and only keep maybe ~5 core resources being tested there. That way when a new resource/component gets added we don't need to update the E2E test and have it continually grow, like it is doing today.

{
var builder = TestDistributedApplicationBuilder.Create();

var redis = builder.AddRedis("redis");

using var app = builder.Build();

await app.StartAsync();

var hb = Host.CreateApplicationBuilder();

hb.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
["ConnectionStrings:redis"] = await redis.Resource.GetConnectionStringAsync()
});

hb.AddRedisClient("redis");

using var host = hb.Build();

await host.StartAsync();

var redisClient = host.Services.GetRequiredService<IConnectionMultiplexer>();

var db = redisClient.GetDatabase();

await db.StringSetAsync("key", "value");

var value = await db.StringGetAsync("key");

Assert.Equal("value", value);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// 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.Redis;
using Aspire.Hosting.Utils;
using Xunit;

Expand Down Expand Up @@ -54,8 +53,8 @@ public void WithImageMutatesImageNameOfLastAnnotation()
public void WithImageTagMutatesImageTag()
{
using var builder = TestDistributedApplicationBuilder.Create();
var redis = builder.AddRedis("redis").WithImageTag(RedisContainerImageTags.Tag);
Assert.Equal(RedisContainerImageTags.Tag, redis.Resource.Annotations.OfType<ContainerImageAnnotation>().Single().Tag);
var redis = builder.AddRedis("redis").WithImageTag("7.1");
Assert.Equal("7.1", redis.Resource.Annotations.OfType<ContainerImageAnnotation>().Single().Tag);
}

[Fact]
Expand All @@ -80,7 +79,7 @@ public void WithImageTagThrowsIfNoImageAnnotation()
using var builder = TestDistributedApplicationBuilder.Create();
var container = builder.AddResource(new TestContainerResource("testcontainer"));

var exception = Assert.Throws<InvalidOperationException>(() => container.WithImageTag(RedisContainerImageTags.Tag));
var exception = Assert.Throws<InvalidOperationException>(() => container.WithImageTag("7.1"));
Assert.Equal("The resource 'testcontainer' does not have a container image specified. Use WithImage to specify the container image and tag.", exception.Message);
}

Expand Down
13 changes: 2 additions & 11 deletions tests/Aspire.Hosting.Tests/ManifestGenerationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,23 +97,14 @@ public async Task WithContainerRegistryUpdatesContainerImageAnnotationsDuringPub
ContainerRegistryOverride = "myprivateregistry.company.com"
});

var redis = builder.AddRedis("redis");
var redis = builder.AddContainer("redis", "redis");
builder.Build().Run();

var redisManifest = await ManifestUtils.GetManifest(redis.Resource);
var expectedManifest = $$"""
{
"type": "container.v0",
"connectionString": "{redis.bindings.tcp.host}:{redis.bindings.tcp.port}",
"image": "myprivateregistry.company.com/{{RedisContainerImageTags.Image}}:{{RedisContainerImageTags.Tag}}",
"bindings": {
"tcp": {
"scheme": "tcp",
"protocol": "tcp",
"transport": "tcp",
"targetPort": 6379
}
}
"image": "myprivateregistry.company.com/redis:latest"
}
""";
Assert.Equal(expectedManifest, redisManifest.ToString());
Expand Down