mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:50:49 +08:00 
			
		
		
		
	tests to handle some error cases and docs
This commit is contained in:
		@@ -6,7 +6,13 @@ namespace Ocelot.AcceptanceTests
 | 
			
		||||
    using Shouldly;
 | 
			
		||||
    using System;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Configuration;
 | 
			
		||||
    using Middleware;
 | 
			
		||||
    using Responses;
 | 
			
		||||
    using ServiceDiscovery.Providers;
 | 
			
		||||
    using TestStack.BDDfy;
 | 
			
		||||
    using Values;
 | 
			
		||||
    using Xunit;
 | 
			
		||||
 | 
			
		||||
    public class LoadBalancerTests : IDisposable
 | 
			
		||||
@@ -122,6 +128,88 @@ namespace Ocelot.AcceptanceTests
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_load_balance_request_with_custom_load_balancer()
 | 
			
		||||
        {
 | 
			
		||||
            var downstreamPortOne = RandomPortFinder.GetRandomPort();
 | 
			
		||||
            var downstreamPortTwo = RandomPortFinder.GetRandomPort();
 | 
			
		||||
            var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
 | 
			
		||||
            var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
 | 
			
		||||
 | 
			
		||||
            var configuration = new FileConfiguration
 | 
			
		||||
            {
 | 
			
		||||
                ReRoutes = new List<FileReRoute>
 | 
			
		||||
                    {
 | 
			
		||||
                        new FileReRoute
 | 
			
		||||
                        {
 | 
			
		||||
                            DownstreamPathTemplate = "/",
 | 
			
		||||
                            DownstreamScheme = "http",
 | 
			
		||||
                            UpstreamPathTemplate = "/",
 | 
			
		||||
                            UpstreamHttpMethod = new List<string> { "Get" },
 | 
			
		||||
                            LoadBalancerOptions = new FileLoadBalancerOptions { Type = nameof(CustomLoadBalancer) },
 | 
			
		||||
                            DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                            {
 | 
			
		||||
                                new FileHostAndPort
 | 
			
		||||
                                {
 | 
			
		||||
                                    Host = "localhost",
 | 
			
		||||
                                    Port = downstreamPortOne,
 | 
			
		||||
                                },
 | 
			
		||||
                                new FileHostAndPort
 | 
			
		||||
                                {
 | 
			
		||||
                                    Host = "localhost",
 | 
			
		||||
                                    Port = downstreamPortTwo,
 | 
			
		||||
                                },
 | 
			
		||||
                            },
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                GlobalConfiguration = new FileGlobalConfiguration(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Func<IServiceProvider, DownstreamReRoute, IServiceDiscoveryProvider, CustomLoadBalancer> loadBalancerFactoryFunc = (serviceProvider, reRoute, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get);
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
 | 
			
		||||
                .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
 | 
			
		||||
                .And(x => _steps.GivenThereIsAConfiguration(configuration))
 | 
			
		||||
                .And(x => _steps.GivenOcelotIsRunningWithCustomLoadBalancer(loadBalancerFactoryFunc))
 | 
			
		||||
                .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50))
 | 
			
		||||
                .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
 | 
			
		||||
                .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private class CustomLoadBalancer : ILoadBalancer
 | 
			
		||||
        {
 | 
			
		||||
            private readonly Func<Task<List<Service>>> _services;
 | 
			
		||||
            private readonly object _lock = new object();
 | 
			
		||||
 | 
			
		||||
            private int _last;
 | 
			
		||||
 | 
			
		||||
            public CustomLoadBalancer(Func<Task<List<Service>>> services)
 | 
			
		||||
            {
 | 
			
		||||
                _services = services;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public async Task<Response<ServiceHostAndPort>> Lease(DownstreamContext downstreamContext)
 | 
			
		||||
            {
 | 
			
		||||
                var services = await _services();
 | 
			
		||||
                lock (_lock)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_last >= services.Count)
 | 
			
		||||
                    {
 | 
			
		||||
                        _last = 0;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var next = services[_last];
 | 
			
		||||
                    _last++;
 | 
			
		||||
                    return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Release(ServiceHostAndPort hostAndPort)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
 | 
			
		||||
        {
 | 
			
		||||
            _counterOne.ShouldBeInRange(bottom, top);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,9 @@ namespace Ocelot.AcceptanceTests
 | 
			
		||||
    using System.Text;
 | 
			
		||||
    using System.Threading;
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Configuration;
 | 
			
		||||
    using LoadBalancer.LoadBalancers;
 | 
			
		||||
    using ServiceDiscovery.Providers;
 | 
			
		||||
    using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
 | 
			
		||||
    using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
 | 
			
		||||
    using CookieHeaderValue = Microsoft.Net.Http.Headers.CookieHeaderValue;
 | 
			
		||||
@@ -255,6 +258,39 @@ namespace Ocelot.AcceptanceTests
 | 
			
		||||
            _ocelotClient = _ocelotServer.CreateClient();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public void GivenOcelotIsRunningWithCustomLoadBalancer<T>(Func<IServiceProvider, DownstreamReRoute, IServiceDiscoveryProvider, T> loadBalancerFactoryFunc)
 | 
			
		||||
            where T : ILoadBalancer
 | 
			
		||||
        {
 | 
			
		||||
            _webHostBuilder = new WebHostBuilder();
 | 
			
		||||
 | 
			
		||||
            _webHostBuilder
 | 
			
		||||
                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
			
		||||
                {
 | 
			
		||||
                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
			
		||||
                    var env = hostingContext.HostingEnvironment;
 | 
			
		||||
                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
 | 
			
		||||
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                    config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                    config.AddEnvironmentVariables();
 | 
			
		||||
                })
 | 
			
		||||
                .ConfigureServices(s =>
 | 
			
		||||
                {
 | 
			
		||||
                    s.AddOcelot()
 | 
			
		||||
                        .AddCustomLoadBalancer(loadBalancerFactoryFunc);
 | 
			
		||||
                })
 | 
			
		||||
                .Configure(app =>
 | 
			
		||||
                {
 | 
			
		||||
                    app.UseOcelot().Wait();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            _ocelotServer = new TestServer(_webHostBuilder);
 | 
			
		||||
 | 
			
		||||
            _ocelotClient = _ocelotServer.CreateClient();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void GivenOcelotIsRunningWithConsul()
 | 
			
		||||
        {
 | 
			
		||||
            _webHostBuilder = new WebHostBuilder();
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,24 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
			
		||||
                .Then(x => x.ThenTheLoadBalancerIsReturned<FakeLoadBalancerTwo>())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_return_error_response_if_cannot_find_load_balancer_creator()
 | 
			
		||||
        {
 | 
			
		||||
            var reRoute = new DownstreamReRouteBuilder()
 | 
			
		||||
                .WithLoadBalancerOptions(new LoadBalancerOptions("DoesntExistLoadBalancer", "", 0))
 | 
			
		||||
                .WithUpstreamHttpMethod(new List<string> { "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.ThenAnErrorResponseIsReturned())
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageIsCorrect())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_call_service_provider()
 | 
			
		||||
        {
 | 
			
		||||
@@ -147,6 +164,11 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
			
		||||
            _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<T> : ILoadBalancerCreator
 | 
			
		||||
            where T : ILoadBalancer, new()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ namespace Ocelot.UnitTests.Responder
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData(OcelotErrorCode.UnableToCompleteRequestError)]
 | 
			
		||||
        [InlineData(OcelotErrorCode.CouldNotFindLoadBalancerCreator)]
 | 
			
		||||
        public void should_return_internal_server_error(OcelotErrorCode errorCode)
 | 
			
		||||
        {
 | 
			
		||||
            ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.InternalServerError);
 | 
			
		||||
@@ -120,7 +121,7 @@ namespace Ocelot.UnitTests.Responder
 | 
			
		||||
            var errors = new List<OcelotErrorCode>
 | 
			
		||||
            {
 | 
			
		||||
                OcelotErrorCode.CannotAddDataError,
 | 
			
		||||
                OcelotErrorCode.RequestTimedOutError
 | 
			
		||||
                OcelotErrorCode.RequestTimedOutError,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            ShouldMapErrorsToStatusCode(errors, HttpStatusCode.ServiceUnavailable);
 | 
			
		||||
@@ -132,7 +133,7 @@ namespace Ocelot.UnitTests.Responder
 | 
			
		||||
            // If this test fails then it's because the number of error codes has changed.
 | 
			
		||||
            // You should make the appropriate changes to the test cases here to ensure
 | 
			
		||||
            // they cover all the error codes, and then modify this assertion.
 | 
			
		||||
            Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(39, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
 | 
			
		||||
            Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(40, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user