From de012a079409fa1aad23dbeef29ee89faf871381 Mon Sep 17 00:00:00 2001 From: David Lievrouw Date: Wed, 10 Jul 2019 19:15:17 +0200 Subject: [PATCH] Rewire LoadBalancerFactory to allow injecting custom load balancers, by introducing LoadBalancerCreators --- .../DependencyInjection/OcelotBuilder.cs | 4 + .../CookieStickySessionsCreator.cs | 20 +++ .../LoadBalancers/ILoadBalancerCreator.cs | 11 ++ .../LoadBalancers/LeastConnectionCreator.cs | 15 +++ .../LoadBalancers/LoadBalancerFactory.cs | 55 ++++---- .../LoadBalancers/NoLoadBalancerCreator.cs | 15 +++ .../LoadBalancer/LoadBalancers/RoundRobin.cs | 16 +-- .../LoadBalancers/RoundRobinCreator.cs | 15 +++ .../DependencyInjection/OcelotBuilderTests.cs | 40 ++++++ .../CookieStickySessionsCreatorTests.cs | 73 +++++++++++ .../LeastConnectionCreatorTests.cs | 73 +++++++++++ .../LoadBalancer/LoadBalancerFactoryTests.cs | 117 ++++++++++++------ .../NoLoadBalancerCreatorTests.cs | 72 +++++++++++ .../LoadBalancer/RoundRobinCreatorTests.cs | 72 +++++++++++ 14 files changed, 521 insertions(+), 77 deletions(-) create mode 100644 src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs create mode 100644 src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs create mode 100644 src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs create mode 100644 src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs create mode 100644 src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs create mode 100644 test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs create mode 100644 test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs create mode 100644 test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs create mode 100644 test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 0336f691..a9290ad7 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -87,6 +87,10 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs new file mode 100644 index 00000000..e78b1884 --- /dev/null +++ b/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs @@ -0,0 +1,20 @@ +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using System.Threading.Tasks; + using Ocelot.Configuration; + using Ocelot.Infrastructure; + using Ocelot.ServiceDiscovery.Providers; + + public class CookieStickySessionsCreator : ILoadBalancerCreator + { + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + var loadBalancer = new RoundRobin(async () => await serviceProvider.Get()); + var bus = new InMemoryBus(); + return new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, + reRoute.LoadBalancerOptions.ExpiryInMs, bus); + } + + public string Type => nameof(CookieStickySessions); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs new file mode 100644 index 00000000..4831e62f --- /dev/null +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs @@ -0,0 +1,11 @@ +using Ocelot.Configuration; +using Ocelot.ServiceDiscovery.Providers; + +namespace Ocelot.LoadBalancer.LoadBalancers +{ + public interface ILoadBalancerCreator + { + ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider); + string Type { get; } + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs new file mode 100644 index 00000000..e9e9c561 --- /dev/null +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs @@ -0,0 +1,15 @@ +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + + public class LeastConnectionCreator : ILoadBalancerCreator + { + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + return new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName); + } + + public string Type => nameof(LeastConnection); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs index 12725a57..2b7e71e2 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs @@ -1,47 +1,42 @@ -using Ocelot.Configuration; -using Ocelot.Infrastructure; -using Ocelot.Responses; -using Ocelot.ServiceDiscovery; -using System.Threading.Tasks; - -namespace Ocelot.LoadBalancer.LoadBalancers +namespace Ocelot.LoadBalancer.LoadBalancers { + using System.Collections.Generic; + using System.Linq; + using Ocelot.Configuration; + using Ocelot.Responses; + using System.Threading.Tasks; + using Ocelot.ServiceDiscovery; + public class LoadBalancerFactory : ILoadBalancerFactory { private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory; + private readonly IEnumerable _loadBalancerCreators; - public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory) + public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory, IEnumerable loadBalancerCreators) { _serviceProviderFactory = serviceProviderFactory; + _loadBalancerCreators = loadBalancerCreators; } - public async Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) + public Task> Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) { - var response = _serviceProviderFactory.Get(config, reRoute); + var serviceProviderFactoryResponse = _serviceProviderFactory.Get(config, reRoute); - if (response.IsError) + Response response; + if (serviceProviderFactoryResponse.IsError) { - return new ErrorResponse(response.Errors); + response = new ErrorResponse(serviceProviderFactoryResponse.Errors); + } + else + { + var serviceProvider = serviceProviderFactoryResponse.Data; + var requestedType = reRoute.LoadBalancerOptions?.Type ?? nameof(NoLoadBalancer); + var applicableCreator = _loadBalancerCreators.Single(c => c.Type == requestedType); + var createdLoadBalancer = applicableCreator.Create(reRoute, serviceProvider); + response = new OkResponse(createdLoadBalancer); } - var serviceProvider = response.Data; - - switch (reRoute.LoadBalancerOptions?.Type) - { - case nameof(RoundRobin): - return new OkResponse(new RoundRobin(async () => await serviceProvider.Get())); - - case nameof(LeastConnection): - return new OkResponse(new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName)); - - case nameof(CookieStickySessions): - var loadBalancer = new RoundRobin(async () => await serviceProvider.Get()); - var bus = new InMemoryBus(); - return new OkResponse(new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, reRoute.LoadBalancerOptions.ExpiryInMs, bus)); - - default: - return new OkResponse(new NoLoadBalancer(async () => await serviceProvider.Get())); - } + return Task.FromResult(response); } } } diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs new file mode 100644 index 00000000..fc1de3fd --- /dev/null +++ b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs @@ -0,0 +1,15 @@ +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + + public class NoLoadBalancerCreator : ILoadBalancerCreator + { + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + return new NoLoadBalancer(async () => await serviceProvider.Get()); + } + + public string Type => nameof(NoLoadBalancer); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs index 3500efe0..492a2c57 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobin.cs @@ -1,12 +1,12 @@ -using Ocelot.Middleware; -using Ocelot.Responses; -using Ocelot.Values; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ocelot.LoadBalancer.LoadBalancers +namespace Ocelot.LoadBalancer.LoadBalancers { + using Ocelot.Middleware; + using Ocelot.Responses; + using Ocelot.Values; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + public class RoundRobin : ILoadBalancer { private readonly Func>> _services; diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs new file mode 100644 index 00000000..8981074d --- /dev/null +++ b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs @@ -0,0 +1,15 @@ +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + + public class RoundRobinCreator : ILoadBalancerCreator + { + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + return new RoundRobin(async () => await serviceProvider.Get()); + } + + public string Type => nameof(RoundRobin); + } +} diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index 0f1ddb60..499c2a75 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -1,3 +1,7 @@ +using Ocelot.Configuration; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.ServiceDiscovery.Providers; + namespace Ocelot.UnitTests.DependencyInjection { using Microsoft.AspNetCore.Hosting; @@ -158,11 +162,26 @@ namespace Ocelot.UnitTests.DependencyInjection .BDDfy(); } + [Fact] + public void should_add_custom_load_balancer_creators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddSingletonLoadBalancerCreator()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + private void AddSingletonDefinedAggregator() where T : class, IDefinedAggregator { _ocelotBuilder.AddSingletonDefinedAggregator(); } + + private void AddSingletonLoadBalancerCreator() + where T : class, ILoadBalancerCreator + { + _ocelotBuilder.Services.AddSingleton(); + } private void AddTransientDefinedAggregator() where T : class, IDefinedAggregator @@ -238,6 +257,17 @@ namespace Ocelot.UnitTests.DependencyInjection handlers[0].ShouldBeOfType(); handlers[1].ShouldBeOfType(); } + + private void ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators() + { + _serviceProvider = _services.BuildServiceProvider(); + var creators = _serviceProvider.GetServices().ToList(); + creators.Count(c => c.GetType() == typeof(NoLoadBalancerCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(RoundRobinCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(CookieStickySessionsCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(LeastConnectionCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(FakeCustomLoadBalancerCreator)).ShouldBe(1); + } private void ThenTheAggregatorsAreTransient() { @@ -323,5 +353,15 @@ namespace Ocelot.UnitTests.DependencyInjection { _ex.ShouldBeNull(); } + + private class FakeCustomLoadBalancerCreator : ILoadBalancerCreator + { + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + throw new NotImplementedException(); + } + + public string Type { get; } + } } } diff --git a/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs new file mode 100644 index 00000000..20f65da4 --- /dev/null +++ b/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs @@ -0,0 +1,73 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.ServiceDiscovery.Providers; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.LoadBalancer +{ + public class CookieStickySessionsCreatorTests + { + private readonly CookieStickySessionsCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamReRoute _reRoute; + private ILoadBalancer _loadBalancer; + private string _typeName; + + public CookieStickySessionsCreatorTests() + { + _creator = new CookieStickySessionsCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_expected_name() + { + var reRoute = new DownstreamReRouteBuilder() + .WithLoadBalancerOptions(new LoadBalancerOptions("myType", "myKey", 1000)) + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("CookieStickySessions")) + .BDDfy(); + } + + private void GivenAReRoute(DownstreamReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs new file mode 100644 index 00000000..507958fd --- /dev/null +++ b/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs @@ -0,0 +1,73 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.ServiceDiscovery.Providers; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.LoadBalancer +{ + public class LeastConnectionCreatorTests + { + private readonly LeastConnectionCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamReRoute _reRoute; + private ILoadBalancer _loadBalancer; + private string _typeName; + + public LeastConnectionCreatorTests() + { + _creator = new LeastConnectionCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_expected_name() + { + var reRoute = new DownstreamReRouteBuilder() + .WithServiceName("myService") + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("LeastConnection")) + .BDDfy(); + } + + private void GivenAReRoute(DownstreamReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index 8df47cea..70b9a837 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -7,6 +7,9 @@ using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery.Providers; using Shouldly; using System.Collections.Generic; +using System.Threading.Tasks; +using Ocelot.Middleware; +using Ocelot.Values; using TestStack.BDDfy; using Xunit; @@ -18,6 +21,7 @@ namespace Ocelot.UnitTests.LoadBalancer private readonly LoadBalancerFactory _factory; private Response _result; private readonly Mock _serviceProviderFactory; + private readonly IEnumerable _loadBalancerCreators; private readonly Mock _serviceProvider; private ServiceProviderConfiguration _serviceProviderConfig; @@ -25,7 +29,13 @@ namespace Ocelot.UnitTests.LoadBalancer { _serviceProviderFactory = new Mock(); _serviceProvider = new Mock(); - _factory = new LoadBalancerFactory(_serviceProviderFactory.Object); + _loadBalancerCreators = new ILoadBalancerCreator[] + { + new FakeLoadBalancerCreator(), + new FakeLoadBalancerCreator(), + new FakeLoadBalancerCreator(nameof(NoLoadBalancer)), + }; + _factory = new LoadBalancerFactory(_serviceProviderFactory.Object, _loadBalancerCreators); } [Fact] @@ -39,15 +49,15 @@ namespace Ocelot.UnitTests.LoadBalancer .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) .BDDfy(); } [Fact] - public void should_return_round_robin_load_balancer() + public void should_return_matching_load_balancer() { var reRoute = new DownstreamReRouteBuilder() - .WithLoadBalancerOptions(new LoadBalancerOptions("RoundRobin", "", 0)) + .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerTwo", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -55,31 +65,15 @@ namespace Ocelot.UnitTests.LoadBalancer .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) .BDDfy(); } - - [Fact] - public void should_return_round_least_connection_balancer() - { - var reRoute = new DownstreamReRouteBuilder() - .WithLoadBalancerOptions(new LoadBalancerOptions("LeastConnection", "", 0)) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) - .And(x => x.GivenTheServiceProviderFactoryReturns()) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - + [Fact] public void should_call_service_provider() { var reRoute = new DownstreamReRouteBuilder() - .WithLoadBalancerOptions(new LoadBalancerOptions("RoundRobin", "", 0)) + .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerOne", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -91,22 +85,6 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } - [Fact] - public void should_return_sticky_session() - { - var reRoute = new DownstreamReRouteBuilder() - .WithLoadBalancerOptions(new LoadBalancerOptions("CookieStickySessions", "", 0)) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) - .And(x => x.GivenTheServiceProviderFactoryReturns()) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - private void GivenAServiceProviderConfig(ServiceProviderConfiguration serviceProviderConfig) { _serviceProviderConfig = serviceProviderConfig; @@ -139,5 +117,66 @@ namespace Ocelot.UnitTests.LoadBalancer { _result.Data.ShouldBeOfType(); } + + private class FakeLoadBalancerCreator : ILoadBalancerCreator + where T : ILoadBalancer, new() + { + + public FakeLoadBalancerCreator() + { + Type = typeof(T).Name; + } + + public FakeLoadBalancerCreator(string type) + { + Type = type; + } + + public ILoadBalancer Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + { + return new T(); + } + + public string Type { get; } + } + + private class FakeLoadBalancerOne : ILoadBalancer + { + public Task> Lease(DownstreamContext context) + { + throw new System.NotImplementedException(); + } + + public void Release(ServiceHostAndPort hostAndPort) + { + throw new System.NotImplementedException(); + } + } + + private class FakeLoadBalancerTwo : ILoadBalancer + { + public Task> Lease(DownstreamContext context) + { + throw new System.NotImplementedException(); + } + + public void Release(ServiceHostAndPort hostAndPort) + { + throw new System.NotImplementedException(); + } + } + + private class FakeNoLoadBalancer : ILoadBalancer + { + public Task> Lease(DownstreamContext context) + { + throw new System.NotImplementedException(); + } + + public void Release(ServiceHostAndPort hostAndPort) + { + throw new System.NotImplementedException(); + } + } } } diff --git a/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs new file mode 100644 index 00000000..6b61c5a9 --- /dev/null +++ b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs @@ -0,0 +1,72 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.ServiceDiscovery.Providers; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.LoadBalancer +{ + public class NoLoadBalancerCreatorTests + { + private readonly NoLoadBalancerCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamReRoute _reRoute; + private ILoadBalancer _loadBalancer; + private string _typeName; + + public NoLoadBalancerCreatorTests() + { + _creator = new NoLoadBalancerCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_expected_name() + { + var reRoute = new DownstreamReRouteBuilder() + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("NoLoadBalancer")) + .BDDfy(); + } + + private void GivenAReRoute(DownstreamReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs new file mode 100644 index 00000000..80d43568 --- /dev/null +++ b/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs @@ -0,0 +1,72 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.ServiceDiscovery.Providers; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.LoadBalancer +{ + public class RoundRobinCreatorTests + { + private readonly RoundRobinCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamReRoute _reRoute; + private ILoadBalancer _loadBalancer; + private string _typeName; + + public RoundRobinCreatorTests() + { + _creator = new RoundRobinCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_expected_name() + { + var reRoute = new DownstreamReRouteBuilder() + .Build(); + + this.Given(x => x.GivenAReRoute(reRoute)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("RoundRobin")) + .BDDfy(); + } + + private void GivenAReRoute(DownstreamReRoute reRoute) + { + _reRoute = reRoute; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +}