File: YarpConfigGeneratorTests.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.Security.Authentication;
using Yarp.ReverseProxy.Configuration;
using Yarp.ReverseProxy.Forwarder;
using Yarp.ReverseProxy.LoadBalancing;
 
namespace Aspire.Hosting.Yarp.Tests;
 
public class YarpConfigGeneratorTests()
{
    #region Routes and Clusters configs
    private readonly List<RouteConfig> _validRoutes =
    [
        new RouteConfig
        {
            RouteId = "routeA",
            ClusterId = "cluster1",
            AuthorizationPolicy = "Default",
            RateLimiterPolicy = "Default",
            TimeoutPolicy = "Default",
            Timeout = TimeSpan.FromSeconds(1),
            CorsPolicy = "Default",
            Order = -1,
            MaxRequestBodySize = -1,
            OutputCachePolicy = "Default",
            Match = new RouteMatch
            {
                Hosts = new List<string> { "host-A" },
                Methods = new List<string> { "GET", "POST", "DELETE" },
                Path = "/apis/entities",
                Headers = new[]
                {
                    new RouteHeader
                    {
                        Name = "header1",
                        Values = new[] { "value1" },
                        IsCaseSensitive = true,
                        Mode = HeaderMatchMode.HeaderPrefix
                    }
                },
                QueryParameters = new[]
                {
                    new RouteQueryParameter
                    {
                        Name = "queryparam1",
                        Values = new[] { "value1" },
                        IsCaseSensitive = true,
                        Mode = QueryParameterMatchMode.Contains
                    }
                }
            },
            Transforms = new[]
            {
                new Dictionary<string, string>
                {
                    { "RequestHeadersCopy", "true" }
                },
                new Dictionary<string, string>
                {
                    { "PathRemovePrefix", "/apis" },
                },
                new Dictionary<string, string>
                {
                    { "PathPrefix", "/apis" }
                },
                new Dictionary<string, string>
                {
                    { "RequestHeader", "header1" },
                    { "Append", "foo" }
                }
            },
            Metadata = new Dictionary<string, string> { { "routeA-K1", "routeA-V1" }, { "routeA-K2", "routeA-V2" } }
        },
        new RouteConfig
        {
            RouteId = "routeB",
            ClusterId = "cluster2",
            Order = 2,
            MaxRequestBodySize = 1,
            Match = new RouteMatch
            {
                Hosts = new List<string> { "host-B" },
                Methods = new List<string> { "GET" },
                Path = "/apis/users",
                Headers = new[]
                {
                    new RouteHeader
                    {
                        Name = "header2",
                        Values = new[] { "value2" },
                        IsCaseSensitive = false,
                        Mode = HeaderMatchMode.ExactHeader
                    }
                },
                QueryParameters = new[]
                {
                    new RouteQueryParameter
                    {
                        Name = "queryparam2",
                        Values = new[] { "value2" },
                        IsCaseSensitive = true,
                        Mode = QueryParameterMatchMode.Contains
                    }
                }
            }
        }
    ];
 
    private readonly List<ClusterConfig> _validClusters =
    [
        new ClusterConfig
        {
            ClusterId = "cluster1",
            Destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase)
                    {
                        {
                            "destinationA",
                            new DestinationConfig
                            {
                                Address = "https://localhost:10000/destA",
                                Health = "https://localhost:20000/destA",
                                Metadata = new Dictionary<string, string> { { "destA-K1", "destA-V1" }, { "destA-K2", "destA-V2" } },
                                Host = "localhost"
                            }
                        },
                        {
                            "destinationB",
                            new DestinationConfig
                            {
                                Address = "https://localhost:10000/destB",
                                Health = "https://localhost:20000/destB",
                                Metadata = new Dictionary<string, string> { { "destB-K1", "destB-V1" }, { "destB-K2", "destB-V2" } },
                                Host = "localhost"
                            }
                        }
                    },
            HealthCheck = new HealthCheckConfig
            {
                Passive = new PassiveHealthCheckConfig
                {
                    Enabled = true,
                    Policy = "FailureRate",
                    ReactivationPeriod = TimeSpan.FromMinutes(5)
                },
                Active = new ActiveHealthCheckConfig
                {
                    Enabled = true,
                    Interval = TimeSpan.FromSeconds(4),
                    Timeout = TimeSpan.FromSeconds(6),
                    Policy = "Any5xxResponse",
                    Path = "healthCheckPath",
                    Query = "?key=value"
                },
                AvailableDestinationsPolicy = "HealthyOrPanic"
            },
            LoadBalancingPolicy = LoadBalancingPolicies.Random,
            SessionAffinity = new SessionAffinityConfig
            {
                Enabled = true,
                FailurePolicy = "Return503Error",
                Policy = "Cookie",
                AffinityKeyName = "Key1",
                Cookie = new SessionAffinityCookieConfig
                {
                    Domain = "localhost",
                    Expiration = TimeSpan.FromHours(3),
                    HttpOnly = true,
                    IsEssential = true,
                    MaxAge = TimeSpan.FromDays(1),
                    Path = "mypath",
                    SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict,
                    SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.None
                }
            },
            HttpClient = new HttpClientConfig
            {
                SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12,
                MaxConnectionsPerServer = 10,
                DangerousAcceptAnyServerCertificate = true,
                EnableMultipleHttp2Connections = true,
                RequestHeaderEncoding = "utf-8",
                ResponseHeaderEncoding = "utf-8",
                WebProxy = new WebProxyConfig()
                {
                    Address = new Uri("http://localhost:8080"),
                    BypassOnLocal = true,
                    UseDefaultCredentials = true,
                }
            },
            HttpRequest = new ForwarderRequestConfig()
            {
                ActivityTimeout = TimeSpan.FromSeconds(60),
                Version = Version.Parse("1.0"),
                VersionPolicy = HttpVersionPolicy.RequestVersionExact,
                AllowResponseBuffering = true
            },
            Metadata = new Dictionary<string, string> { { "cluster1-K1", "cluster1-V1" }, { "cluster1-K2", "cluster1-V2" } }
        },
        new ClusterConfig
        {
            ClusterId = "cluster2",
            Destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase)
            {
                { "destinationC", new DestinationConfig { Address = "https://localhost:10001/destC", Host = "localhost" } },
                { "destinationD", new DestinationConfig { Address = "https://localhost:10000/destB", Host = "remotehost" } }
            },
            LoadBalancingPolicy = LoadBalancingPolicies.RoundRobin
        }
    ];
 
    #endregion
 
    [Fact]
    public async Task GenerateConfiguration()
    {
        var config = new YarpConfigurationBuilder();
        foreach (var cluster in _validClusters)
        {
            config.AddCluster(cluster);
        }
        foreach (var routes in _validRoutes)
        {
            config.AddRoute(routes);
        }
 
        var content = await config.Build(CancellationToken.None);
        Assert.NotEmpty(content);
        await Verify(content, "json");
    }
}