using System.Collections.Generic; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Responses; using Ocelot.Values; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.LoadBalancer { public class LeastConnectionTests { private HostAndPort _hostAndPort; private Response _result; private LeastConnectionLoadBalancer _leastConnection; private List _services; [Fact] public void should_get_next_url() { var serviceName = "products"; var hostAndPort = new HostAndPort("localhost", 80); var availableServices = new List { new Service(serviceName, hostAndPort) }; this.Given(x => x.GivenAHostAndPort(hostAndPort)) .And(x => x.GivenTheLoadBalancerStarts(availableServices, serviceName)) .When(x => x.WhenIGetTheNextHostAndPort()) .Then(x => x.ThenTheNextHostAndPortIsReturned()) .BDDfy(); } [Fact] public void should_serve_from_service_with_least_connections() { var serviceName = "products"; var availableServices = new List { new Service(serviceName, new HostAndPort("127.0.0.1", 80)), new Service(serviceName, new HostAndPort("127.0.0.2", 80)), new Service(serviceName, new HostAndPort("127.0.0.3", 80)) }; _services = availableServices; _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[2].HostAndPort.DownstreamHost); } [Fact] public void should_build_connections_per_service() { var serviceName = "products"; var availableServices = new List { new Service(serviceName, new HostAndPort("127.0.0.1", 80)), new Service(serviceName, new HostAndPort("127.0.0.2", 80)), }; _services = availableServices; _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); } [Fact] public void should_release_connection() { var serviceName = "products"; var availableServices = new List { new Service(serviceName, new HostAndPort("127.0.0.1", 80)), new Service(serviceName, new HostAndPort("127.0.0.2", 80)), }; _services = availableServices; _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); var response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); //release this so 2 should have 1 connection and we should get 2 back as our next host and port _leastConnection.Release(availableServices[1].HostAndPort); response = _leastConnection.Lease(); response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost); } [Fact] public void should_return_error_if_services_are_null() { var serviceName = "products"; var hostAndPort = new HostAndPort("localhost", 80); this.Given(x => x.GivenAHostAndPort(hostAndPort)) .And(x => x.GivenTheLoadBalancerStarts(null, serviceName)) .When(x => x.WhenIGetTheNextHostAndPort()) .Then(x => x.ThenServiceAreNullErrorIsReturned()) .BDDfy(); } [Fact] public void should_return_error_if_services_are_empty() { var serviceName = "products"; var hostAndPort = new HostAndPort("localhost", 80); this.Given(x => x.GivenAHostAndPort(hostAndPort)) .And(x => x.GivenTheLoadBalancerStarts(new List(), serviceName)) .When(x => x.WhenIGetTheNextHostAndPort()) .Then(x => x.ThenServiceAreEmptyErrorIsReturned()) .BDDfy(); } private void ThenServiceAreNullErrorIsReturned() { _result.IsError.ShouldBeTrue(); _result.Errors[0].ShouldBeOfType(); } private void ThenServiceAreEmptyErrorIsReturned() { _result.IsError.ShouldBeTrue(); _result.Errors[0].ShouldBeOfType(); } private void GivenTheLoadBalancerStarts(List services, string serviceName) { _services = services; _leastConnection = new LeastConnectionLoadBalancer(() => _services, serviceName); } private void WhenTheLoadBalancerStarts(List services, string serviceName) { GivenTheLoadBalancerStarts(services, serviceName); } private void GivenAHostAndPort(HostAndPort hostAndPort) { _hostAndPort = hostAndPort; } private void WhenIGetTheNextHostAndPort() { _result = _leastConnection.Lease(); } private void ThenTheNextHostAndPortIsReturned() { _result.Data.DownstreamHost.ShouldBe(_hostAndPort.DownstreamHost); _result.Data.DownstreamPort.ShouldBe(_hostAndPort.DownstreamPort); } } }