| File: YarpClusterTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Yarp.Tests\Aspire.Hosting.Yarp.Tests.csproj (Aspire.Hosting.Yarp.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Utils; using Yarp.ReverseProxy.Configuration; using Yarp.ReverseProxy.Forwarder; namespace Aspire.Hosting.Yarp.Tests; public class YarpClusterTests(ITestOutputHelper testOutputHelper) { [Fact] public void Create_YarpCluster_From_Raw_Strings() { var cluster = new YarpCluster("raw_cluster", "http://localhost:5000", "https://localhost:5001"); Assert.Equal("http://localhost:5000", cluster.Targets[0]); Assert.Equal("https://localhost:5001", cluster.Targets[1]); } [Fact] public void Create_YarpCluster_From_Endpoints_With_Names() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var resource = builder.AddResource(new TestResource("ServiceA")) .WithHttpEndpoint(name: "testendpoint") .WithHttpsEndpoint(name: "anotherendpoint"); var httpEndpoint = resource.GetEndpoint("testendpoint"); var httpsEndpoint = resource.GetEndpoint("anotherendpoint"); var httpCluster = new YarpCluster(httpEndpoint); Assert.Equal("http://_testendpoint.ServiceA", httpCluster.Targets[0]); var httpsCluster = new YarpCluster(httpsEndpoint); Assert.Equal("https://_anotherendpoint.ServiceA", httpsCluster.Targets[0]); } [Fact] public void Create_YarpCluster_From_Endpoints_Without_Names() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var resource = builder.AddResource(new TestResource("ServiceC")) .WithHttpEndpoint() .WithHttpsEndpoint(); var httpEndpoint = resource.GetEndpoint("http"); var httpsEndpoint = resource.GetEndpoint("https"); var httpCluster = new YarpCluster(httpEndpoint); Assert.Equal("http://_http.ServiceC", httpCluster.Targets[0]); var httpsCluster = new YarpCluster(httpsEndpoint); Assert.Equal("https://_https.ServiceC", httpsCluster.Targets[0]); } [Fact] public void Create_YarpCluster_From_Resource_With_One_Endpoint() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var httpService = builder.AddResource(new TestResource("ServiceC")) .WithHttpEndpoint() .WithHttpEndpoint(name: "grpc"); var httpCluster = new YarpCluster(httpService.Resource); Assert.Equal($"http://ServiceC", httpCluster.Targets[0]); var httpsService = builder.AddResource(new TestResource("ServiceD")) .WithHttpsEndpoint() .WithHttpsEndpoint(name: "grpc"); var httpsCluster = new YarpCluster(httpsService.Resource); Assert.Equal($"https://ServiceD", httpsCluster.Targets[0]); } [Fact] public void Create_YarpCluster_From_Resource_With_Both_Endpoints() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var serviceA = builder.AddResource(new TestResource("ServiceA")) .WithHttpEndpoint() .WithHttpsEndpoint(); var clusterA = new YarpCluster(serviceA.Resource); Assert.Equal($"https+http://ServiceA", clusterA.Targets[0]); } [Fact] public void AddCluster_WithStringDestination_CreatesCluster() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var cluster = config.AddCluster("test-cluster", (object)"http://localhost:5000"); Assert.NotNull(cluster); Assert.Single(cluster.Targets); Assert.Equal("http://localhost:5000", cluster.Targets[0]); }); } [Fact] public void AddCluster_WithUriDestination_CreatesCluster() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var uri = new Uri("https://example.com:8080"); var cluster = config.AddCluster("test-cluster", (object)uri); Assert.NotNull(cluster); Assert.Single(cluster.Targets); Assert.Equal(uri, cluster.Targets[0]); }); } [Fact] public void AddCluster_WithObjectArrayDestinations_CreatesCluster() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var destinations = new object[] { "http://localhost:5000", new Uri("http://localhost:5001") }; var cluster = config.AddCluster("test-cluster", destinations); Assert.NotNull(cluster); Assert.Equal(2, cluster.Targets.Length); Assert.Equal("http://localhost:5000", cluster.Targets[0]); }); } [Fact] public void AddCluster_WithNullObjectDestination_ThrowsArgumentException() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var ex = Assert.Throws<ArgumentException>(() => config.AddCluster("test-cluster", (object)null!)); Assert.Contains("IValueProvider, string, or Uri", ex.Message); }); } [Fact] public void AddCluster_WithNullObjectArrayDestinations_ThrowsArgumentNullException() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { Assert.Throws<ArgumentNullException>(() => config.AddCluster("test-cluster", (object[])null!)); }); } [Fact] public void AddCluster_WithEmptyDestinationsArray_ThrowsArgumentException() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { Assert.Throws<ArgumentException>(() => config.AddCluster("test-cluster", Array.Empty<object>())); }); } [Fact] public void AddCluster_WithInvalidDestinationType_ThrowsArgumentException() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var ex = Assert.Throws<ArgumentException>(() => config.AddCluster("test-cluster", new object[] { 123 })); Assert.Contains("IValueProvider, string, or Uri", ex.Message); }); } [Fact] public void AddCluster_WithMixedValidTypes_CreatesCluster() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { var uri = new Uri("https://example.com"); var refExpr = ReferenceExpression.Create($"http://localhost:5000"); var cluster = config.AddCluster("test-cluster", new object[] { "http://localhost:5000", uri, refExpr }); Assert.NotNull(cluster); Assert.Equal(3, cluster.Targets.Length); }); } [Fact] public void AddCluster_WithObjectOverload_ValidatesTypes() { using var builder = TestDistributedApplicationBuilder.Create(testOutputHelper); var yarp = builder.AddYarp("gateway"); yarp.WithConfiguration(config => { // Valid object type (string) var cluster = config.AddCluster("test-cluster", (object)"http://localhost:5000"); Assert.NotNull(cluster); Assert.Single(cluster.Targets); // Invalid object type var ex = Assert.Throws<ArgumentException>(() => config.AddCluster("test-cluster2", (object)123)); Assert.Contains("IValueProvider, string, or Uri", ex.Message); }); } [Fact] public void ClusterConfigDtos_StayInSyncWithYarpConfigurationTypes() { AssertDtoShapeMatchesYarpConfig<YarpForwarderRequestConfig, ForwarderRequestConfig>(); AssertDtoShapeMatchesYarpConfig<YarpHttpClientConfig, HttpClientConfig>(); AssertDtoShapeMatchesYarpConfig<YarpWebProxyConfig, WebProxyConfig>(); AssertDtoShapeMatchesYarpConfig<YarpSessionAffinityConfig, SessionAffinityConfig>(); AssertDtoShapeMatchesYarpConfig<YarpSessionAffinityCookieConfig, SessionAffinityCookieConfig>(); AssertDtoShapeMatchesYarpConfig<YarpHealthCheckConfig, HealthCheckConfig>(); AssertDtoShapeMatchesYarpConfig<YarpActiveHealthCheckConfig, ActiveHealthCheckConfig>(); AssertDtoShapeMatchesYarpConfig<YarpPassiveHealthCheckConfig, PassiveHealthCheckConfig>(); } private static void AssertDtoShapeMatchesYarpConfig<TDto, TYarp>() { var dtoProperties = typeof(TDto) .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Select(property => property.Name) .OrderBy(name => name) .ToArray(); var yarpProperties = typeof(TYarp) .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Select(property => property.Name) .OrderBy(name => name) .ToArray(); Assert.Equal(yarpProperties, dtoProperties); } private sealed class TestResource(string name) : IResourceWithServiceDiscovery { public string Name => name; public ResourceAnnotationCollection Annotations { get; } = new ResourceAnnotationCollection(); } }