mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 10:18:17 +08:00
Merge branch 'develop' into CircuitBreakerPattern
This commit is contained in:
@ -8,6 +8,7 @@ using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Parser;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
@ -24,6 +25,9 @@ namespace Ocelot.UnitTests.Configuration
|
||||
private readonly Mock<IClaimToThingConfigurationParser> _configParser;
|
||||
private readonly Mock<ILogger<FileOcelotConfigurationCreator>> _logger;
|
||||
private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator;
|
||||
private readonly Mock<ILoadBalancerFactory> _loadBalancerFactory;
|
||||
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
|
||||
private readonly Mock<ILoadBalancer> _loadBalancer;
|
||||
|
||||
public FileConfigurationCreatorTests()
|
||||
{
|
||||
@ -31,8 +35,37 @@ namespace Ocelot.UnitTests.Configuration
|
||||
_configParser = new Mock<IClaimToThingConfigurationParser>();
|
||||
_validator = new Mock<IConfigurationValidator>();
|
||||
_fileConfig = new Mock<IOptions<FileConfiguration>>();
|
||||
_loadBalancerFactory = new Mock<ILoadBalancerFactory>();
|
||||
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
|
||||
_loadBalancer = new Mock<ILoadBalancer>();
|
||||
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
|
||||
_fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object);
|
||||
_fileConfig.Object, _validator.Object, _configParser.Object, _logger.Object,
|
||||
_loadBalancerFactory.Object, _loadBalancerHouse.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_load_balancer()
|
||||
{
|
||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamHost = "127.0.0.1",
|
||||
UpstreamTemplate = "/api/products/{productId}",
|
||||
DownstreamPathTemplate = "/products/{productId}",
|
||||
UpstreamHttpMethod = "Get",
|
||||
}
|
||||
},
|
||||
}))
|
||||
.And(x => x.GivenTheConfigIsValid())
|
||||
.And(x => x.GivenTheLoadBalancerFactoryReturns())
|
||||
.When(x => x.WhenICreateTheConfig())
|
||||
.Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly())
|
||||
.And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly())
|
||||
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -66,6 +99,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_downstream_scheme()
|
||||
{
|
||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||
@ -117,7 +151,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Provider = "consul",
|
||||
Address = "127.0.0.1"
|
||||
Host = "127.0.0.1"
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -376,6 +410,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
}))
|
||||
.And(x => x.GivenTheConfigIsValid())
|
||||
.And(x => x.GivenTheConfigHeaderExtractorReturns(new ClaimToThing("CustomerId", "CustomerId", "", 0)))
|
||||
.And(x => x.GivenTheLoadBalancerFactoryReturns())
|
||||
.When(x => x.WhenICreateTheConfig())
|
||||
.Then(x => x.ThenTheReRoutesAre(expected))
|
||||
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
|
||||
@ -430,6 +465,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheConfigIsValid())
|
||||
.And(x => x.GivenTheLoadBalancerFactoryReturns())
|
||||
.When(x => x.WhenICreateTheConfig())
|
||||
.Then(x => x.ThenTheReRoutesAre(expected))
|
||||
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
|
||||
@ -543,7 +579,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void WhenICreateTheConfig()
|
||||
{
|
||||
_config = _ocelotConfigurationCreator.Create();
|
||||
_config = _ocelotConfigurationCreator.Create().Result;
|
||||
}
|
||||
|
||||
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes)
|
||||
@ -576,5 +612,24 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheLoadBalancerFactoryReturns()
|
||||
{
|
||||
_loadBalancerFactory
|
||||
.Setup(x => x.Get(It.IsAny<ReRoute>()))
|
||||
.ReturnsAsync(_loadBalancer.Object);
|
||||
}
|
||||
|
||||
private void TheLoadBalancerFactoryIsCalledCorrectly()
|
||||
{
|
||||
_loadBalancerFactory
|
||||
.Verify(x => x.Get(It.IsAny<ReRoute>()), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheLoadBalancerHouseIsCalledCorrectly()
|
||||
{
|
||||
_loadBalancerHouse
|
||||
.Verify(x => x.Add(It.IsAny<string>(), _loadBalancer.Object), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
_creator
|
||||
.Setup(x => x.Create())
|
||||
.Returns(config);
|
||||
.ReturnsAsync(config);
|
||||
}
|
||||
|
||||
private void GivenTheRepoReturns(Response<IOcelotConfiguration> config)
|
||||
@ -93,7 +93,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void WhenIGetTheConfig()
|
||||
{
|
||||
_result = _ocelotConfigurationProvider.Get();
|
||||
_result = _ocelotConfigurationProvider.Get().Result;
|
||||
}
|
||||
|
||||
private void TheFollowingIsReturned(Response<IOcelotConfiguration> expected)
|
||||
|
@ -84,7 +84,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||
_downstreamRouteFinder
|
||||
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns(_downstreamRoute);
|
||||
.ReturnsAsync(_downstreamRoute);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -159,7 +159,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
_reRoutesConfig = reRoutesConfig;
|
||||
_mockConfig
|
||||
.Setup(x => x.Get())
|
||||
.Returns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig)));
|
||||
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig)));
|
||||
}
|
||||
|
||||
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
|
||||
@ -169,7 +169,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void WhenICallTheFinder()
|
||||
{
|
||||
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod);
|
||||
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod).Result;
|
||||
}
|
||||
|
||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||
|
@ -36,6 +36,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
|
||||
private HttpResponseMessage _result;
|
||||
private OkResponse<DownstreamPath> _downstreamPath;
|
||||
private OkResponse<DownstreamUrl> _downstreamUrl;
|
||||
private HostAndPort _hostAndPort;
|
||||
|
||||
public DownstreamUrlCreatorMiddlewareTests()
|
||||
{
|
||||
@ -69,14 +70,25 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
|
||||
[Fact]
|
||||
public void should_call_dependencies_correctly()
|
||||
{
|
||||
var hostAndPort = new HostAndPort("127.0.0.1", 80);
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder().WithDownstreamPathTemplate("any old string").Build())))
|
||||
.And(x => x.GivenTheHostAndPortIs(hostAndPort))
|
||||
.And(x => x.TheUrlReplacerReturns("/api/products/1"))
|
||||
.And(x => x.TheUrlBuilderReturns("http://www.bbc.co.uk/api/products/1"))
|
||||
.And(x => x.TheUrlBuilderReturns("http://127.0.0.1:80/api/products/1"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheHostAndPortIs(HostAndPort hostAndPort)
|
||||
{
|
||||
_hostAndPort = hostAndPort;
|
||||
_scopedRepository
|
||||
.Setup(x => x.Get<HostAndPort>("HostAndPort"))
|
||||
.Returns(new OkResponse<HostAndPort>(_hostAndPort));
|
||||
}
|
||||
|
||||
private void TheUrlBuilderReturns(string dsUrl)
|
||||
{
|
||||
_downstreamUrl = new OkResponse<DownstreamUrl>(new DownstreamUrl(dsUrl));
|
||||
|
238
test/Ocelot.UnitTests/LoadBalancer/LeastConnectionTests.cs
Normal file
238
test/Ocelot.UnitTests/LoadBalancer/LeastConnectionTests.cs
Normal file
@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
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<HostAndPort> _result;
|
||||
private LeastConnectionLoadBalancer _leastConnection;
|
||||
private List<Service> _services;
|
||||
private Random _random;
|
||||
|
||||
public LeastConnectionTests()
|
||||
{
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_be_able_to_lease_and_release_concurrently()
|
||||
{
|
||||
var serviceName = "products";
|
||||
|
||||
var availableServices = new List<Service>
|
||||
{
|
||||
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
|
||||
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
|
||||
};
|
||||
|
||||
_services = availableServices;
|
||||
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
|
||||
|
||||
var tasks = new Task[100];
|
||||
|
||||
for(var i = 0; i < tasks.Length; i++)
|
||||
{
|
||||
tasks[i] = LeaseDelayAndRelease();
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
private async Task LeaseDelayAndRelease()
|
||||
{
|
||||
var hostAndPort = await _leastConnection.Lease();
|
||||
await Task.Delay(_random.Next(1, 100));
|
||||
_leastConnection.Release(hostAndPort.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_next_url()
|
||||
{
|
||||
var serviceName = "products";
|
||||
|
||||
var hostAndPort = new HostAndPort("localhost", 80);
|
||||
|
||||
var availableServices = new List<Service>
|
||||
{
|
||||
new Service(serviceName, hostAndPort, string.Empty, string.Empty, new string[0])
|
||||
};
|
||||
|
||||
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<Service>
|
||||
{
|
||||
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
|
||||
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
|
||||
new Service(serviceName, new HostAndPort("127.0.0.3", 80), string.Empty, string.Empty, new string[0])
|
||||
};
|
||||
|
||||
_services = availableServices;
|
||||
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
|
||||
|
||||
var response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[2].HostAndPort.DownstreamHost);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_connections_per_service()
|
||||
{
|
||||
var serviceName = "products";
|
||||
|
||||
var availableServices = new List<Service>
|
||||
{
|
||||
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
|
||||
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
|
||||
};
|
||||
|
||||
_services = availableServices;
|
||||
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
|
||||
|
||||
var response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_release_connection()
|
||||
{
|
||||
var serviceName = "products";
|
||||
|
||||
var availableServices = new List<Service>
|
||||
{
|
||||
new Service(serviceName, new HostAndPort("127.0.0.1", 80), string.Empty, string.Empty, new string[0]),
|
||||
new Service(serviceName, new HostAndPort("127.0.0.2", 80), string.Empty, string.Empty, new string[0]),
|
||||
};
|
||||
|
||||
_services = availableServices;
|
||||
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
|
||||
|
||||
var response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[1].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
response.Data.DownstreamHost.ShouldBe(availableServices[0].HostAndPort.DownstreamHost);
|
||||
|
||||
response = _leastConnection.Lease().Result;
|
||||
|
||||
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().Result;
|
||||
|
||||
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<Service>(), serviceName))
|
||||
.When(x => x.WhenIGetTheNextHostAndPort())
|
||||
.Then(x => x.ThenServiceAreEmptyErrorIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenServiceAreNullErrorIsReturned()
|
||||
{
|
||||
_result.IsError.ShouldBeTrue();
|
||||
_result.Errors[0].ShouldBeOfType<ServicesAreNullError>();
|
||||
}
|
||||
|
||||
private void ThenServiceAreEmptyErrorIsReturned()
|
||||
{
|
||||
_result.IsError.ShouldBeTrue();
|
||||
_result.Errors[0].ShouldBeOfType<ServicesAreEmptyError>();
|
||||
}
|
||||
|
||||
private void GivenTheLoadBalancerStarts(List<Service> services, string serviceName)
|
||||
{
|
||||
_services = services;
|
||||
_leastConnection = new LeastConnectionLoadBalancer(() => Task.FromResult(_services), serviceName);
|
||||
}
|
||||
|
||||
private void WhenTheLoadBalancerStarts(List<Service> services, string serviceName)
|
||||
{
|
||||
GivenTheLoadBalancerStarts(services, serviceName);
|
||||
}
|
||||
|
||||
private void GivenAHostAndPort(HostAndPort hostAndPort)
|
||||
{
|
||||
_hostAndPort = hostAndPort;
|
||||
}
|
||||
|
||||
private void WhenIGetTheNextHostAndPort()
|
||||
{
|
||||
_result = _leastConnection.Lease().Result;
|
||||
}
|
||||
|
||||
private void ThenTheNextHostAndPortIsReturned()
|
||||
{
|
||||
_result.Data.DownstreamHost.ShouldBe(_hostAndPort.DownstreamHost);
|
||||
_result.Data.DownstreamPort.ShouldBe(_hostAndPort.DownstreamPort);
|
||||
}
|
||||
}
|
||||
}
|
110
test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs
Normal file
110
test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.LoadBalancer
|
||||
{
|
||||
public class LoadBalancerFactoryTests
|
||||
{
|
||||
private ReRoute _reRoute;
|
||||
private LoadBalancerFactory _factory;
|
||||
private ILoadBalancer _result;
|
||||
private Mock<IServiceDiscoveryProviderFactory> _serviceProviderFactory;
|
||||
private Mock<IServiceDiscoveryProvider> _serviceProvider;
|
||||
|
||||
public LoadBalancerFactoryTests()
|
||||
{
|
||||
_serviceProviderFactory = new Mock<IServiceDiscoveryProviderFactory>();
|
||||
_serviceProvider = new Mock<IServiceDiscoveryProvider>();
|
||||
_factory = new LoadBalancerFactory(_serviceProviderFactory.Object);
|
||||
}
|
||||
|
||||
private void GivenTheServiceProviderFactoryReturns()
|
||||
{
|
||||
_serviceProviderFactory
|
||||
.Setup(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()))
|
||||
.Returns(_serviceProvider.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_no_load_balancer()
|
||||
{
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenAReRoute(reRoute))
|
||||
.And(x => x.GivenTheServiceProviderFactoryReturns())
|
||||
.When(x => x.WhenIGetTheLoadBalancer())
|
||||
.Then(x => x.ThenTheLoadBalancerIsReturned<NoLoadBalancer>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_round_robin_load_balancer()
|
||||
{
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithLoadBalancer("RoundRobin")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenAReRoute(reRoute))
|
||||
.And(x => x.GivenTheServiceProviderFactoryReturns())
|
||||
.When(x => x.WhenIGetTheLoadBalancer())
|
||||
.Then(x => x.ThenTheLoadBalancerIsReturned<RoundRobinLoadBalancer>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_round_least_connection_balancer()
|
||||
{
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithLoadBalancer("LeastConnection")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenAReRoute(reRoute))
|
||||
.And(x => x.GivenTheServiceProviderFactoryReturns())
|
||||
.When(x => x.WhenIGetTheLoadBalancer())
|
||||
.Then(x => x.ThenTheLoadBalancerIsReturned<LeastConnectionLoadBalancer>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_service_provider()
|
||||
{
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithLoadBalancer("RoundRobin")
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenAReRoute(reRoute))
|
||||
.And(x => x.GivenTheServiceProviderFactoryReturns())
|
||||
.When(x => x.WhenIGetTheLoadBalancer())
|
||||
.Then(x => x.ThenTheServiceProviderIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheServiceProviderIsCalledCorrectly()
|
||||
{
|
||||
_serviceProviderFactory
|
||||
.Verify(x => x.Get(It.IsAny<ServiceProviderConfiguraion>()), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenAReRoute(ReRoute reRoute)
|
||||
{
|
||||
_reRoute = reRoute;
|
||||
}
|
||||
|
||||
private void WhenIGetTheLoadBalancer()
|
||||
{
|
||||
_result = _factory.Get(_reRoute).Result;
|
||||
}
|
||||
|
||||
private void ThenTheLoadBalancerIsReturned<T>()
|
||||
{
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
}
|
||||
}
|
136
test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs
Normal file
136
test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.LoadBalancer
|
||||
{
|
||||
public class LoadBalancerHouseTests
|
||||
{
|
||||
private ILoadBalancer _loadBalancer;
|
||||
private readonly LoadBalancerHouse _loadBalancerHouse;
|
||||
private Response _addResult;
|
||||
private Response<ILoadBalancer> _getResult;
|
||||
private string _key;
|
||||
|
||||
public LoadBalancerHouseTests()
|
||||
{
|
||||
_loadBalancerHouse = new LoadBalancerHouse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_store_load_balancer()
|
||||
{
|
||||
var key = "test";
|
||||
|
||||
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
|
||||
.When(x => x.WhenIAddTheLoadBalancer())
|
||||
.Then(x => x.ThenItIsAdded())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_load_balancer()
|
||||
{
|
||||
var key = "test";
|
||||
|
||||
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
|
||||
.When(x => x.WhenWeGetTheLoadBalancer(key))
|
||||
.Then(x => x.ThenItIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_store_load_balancers_by_key()
|
||||
{
|
||||
var key = "test";
|
||||
var keyTwo = "testTwo";
|
||||
|
||||
this.Given(x => x.GivenThereIsALoadBalancer(key, new FakeLoadBalancer()))
|
||||
.And(x => x.GivenThereIsALoadBalancer(keyTwo, new FakeRoundRobinLoadBalancer()))
|
||||
.When(x => x.WhenWeGetTheLoadBalancer(key))
|
||||
.Then(x => x.ThenTheLoadBalancerIs<FakeLoadBalancer>())
|
||||
.When(x => x.WhenWeGetTheLoadBalancer(keyTwo))
|
||||
.Then(x => x.ThenTheLoadBalancerIs<FakeRoundRobinLoadBalancer>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error_if_no_load_balancer_with_key()
|
||||
{
|
||||
this.When(x => x.WhenWeGetTheLoadBalancer("test"))
|
||||
.Then(x => x.ThenAnErrorIsReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsReturned()
|
||||
{
|
||||
_getResult.IsError.ShouldBeTrue();
|
||||
_getResult.Errors[0].ShouldBeOfType<UnableToFindLoadBalancerError>();
|
||||
}
|
||||
|
||||
private void ThenTheLoadBalancerIs<T>()
|
||||
{
|
||||
_getResult.Data.ShouldBeOfType<T>();
|
||||
}
|
||||
|
||||
private void ThenItIsAdded()
|
||||
{
|
||||
_addResult.IsError.ShouldBe(false);
|
||||
_addResult.ShouldBeOfType<OkResponse>();
|
||||
}
|
||||
|
||||
private void WhenIAddTheLoadBalancer()
|
||||
{
|
||||
_addResult = _loadBalancerHouse.Add(_key, _loadBalancer);
|
||||
}
|
||||
|
||||
|
||||
private void GivenThereIsALoadBalancer(string key, ILoadBalancer loadBalancer)
|
||||
{
|
||||
_key = key;
|
||||
_loadBalancer = loadBalancer;
|
||||
WhenIAddTheLoadBalancer();
|
||||
}
|
||||
|
||||
private void WhenWeGetTheLoadBalancer(string key)
|
||||
{
|
||||
_getResult = _loadBalancerHouse.Get(key);
|
||||
}
|
||||
|
||||
private void ThenItIsReturned()
|
||||
{
|
||||
_getResult.Data.ShouldBe(_loadBalancer);
|
||||
}
|
||||
|
||||
class FakeLoadBalancer : ILoadBalancer
|
||||
{
|
||||
public Task<Response<HostAndPort>> Lease()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Release(HostAndPort hostAndPort)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
class FakeRoundRobinLoadBalancer : ILoadBalancer
|
||||
{
|
||||
public Task<Response<HostAndPort>> Lease()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Release(HostAndPort hostAndPort)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.DownstreamRouteFinder;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.LoadBalancer.Middleware;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.LoadBalancer
|
||||
{
|
||||
public class LoadBalancerMiddlewareTests
|
||||
{
|
||||
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
|
||||
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
|
||||
private readonly Mock<ILoadBalancer> _loadBalancer;
|
||||
private readonly string _url;
|
||||
private readonly TestServer _server;
|
||||
private readonly HttpClient _client;
|
||||
private HttpResponseMessage _result;
|
||||
private HostAndPort _hostAndPort;
|
||||
private OkResponse<Ocelot.Request.Request> _request;
|
||||
private OkResponse<string> _downstreamUrl;
|
||||
private OkResponse<DownstreamRoute> _downstreamRoute;
|
||||
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;
|
||||
private ErrorResponse<HostAndPort> _getHostAndPortError;
|
||||
|
||||
public LoadBalancerMiddlewareTests()
|
||||
{
|
||||
_url = "http://localhost:51879";
|
||||
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
|
||||
_scopedRepository = new Mock<IRequestScopedDataRepository>();
|
||||
_loadBalancer = new Mock<ILoadBalancer>();
|
||||
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
x.AddLogging();
|
||||
x.AddSingleton(_loadBalancerHouse.Object);
|
||||
x.AddSingleton(_scopedRepository.Object);
|
||||
})
|
||||
.UseUrls(_url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(_url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseLoadBalancingMiddleware();
|
||||
});
|
||||
|
||||
_server = new TestServer(builder);
|
||||
_client = _server.CreateClient();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_scoped_data_repository_correctly()
|
||||
{
|
||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.Build());
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => x.GivenTheLoadBalancerHouseReturns())
|
||||
.And(x => x.GivenTheLoadBalancerReturns())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_pipeline_error_if_cannot_get_load_balancer()
|
||||
{
|
||||
var downstreamRoute = new DownstreamRoute(new List<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.Build());
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||
.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<Ocelot.DownstreamRouteFinder.UrlMatcher.UrlPathPlaceholderNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.Build());
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => x.GivenTheLoadBalancerHouseReturns())
|
||||
.And(x => x.GivenTheLoadBalancerReturnsAnError())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheLoadBalancerReturnsAnError()
|
||||
{
|
||||
_getHostAndPortError = new ErrorResponse<HostAndPort>(new List<Error>() { new ServicesAreNullError($"services were null for bah") });
|
||||
_loadBalancer
|
||||
.Setup(x => x.Lease())
|
||||
.ReturnsAsync(_getHostAndPortError);
|
||||
}
|
||||
|
||||
private void GivenTheLoadBalancerReturns()
|
||||
{
|
||||
_hostAndPort = new HostAndPort("127.0.0.1", 80);
|
||||
_loadBalancer
|
||||
.Setup(x => x.Lease())
|
||||
.ReturnsAsync(new OkResponse<HostAndPort>(_hostAndPort));
|
||||
}
|
||||
|
||||
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
||||
{
|
||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||
_scopedRepository
|
||||
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
|
||||
.Returns(_downstreamRoute);
|
||||
}
|
||||
|
||||
private void GivenTheLoadBalancerHouseReturns()
|
||||
{
|
||||
_loadBalancerHouse
|
||||
.Setup(x => x.Get(It.IsAny<string>()))
|
||||
.Returns(new OkResponse<ILoadBalancer>(_loadBalancer.Object));
|
||||
}
|
||||
|
||||
|
||||
private void GivenTheLoadBalancerHouseReturnsAnError()
|
||||
{
|
||||
_getLoadBalancerHouseError = new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
|
||||
{
|
||||
new UnableToFindLoadBalancerError($"unabe to find load balancer for bah")
|
||||
});
|
||||
|
||||
_loadBalancerHouse
|
||||
.Setup(x => x.Get(It.IsAny<string>()))
|
||||
.Returns(_getLoadBalancerHouseError);
|
||||
}
|
||||
|
||||
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
|
||||
{
|
||||
_scopedRepository
|
||||
.Verify(x => x.Add("HostAndPort", _hostAndPort), Times.Once());
|
||||
}
|
||||
|
||||
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<List<Error>>()), 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 WhenICallTheMiddleware()
|
||||
{
|
||||
_result = _client.GetAsync(_url).Result;
|
||||
}
|
||||
|
||||
private void GivenTheDownStreamUrlIs(string downstreamUrl)
|
||||
{
|
||||
_downstreamUrl = new OkResponse<string>(downstreamUrl);
|
||||
_scopedRepository
|
||||
.Setup(x => x.Get<string>(It.IsAny<string>()))
|
||||
.Returns(_downstreamUrl);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
_server.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
48
test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs
Normal file
48
test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs
Normal file
@ -0,0 +1,48 @@
|
||||
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 NoLoadBalancerTests
|
||||
{
|
||||
private List<Service> _services;
|
||||
private NoLoadBalancer _loadBalancer;
|
||||
private Response<HostAndPort> _result;
|
||||
|
||||
[Fact]
|
||||
public void should_return_host_and_port()
|
||||
{
|
||||
var hostAndPort = new HostAndPort("127.0.0.1", 80);
|
||||
|
||||
var services = new List<Service>
|
||||
{
|
||||
new Service("product", hostAndPort, string.Empty, string.Empty, new string[0])
|
||||
};
|
||||
this.Given(x => x.GivenServices(services))
|
||||
.When(x => x.WhenIGetTheNextHostAndPort())
|
||||
.Then(x => x.ThenTheHostAndPortIs(hostAndPort))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenServices(List<Service> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
private void WhenIGetTheNextHostAndPort()
|
||||
{
|
||||
_loadBalancer = new NoLoadBalancer(_services);
|
||||
_result = _loadBalancer.Lease().Result;
|
||||
}
|
||||
|
||||
private void ThenTheHostAndPortIs(HostAndPort expected)
|
||||
{
|
||||
_result.Data.ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
68
test/Ocelot.UnitTests/LoadBalancer/RoundRobinTests.cs
Normal file
68
test/Ocelot.UnitTests/LoadBalancer/RoundRobinTests.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.LoadBalancer
|
||||
{
|
||||
public class RoundRobinTests
|
||||
{
|
||||
private readonly RoundRobinLoadBalancer _roundRobin;
|
||||
private readonly List<Service> _services;
|
||||
private Response<HostAndPort> _hostAndPort;
|
||||
|
||||
public RoundRobinTests()
|
||||
{
|
||||
_services = new List<Service>
|
||||
{
|
||||
new Service("product", new HostAndPort("127.0.0.1", 5000), string.Empty, string.Empty, new string[0]),
|
||||
new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0]),
|
||||
new Service("product", new HostAndPort("127.0.0.1", 5001), string.Empty, string.Empty, new string[0])
|
||||
};
|
||||
|
||||
_roundRobin = new RoundRobinLoadBalancer(_services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_next_address()
|
||||
{
|
||||
this.Given(x => x.GivenIGetTheNextAddress())
|
||||
.Then(x => x.ThenTheNextAddressIndexIs(0))
|
||||
.Given(x => x.GivenIGetTheNextAddress())
|
||||
.Then(x => x.ThenTheNextAddressIndexIs(1))
|
||||
.Given(x => x.GivenIGetTheNextAddress())
|
||||
.Then(x => x.ThenTheNextAddressIndexIs(2))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_go_back_to_first_address_after_finished_last()
|
||||
{
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
while (stopWatch.ElapsedMilliseconds < 1000)
|
||||
{
|
||||
var address = _roundRobin.Lease().Result;
|
||||
address.Data.ShouldBe(_services[0].HostAndPort);
|
||||
address = _roundRobin.Lease().Result;
|
||||
address.Data.ShouldBe(_services[1].HostAndPort);
|
||||
address = _roundRobin.Lease().Result;
|
||||
address.Data.ShouldBe(_services[2].HostAndPort);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenIGetTheNextAddress()
|
||||
{
|
||||
_hostAndPort = _roundRobin.Lease().Result;
|
||||
}
|
||||
|
||||
private void ThenTheNextAddressIndexIs(int index)
|
||||
{
|
||||
_hostAndPort.Data.ShouldBe(_services[index].HostAndPort);
|
||||
}
|
||||
}
|
||||
}
|
@ -62,19 +62,11 @@ namespace Ocelot.UnitTests.Responder
|
||||
{
|
||||
this.Given(x => x.GivenTheHttpResponseMessageIs(new HttpResponseMessage()))
|
||||
.And(x => x.GivenThereAreNoPipelineErrors())
|
||||
.And(x => x.GivenTheResponderReturns())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenThereAreNoErrors())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheResponderReturns()
|
||||
{
|
||||
_responder
|
||||
.Setup(x => x.SetResponseOnHttpContext(It.IsAny<HttpContext>(), It.IsAny<HttpResponseMessage>()))
|
||||
.ReturnsAsync(new OkResponse());
|
||||
}
|
||||
|
||||
private void GivenThereAreNoPipelineErrors()
|
||||
{
|
||||
_scopedRepository
|
||||
|
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
public class ConfigurationServiceProviderTests
|
||||
{
|
||||
private ConfigurationServiceProvider _serviceProvider;
|
||||
private HostAndPort _hostAndPort;
|
||||
private List<Service> _result;
|
||||
private List<Service> _expected;
|
||||
|
||||
[Fact]
|
||||
public void should_return_services()
|
||||
{
|
||||
var hostAndPort = new HostAndPort("127.0.0.1", 80);
|
||||
|
||||
var services = new List<Service>
|
||||
{
|
||||
new Service("product", hostAndPort, string.Empty, string.Empty, new string[0])
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenServices(services))
|
||||
.When(x => x.WhenIGetTheService())
|
||||
.Then(x => x.ThenTheFollowingIsReturned(services))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenServices(List<Service> services)
|
||||
{
|
||||
_expected = services;
|
||||
}
|
||||
|
||||
private void WhenIGetTheService()
|
||||
{
|
||||
_serviceProvider = new ConfigurationServiceProvider(_expected);
|
||||
_result = _serviceProvider.Get().Result;
|
||||
}
|
||||
|
||||
private void ThenTheFollowingIsReturned(List<Service> services)
|
||||
{
|
||||
_result[0].HostAndPort.DownstreamHost.ShouldBe(services[0].HostAndPort.DownstreamHost);
|
||||
|
||||
_result[0].HostAndPort.DownstreamPort.ShouldBe(services[0].HostAndPort.DownstreamPort);
|
||||
|
||||
_result[0].Name.ShouldBe(services[0].Name);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
public class ServiceProviderFactoryTests
|
||||
{
|
||||
private ServiceProviderConfiguraion _serviceConfig;
|
||||
private IServiceDiscoveryProvider _result;
|
||||
private readonly ServiceDiscoveryProviderFactory _factory;
|
||||
|
||||
public ServiceProviderFactoryTests()
|
||||
{
|
||||
_factory = new ServiceDiscoveryProviderFactory();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_no_service_provider()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfiguraion("product", "127.0.0.1", 80, false, "Does not matter", string.Empty, 0);
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConfigurationServiceProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_consul_service_provider()
|
||||
{
|
||||
var serviceConfig = new ServiceProviderConfiguraion("product", string.Empty, 0, true, "Consul", string.Empty, 0);
|
||||
|
||||
this.Given(x => x.GivenTheReRoute(serviceConfig))
|
||||
.When(x => x.WhenIGetTheServiceProvider())
|
||||
.Then(x => x.ThenTheServiceProviderIs<ConsulServiceDiscoveryProvider>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheReRoute(ServiceProviderConfiguraion serviceConfig)
|
||||
{
|
||||
_serviceConfig = serviceConfig;
|
||||
}
|
||||
|
||||
private void WhenIGetTheServiceProvider()
|
||||
{
|
||||
_result = _factory.Get(_serviceConfig);
|
||||
}
|
||||
|
||||
private void ThenTheServiceProviderIs<T>()
|
||||
{
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
}
|
||||
}
|
136
test/Ocelot.UnitTests/ServiceDiscovery/ServiceRegistryTests.cs
Normal file
136
test/Ocelot.UnitTests/ServiceDiscovery/ServiceRegistryTests.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||
{
|
||||
public class ServiceRegistryTests
|
||||
{
|
||||
private Service _service;
|
||||
private List<Service> _services;
|
||||
private ServiceRegistry _serviceRegistry;
|
||||
private ServiceRepository _serviceRepository;
|
||||
|
||||
public ServiceRegistryTests()
|
||||
{
|
||||
_serviceRepository = new ServiceRepository();
|
||||
_serviceRegistry = new ServiceRegistry(_serviceRepository);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_register_service()
|
||||
{
|
||||
this.Given(x => x.GivenAServiceToRegister("product", "localhost:5000", 80))
|
||||
.When(x => x.WhenIRegisterTheService())
|
||||
.Then(x => x.ThenTheServiceIsRegistered())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
public void should_lookup_service()
|
||||
{
|
||||
this.Given(x => x.GivenAServiceIsRegistered("product", "localhost:600", 80))
|
||||
.When(x => x.WhenILookupTheService("product"))
|
||||
.Then(x => x.ThenTheServiceDetailsAreReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheServiceDetailsAreReturned()
|
||||
{
|
||||
_services[0].HostAndPort.DownstreamHost.ShouldBe(_service.HostAndPort.DownstreamHost);
|
||||
_services[0].HostAndPort.DownstreamPort.ShouldBe(_service.HostAndPort.DownstreamPort);
|
||||
_services[0].Name.ShouldBe(_service.Name);
|
||||
}
|
||||
|
||||
private void WhenILookupTheService(string name)
|
||||
{
|
||||
_services = _serviceRegistry.Lookup(name);
|
||||
}
|
||||
|
||||
private void GivenAServiceIsRegistered(string name, string address, int port)
|
||||
{
|
||||
_service = new Service(name, new HostAndPort(address, port), string.Empty, string.Empty, new string[0]);
|
||||
_serviceRepository.Set(_service);
|
||||
}
|
||||
|
||||
private void GivenAServiceToRegister(string name, string address, int port)
|
||||
{
|
||||
_service = new Service(name, new HostAndPort(address, port), string.Empty, string.Empty, new string[0]);
|
||||
}
|
||||
|
||||
private void WhenIRegisterTheService()
|
||||
{
|
||||
_serviceRegistry.Register(_service);
|
||||
}
|
||||
|
||||
private void ThenTheServiceIsRegistered()
|
||||
{
|
||||
var serviceNameAndAddress = _serviceRepository.Get(_service.Name);
|
||||
serviceNameAndAddress[0].HostAndPort.DownstreamHost.ShouldBe(_service.HostAndPort.DownstreamHost);
|
||||
serviceNameAndAddress[0].HostAndPort.DownstreamPort.ShouldBe(_service.HostAndPort.DownstreamPort);
|
||||
serviceNameAndAddress[0].Name.ShouldBe(_service.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IServiceRegistry
|
||||
{
|
||||
void Register(Service serviceNameAndAddress);
|
||||
List<Service> Lookup(string name);
|
||||
}
|
||||
|
||||
public class ServiceRegistry : IServiceRegistry
|
||||
{
|
||||
private readonly IServiceRepository _repository;
|
||||
public ServiceRegistry(IServiceRepository repository)
|
||||
{
|
||||
_repository = repository;
|
||||
}
|
||||
|
||||
public void Register(Service serviceNameAndAddress)
|
||||
{
|
||||
_repository.Set(serviceNameAndAddress);
|
||||
}
|
||||
|
||||
public List<Service> Lookup(string name)
|
||||
{
|
||||
return _repository.Get(name);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IServiceRepository
|
||||
{
|
||||
List<Service> Get(string serviceName);
|
||||
void Set(Service serviceNameAndAddress);
|
||||
}
|
||||
|
||||
public class ServiceRepository : IServiceRepository
|
||||
{
|
||||
private Dictionary<string, List<Service>> _registeredServices;
|
||||
|
||||
public ServiceRepository()
|
||||
{
|
||||
_registeredServices = new Dictionary<string, List<Service>>();
|
||||
}
|
||||
|
||||
public List<Service> Get(string serviceName)
|
||||
{
|
||||
return _registeredServices[serviceName];
|
||||
}
|
||||
|
||||
public void Set(Service serviceNameAndAddress)
|
||||
{
|
||||
List<Service> services;
|
||||
if(_registeredServices.TryGetValue(serviceNameAndAddress.Name, out services))
|
||||
{
|
||||
services.Add(serviceNameAndAddress);
|
||||
_registeredServices[serviceNameAndAddress.Name] = services;
|
||||
}
|
||||
else
|
||||
{
|
||||
_registeredServices[serviceNameAndAddress.Name] = new List<Service>(){ serviceNameAndAddress };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.0-*",
|
||||
"version": "0.0.0-dev",
|
||||
|
||||
"testRunner": "xunit",
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||
"Ocelot": "1.0.0-*",
|
||||
"Ocelot": "0.0.0-dev",
|
||||
"xunit": "2.2.0-beta2-build3300",
|
||||
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||
"Moq": "4.6.38-alpha",
|
||||
@ -32,7 +32,7 @@
|
||||
"win7-x64": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.4": {
|
||||
"netcoreapp1.1": {
|
||||
"imports": [
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user