using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Responses; using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery.Providers; using Shouldly; using System.Collections.Generic; using System.Threading.Tasks; using Ocelot.Infrastructure.RequestData; using Ocelot.Middleware; using Ocelot.Values; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.LoadBalancer { using System; using Microsoft.AspNetCore.Http; public class LoadBalancerFactoryTests { private DownstreamRoute _route; private readonly LoadBalancerFactory _factory; private Response _result; private readonly Mock _serviceProviderFactory; private readonly IEnumerable _loadBalancerCreators; private readonly Mock _serviceProvider; private ServiceProviderConfiguration _serviceProviderConfig; public LoadBalancerFactoryTests() { _serviceProviderFactory = new Mock(); _serviceProvider = new Mock(); _loadBalancerCreators = new ILoadBalancerCreator[] { new FakeLoadBalancerCreator(), new FakeLoadBalancerCreator(), new FakeLoadBalancerCreator(nameof(NoLoadBalancer)), new BrokenLoadBalancerCreator(), }; _factory = new LoadBalancerFactory(_serviceProviderFactory.Object, _loadBalancerCreators); } [Fact] public void should_return_no_load_balancer_by_default() { var route = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenTheLoadBalancerIsReturned()) .BDDfy(); } [Fact] public void should_return_matching_load_balancer() { var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerTwo", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenTheLoadBalancerIsReturned()) .BDDfy(); } [Fact] public void should_return_error_response_if_cannot_find_load_balancer_creator() { var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("DoesntExistLoadBalancer", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenAnErrorResponseIsReturned()) .And(x => x.ThenTheErrorMessageIsCorrect()) .BDDfy(); } [Fact] public void should_return_error_response_if_creator_errors() { var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("BrokenLoadBalancer", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenAnErrorResponseIsReturned()) .BDDfy(); } [Fact] public void should_call_service_provider() { var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerOne", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenTheServiceProviderIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_return_error_response_when_call_to_service_provider_fails() { var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerOne", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryFails()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenAnErrorResponseIsReturned()) .BDDfy(); } private void GivenAServiceProviderConfig(ServiceProviderConfiguration serviceProviderConfig) { _serviceProviderConfig = serviceProviderConfig; } private void GivenTheServiceProviderFactoryReturns() { _serviceProviderFactory .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(new OkResponse(_serviceProvider.Object)); } private void GivenTheServiceProviderFactoryFails() { _serviceProviderFactory .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(new ErrorResponse(new CannotFindDataError("For tests"))); } private void ThenTheServiceProviderIsCalledCorrectly() { _serviceProviderFactory .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); } private void GivenARoute(DownstreamRoute route) { _route = route; } private void WhenIGetTheLoadBalancer() { _result = _factory.Get(_route, _serviceProviderConfig); } private void ThenTheLoadBalancerIsReturned() { _result.Data.ShouldBeOfType(); } private void ThenAnErrorResponseIsReturned() { _result.IsError.ShouldBeTrue(); } private void ThenTheErrorMessageIsCorrect() { _result.Errors[0].Message.ShouldBe("Could not find load balancer creator for Type: DoesntExistLoadBalancer, please check your config specified the correct load balancer and that you have registered a class with the same name."); } private class FakeLoadBalancerCreator : ILoadBalancerCreator where T : ILoadBalancer, new() { public FakeLoadBalancerCreator() { Type = typeof(T).Name; } public FakeLoadBalancerCreator(string type) { Type = type; } public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) { return new OkResponse(new T()); } public string Type { get; } } private class BrokenLoadBalancerCreator : ILoadBalancerCreator where T : ILoadBalancer, new() { public BrokenLoadBalancerCreator() { Type = typeof(T).Name; } public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) { return new ErrorResponse(new ErrorInvokingLoadBalancerCreator(new Exception())); } public string Type { get; } } private class FakeLoadBalancerOne : ILoadBalancer { public Task> Lease(HttpContext httpContext) { throw new System.NotImplementedException(); } public void Release(ServiceHostAndPort hostAndPort) { throw new System.NotImplementedException(); } } private class FakeLoadBalancerTwo : ILoadBalancer { public Task> Lease(HttpContext httpContext) { throw new System.NotImplementedException(); } public void Release(ServiceHostAndPort hostAndPort) { throw new System.NotImplementedException(); } } private class FakeNoLoadBalancer : ILoadBalancer { public Task> Lease(HttpContext httpContext) { throw new System.NotImplementedException(); } public void Release(ServiceHostAndPort hostAndPort) { throw new System.NotImplementedException(); } } private class BrokenLoadBalancer : ILoadBalancer { public Task> Lease(HttpContext httpContext) { throw new System.NotImplementedException(); } public void Release(ServiceHostAndPort hostAndPort) { throw new System.NotImplementedException(); } } } }