namespace Ocelot.UnitTests.LoadBalancer { using System.Collections.Generic; using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Provider; using Ocelot.DownstreamRouteFinder; using Ocelot.Errors; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.Middleware; using Ocelot.Logging; using Ocelot.Responses; using Ocelot.Values; using Shouldly; using TestStack.BDDfy; using Xunit; public class LoadBalancerMiddlewareTests : ServerHostedMiddlewareTest { private readonly Mock _loadBalancerHouse; private readonly Mock _loadBalancer; private ServiceHostAndPort _hostAndPort; private OkResponse _downstreamRoute; private ErrorResponse _getLoadBalancerHouseError; private ErrorResponse _getHostAndPortError; private HttpRequestMessage _downstreamRequest; private ServiceProviderConfiguration _config; public LoadBalancerMiddlewareTests() { _loadBalancerHouse = new Mock(); _loadBalancer = new Mock(); _loadBalancerHouse = new Mock(); _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, ""); ScopedRepository .Setup(sr => sr.Get("DownstreamRequest")) .Returns(new OkResponse(_downstreamRequest)); GivenTheTestServerIsConfigured(); } [Fact] public void should_call_scoped_data_repository_correctly() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build()); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturns()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheDownstreamUrlIsReplacedWith("http://127.0.0.1:80/abc?q=123")) .BDDfy(); } [Fact] public void should_set_pipeline_error_if_cannot_get_load_balancer() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build()); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturnsAnError()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline()) .BDDfy(); } [Fact] public void should_set_pipeline_error_if_cannot_get_least() { var downstreamRoute = new DownstreamRoute(new List(), new ReRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build()); var serviceProviderConfig = new ServiceProviderConfigurationBuilder() .Build(); this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) .And(x => GivenTheConfigurationIs(serviceProviderConfig)) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheLoadBalancerHouseReturns()) .And(x => x.GivenTheLoadBalancerReturnsAnError()) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline()) .BDDfy(); } private void GivenTheConfigurationIs(ServiceProviderConfiguration config) { _config = config; ScopedRepository .Setup(x => x.Get("ServiceProviderConfiguration")).Returns(new OkResponse(config)); } protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) { services.AddSingleton(); services.AddLogging(); services.AddSingleton(_loadBalancerHouse.Object); services.AddSingleton(ScopedRepository.Object); } protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) { app.UseLoadBalancingMiddleware(); } private void GivenTheDownStreamUrlIs(string downstreamUrl) { _downstreamRequest.RequestUri = new System.Uri(downstreamUrl); } private void GivenTheLoadBalancerReturnsAnError() { _getHostAndPortError = new ErrorResponse(new List() { new ServicesAreNullError($"services were null for bah") }); _loadBalancer .Setup(x => x.Lease()) .ReturnsAsync(_getHostAndPortError); } private void GivenTheLoadBalancerReturns() { _hostAndPort = new ServiceHostAndPort("127.0.0.1", 80); _loadBalancer .Setup(x => x.Lease()) .ReturnsAsync(new OkResponse(_hostAndPort)); } private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) { _downstreamRoute = new OkResponse(downstreamRoute); ScopedRepository .Setup(x => x.Get(It.IsAny())) .Returns(_downstreamRoute); } private void GivenTheLoadBalancerHouseReturns() { _loadBalancerHouse .Setup(x => x.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(new OkResponse(_loadBalancer.Object)); } private void GivenTheLoadBalancerHouseReturnsAnError() { _getLoadBalancerHouseError = new ErrorResponse(new List() { new UnableToFindLoadBalancerError($"unabe to find load balancer for bah") }); _loadBalancerHouse .Setup(x => x.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(_getLoadBalancerHouseError); } private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline() { ScopedRepository .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); ScopedRepository .Verify(x => x.Add("OcelotMiddlewareErrors", _getLoadBalancerHouseError.Errors), Times.Once); } private void ThenAnErrorSayingReleaseFailedIsSetOnThePipeline() { ScopedRepository .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); ScopedRepository .Verify(x => x.Add("OcelotMiddlewareErrors", It.IsAny>()), Times.Once); } private void ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline() { ScopedRepository .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); ScopedRepository .Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once); } private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) { _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); } } }