Feature/expose http handlers (#224)

* temp commit

* trying to work out how to expose the http handlers in a decent way..

* pissing about at lunch

* changed to func so you can instanciate object or new it up each time

* docs for dele handlers

* upgraded to sdk 2.1.4

* some validation for consul services
This commit is contained in:
Tom Pallister
2018-02-13 09:07:09 +00:00
committed by GitHub
parent ef3c4f614a
commit 98133d9473
48 changed files with 1352 additions and 266 deletions

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class HttpDelegatingHandlersTests
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
public HttpDelegatingHandlersTests()
{
_steps = new Steps();
}
[Fact]
public void should_call_handlers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 61879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var handlerOne = new FakeHandler();
var handlerTwo = new FakeHandler();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithHandlers(handlerOne, handlerTwo))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheHandlersAreCalledCorrectly(handlerOne, handlerTwo))
.BDDfy();
}
private void ThenTheHandlersAreCalledCorrectly(FakeHandler one, FakeHandler two)
{
one.TimeCalled.ShouldBeLessThan(two.TimeCalled);
}
class FakeHandler : DelegatingHandler
{
public DateTime TimeCalled { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeCalled = DateTime.Now;
return await base.SendAsync(request, cancellationToken);
}
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
}
}

View File

@ -88,6 +88,38 @@ 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 GivenOcelotIsRunningWithHandlers(DelegatingHandler handlerOne, DelegatingHandler handlerTwo)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s =>
{
s.AddSingleton(_webHostBuilder);
s.AddOcelot()
.AddDelegatingHandler(() => handlerOne)
.AddDelegatingHandler(() => handlerTwo);
});
_webHostBuilder.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("configuration.json");
config.AddEnvironmentVariables();
}).Configure(a =>
{
a.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_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>

View File

@ -1,13 +1,7 @@
using System;
using CacheManager.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.ManualTest
{
@ -15,11 +9,6 @@ namespace Ocelot.ManualTest
{
public void ConfigureServices(IServiceCollection services)
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithDictionaryHandle();
};
services.AddAuthentication()
.AddJwtBearer("TestKey", x =>
{
@ -28,7 +17,10 @@ namespace Ocelot.ManualTest
});
services.AddOcelot()
.AddCacheManager(settings)
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
})
.AddOpenTracing(option =>
{
option.CollectorUrl = "http://localhost:9618";
@ -42,4 +34,4 @@ namespace Ocelot.ManualTest
app.UseOcelot().Wait();
}
}
}
}

View File

@ -1,4 +1,8 @@
using CacheManager.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration;
@ -9,10 +13,9 @@ using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
using Ocelot.DependencyInjection;
using Ocelot.Requester;
using Ocelot.UnitTests.Requester;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using TestStack.BDDfy;
using Xunit;
@ -20,11 +23,11 @@ namespace Ocelot.UnitTests.DependencyInjection
{
public class OcelotBuilderTests
{
private IServiceCollection _services;
private readonly IServiceCollection _services;
private IServiceProvider _serviceProvider;
private IConfiguration _configRoot;
private readonly IConfiguration _configRoot;
private IOcelotBuilder _ocelotBuilder;
private int _maxRetries;
private readonly int _maxRetries;
public OcelotBuilderTests()
{
@ -38,6 +41,19 @@ namespace Ocelot.UnitTests.DependencyInjection
}
private Exception _ex;
[Fact]
public void should_add_delegating_handlers()
{
var fakeOne = new FakeDelegatingHandler(0);
var fakeTwo = new FakeDelegatingHandler(1);
this.Given(x => WhenISetUpOcelotServices())
.When(x => AddDelegate(fakeOne))
.And(x => AddDelegate(fakeTwo))
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers())
.BDDfy();
}
[Fact]
public void should_set_up_services()
{
@ -54,7 +70,7 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy();
}
[Fact]
public void should_set_up_cache_manager()
{
@ -74,7 +90,7 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy();
}
[Fact]
[Fact]
public void should_set_up_rafty()
{
this.Given(x => WhenISetUpOcelotServices())
@ -119,6 +135,17 @@ namespace Ocelot.UnitTests.DependencyInjection
path.Path.ShouldBe("/administration");
}
private void ThenTheProviderIsRegisteredAndReturnsHandlers()
{
_serviceProvider = _services.BuildServiceProvider();
var provider = _serviceProvider.GetService<IDelegatingHandlerHandlerProvider>();
var handlers = provider.Get();
var handler = (FakeDelegatingHandler)handlers[0].Invoke();
handler.Order.ShouldBe(0);
handler = (FakeDelegatingHandler)handlers[1].Invoke();
handler.Order.ShouldBe(1);
}
private void OnlyOneVersionOfEachCacheIsRegistered()
{
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
@ -157,6 +184,11 @@ namespace Ocelot.UnitTests.DependencyInjection
}
}
private void AddDelegate(DelegatingHandler handler)
{
_ocelotBuilder.AddDelegatingHandler(() => handler);
}
private void ThenAnOcelotBuilderIsReturned()
{
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();

View File

@ -58,7 +58,7 @@
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false,false)))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false)))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy();
@ -78,7 +78,7 @@
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, false)))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false)))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryQosProviderError())
.BDDfy();
@ -132,8 +132,8 @@
It.IsAny<IQoSProvider>(),
It.IsAny<bool>(),
It.IsAny<bool>(),
It.IsAny<bool>()
))
It.IsAny<string>(),
It.IsAny<bool>()))
.ReturnsAsync(_request);
}

View File

@ -1,4 +1,4 @@
namespace Ocelot.UnitTests.Request
namespace Ocelot.UnitTests.Request
{
using System.Net.Http;
@ -17,9 +17,9 @@
private readonly HttpRequestMessage _requestMessage;
private readonly bool _useCookieContainer;
private readonly bool _allowAutoRedirect;
private readonly bool _useTracing;
private Response<Ocelot.Request.Request> _response;
private string _reRouteKey;
private readonly bool _useTracing;
public HttpRequestCreatorTests()
{
@ -28,7 +28,7 @@
_qoSProvider = new NoQoSProvider();
_useCookieContainer = false;
_allowAutoRedirect = false;
_useTracing = false;
_requestMessage = new HttpRequestMessage();
}
@ -48,7 +48,7 @@
private void WhenIBuildARequest()
{
_response = _requestCreator.Build(_requestMessage,
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _useTracing)
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _reRouteKey, _useTracing)
.GetAwaiter()
.GetResult();
}

View File

@ -0,0 +1,108 @@
using System;
using System.Net.Http;
using Moq;
using Ocelot.Requester;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class DelegatingHandlerHandlerHouseTests
{
private readonly DelegatingHandlerHandlerHouse _house;
private Mock<IDelegatingHandlerHandlerProviderFactory> _factory;
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
private Ocelot.Request.Request _request;
private Response<IDelegatingHandlerHandlerProvider> _result;
public DelegatingHandlerHandlerHouseTests()
{
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
_house = new DelegatingHandlerHandlerHouse(_factory.Object);
}
[Fact]
public void should_create_and_store_provider()
{
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
this.Given(x => GivenTheRequest(request))
.And(x => GivenTheProviderReturns())
.When(x => WhenIGet())
.Then(x => ThenTheFactoryIsCalled(1))
.And(x => ThenTheProviderIsNotNull())
.BDDfy();
}
[Fact]
public void should_get_provider()
{
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
this.Given(x => GivenTheRequest(request))
.And(x => GivenTheProviderReturns())
.And(x => WhenIGet())
.And(x => GivenTheFactoryIsCleared())
.When(x => WhenIGet())
.Then(x => ThenTheFactoryIsCalled(0))
.And(x => ThenTheProviderIsNotNull())
.BDDfy();
}
[Fact]
public void should_return_error()
{
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
this.Given(x => GivenTheRequest(request))
.And(x => GivenTheProviderThrows())
.When(x => WhenIGet())
.And(x => ThenAnErrorIsReturned())
.BDDfy();
}
private void ThenAnErrorIsReturned()
{
_result.IsError.ShouldBeTrue();
_result.Errors[0].ShouldBeOfType<UnableToFindDelegatingHandlerProviderError>();
}
private void GivenTheProviderThrows()
{
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Throws<Exception>();
}
private void GivenTheFactoryIsCleared()
{
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
}
private void ThenTheProviderIsNotNull()
{
_result.Data.ShouldBe(_provider.Object);
}
private void WhenIGet()
{
_result = _house.Get(_request);
}
private void GivenTheRequest(Ocelot.Request.Request request)
{
_request = request;
}
private void GivenTheProviderReturns()
{
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(_provider.Object);
}
private void ThenTheFactoryIsCalled(int times)
{
_factory.Verify(x => x.Get(_request), Times.Exactly(times));
}
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class DelegatingHandlerHandlerProviderFactoryTests
{
private readonly DelegatingHandlerHandlerProviderFactory _factory;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Ocelot.Request.Request _request;
private IDelegatingHandlerHandlerProvider _provider;
private readonly Mock<IDelegatingHandlerHandlerProvider> _allRoutesProvider;
public DelegatingHandlerHandlerProviderFactoryTests()
{
_allRoutesProvider = new Mock<IDelegatingHandlerHandlerProvider>();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, null);
}
[Fact]
public void should_all_from_all_routes_provider_and_qos()
{
var handlers = new List<Func<DelegatingHandler>>
{
() => new FakeDelegatingHandler(0),
() => new FakeDelegatingHandler(1)
};
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
this.Given(x => GivenTheFollowingRequest(request))
.And(x => GivenTheAllRoutesProviderReturns(handlers))
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(3))
.And(x => ThenTheDelegatesAreAddedCorrectly())
.And(x => ThenItIsPolly(2))
.BDDfy();
}
[Fact]
public void should_return_provider_with_no_delegates()
{
var request = new Ocelot.Request.Request(new HttpRequestMessage(), false, null, true, true, "", false);
this.Given(x => GivenTheFollowingRequest(request))
.And(x => GivenTheAllRoutesProviderReturns())
.When(x => WhenIGet())
.Then(x => ThenNoDelegatesAreInTheProvider())
.BDDfy();
}
[Fact]
public void should_return_provider_with_qos_delegate()
{
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
this.Given(x => GivenTheFollowingRequest(request))
.And(x => GivenTheAllRoutesProviderReturns())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(1))
.And(x => ThenItIsPolly(0))
.BDDfy();
}
private void ThenTheDelegatesAreAddedCorrectly()
{
var delegates = _provider.Get();
var del = delegates[0].Invoke();
var handler = (FakeDelegatingHandler) del;
handler.Order.ShouldBe(0);
del = delegates[1].Invoke();
handler = (FakeDelegatingHandler)del;
handler.Order.ShouldBe(1);
}
private void GivenTheAllRoutesProviderReturns()
{
_allRoutesProvider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
}
private void GivenTheAllRoutesProviderReturns(List<Func<DelegatingHandler>> handlers)
{
_allRoutesProvider.Setup(x => x.Get()).Returns(handlers);
}
private void ThenItIsPolly(int i)
{
var delegates = _provider.Get();
var del = delegates[i].Invoke();
del.ShouldBeOfType<PollyCircuitBreakingDelegatingHandler>();
}
private void ThenThereIsDelegatesInProvider(int count)
{
_provider.ShouldNotBeNull();
_provider.Get().Count.ShouldBe(count);
}
private void GivenTheFollowingRequest(Ocelot.Request.Request request)
{
_request = request;
}
private void WhenIGet()
{
_provider = _factory.Get(_request);
}
private void ThenNoDelegatesAreInTheProvider()
{
_provider.ShouldNotBeNull();
_provider.Get().Count.ShouldBe(0);
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class DelegatingHandlerHandlerProviderTests
{
private readonly DelegatingHandlerHandlerProvider _provider;
private List<Func<DelegatingHandler>> _handlers;
public DelegatingHandlerHandlerProviderTests()
{
_provider = new DelegatingHandlerHandlerProvider();
}
[Fact]
public void should_return_empty_list()
{
this.When(x => WhenIGet())
.Then(x => ThenAnEmptyListIsReturned())
.BDDfy();
}
[Fact]
public void should_get_delegating_handlers_in_order_first_in_first_out()
{
this.Given(x => GivenTheHandlers())
.When(x => WhenIGet())
.Then(x => ThenTheHandlersAreReturnedInOrder())
.BDDfy();
}
private void ThenAnEmptyListIsReturned()
{
_handlers.Count.ShouldBe(0);
}
private void ThenTheHandlersAreReturnedInOrder()
{
var handler = (FakeDelegatingHandler)_handlers[0].Invoke();
handler.Order.ShouldBe(0);
handler = (FakeDelegatingHandler)_handlers[1].Invoke();
handler.Order.ShouldBe(1);
}
private void WhenIGet()
{
_handlers = _provider.Get();
}
private void GivenTheHandlers()
{
_provider.Add(() => new FakeDelegatingHandler(0));
_provider.Add(() => new FakeDelegatingHandler(1));
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Ocelot.UnitTests.Requester
{
public class FakeDelegatingHandler : DelegatingHandler
{
public FakeDelegatingHandler()
{
}
public FakeDelegatingHandler(int order)
{
Order = order;
}
public int Order {get;private set;}
public DateTime TimeCalled {get;private set;}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeCalled = DateTime.Now;
return new HttpResponseMessage();
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Moq;
using Ocelot.Requester;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class HttpClientBuilderTests
{
private readonly HttpClientBuilder _builder;
private readonly Mock<IDelegatingHandlerHandlerHouse> _house;
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
private IHttpClientBuilder _builderResult;
private IHttpClient _httpClient;
private HttpResponseMessage _response;
private Ocelot.Request.Request _request;
public HttpClientBuilderTests()
{
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
_house = new Mock<IDelegatingHandlerHandlerHouse>();
_builder = new HttpClientBuilder(_house.Object);
}
[Fact]
public void should_build_http_client()
{
this.Given(x => GivenTheProviderReturns())
.And(x => GivenARequest())
.And(x => GivenTheHouseReturns())
.When(x => WhenIBuild())
.Then(x => ThenTheHttpClientShouldNotBeNull())
.BDDfy();
}
[Fact]
public void should_call_delegating_handlers_in_order()
{
var fakeOne = new FakeDelegatingHandler();
var fakeTwo = new FakeDelegatingHandler();
var handlers = new List<Func<DelegatingHandler>>()
{
() => fakeOne,
() => fakeTwo
};
this.Given(x => GivenTheProviderReturns(handlers))
.And(x => GivenARequest())
.And(x => GivenTheHouseReturns())
.And(x => WhenIBuild())
.When(x => WhenICallTheClient())
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
.And(x => ThenSomethingIsReturned())
.BDDfy();
}
private void GivenARequest()
{
_request = new Ocelot.Request.Request(null, false, null, false, false, "", false);
}
private void GivenTheHouseReturns()
{
_house
.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>()))
.Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
}
private void ThenSomethingIsReturned()
{
_response.ShouldNotBeNull();
}
private void WhenICallTheClient()
{
_response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult();
}
private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo)
{
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
}
private void GivenTheProviderReturns()
{
_provider
.Setup(x => x.Get())
.Returns(new List<Func<DelegatingHandler>>(){ () => new FakeDelegatingHandler()});
}
private void GivenTheProviderReturns(List<Func<DelegatingHandler>> handlers)
{
_provider
.Setup(x => x.Get())
.Returns(handlers);
}
private void WhenIBuild()
{
_httpClient = _builder.Create(_request);
}
private void ThenTheHttpClientShouldNotBeNull()
{
_httpClient.ShouldNotBeNull();
}
}
}

View File

@ -10,13 +10,15 @@ using System.Net.Http;
using System.Text;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
namespace Ocelot.UnitTests.Requester
{
public class HttpClientHttpRequesterTest
{
private readonly Mock<IHttpClientCache> _cacheHandlers;
private Mock<IServiceProvider> _serviceProvider;
private Mock<IDelegatingHandlerHandlerHouse> _house;
private Mock<IDelegatingHandlerHandlerProvider> _provider;
private Response<HttpResponseMessage> _response;
private readonly HttpClientHttpRequester _httpClientRequester;
private Ocelot.Request.Request _request;
@ -25,29 +27,32 @@ namespace Ocelot.UnitTests.Requester
public HttpClientHttpRequesterTest()
{
_serviceProvider = new Mock<IServiceProvider>();
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
_provider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
_house = new Mock<IDelegatingHandlerHandlerHouse>();
_house.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
_logger = new Mock<IOcelotLogger>();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_loggerFactory
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
.Returns(_logger.Object);
_cacheHandlers = new Mock<IHttpClientCache>();
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _serviceProvider.Object);
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _house.Object);
}
[Fact]
public void should_call_request_correctly()
{
this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, false)))
this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, "", false)))
.When(x=>x.WhenIGetResponse())
.Then(x => x.ThenTheResponseIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_call_request_UnableToCompleteRequest()
public void should_call_request_unable_to_complete_request()
{
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, false)))
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, "", false)))
.When(x => x.WhenIGetResponse())
.Then(x => x.ThenTheResponseIsCalledError())
.BDDfy();
@ -65,12 +70,12 @@ namespace Ocelot.UnitTests.Requester
private void ThenTheResponseIsCalledCorrectly()
{
Assert.True(_response.IsError == false);
_response.IsError.ShouldBeFalse();
}
private void ThenTheResponseIsCalledError()
{
Assert.True(_response.IsError == true);
_response.IsError.ShouldBeTrue();
}
}
}

View File

@ -1,4 +1,4 @@
namespace Ocelot.UnitTests.Requester
namespace Ocelot.UnitTests.Requester
{
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
@ -28,7 +28,7 @@
[Fact]
public void should_call_scoped_data_repository_correctly()
{
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false,false)))
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false, "", false)))
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
.And(x => x.GivenTheScopedRepoReturns())
.When(x => x.WhenICallTheMiddleware())

View File

@ -1,10 +1,8 @@
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using Ocelot.UnitTests.LoadBalancer;
using Shouldly;
using TestStack.BDDfy;
using Xunit;

View File

@ -121,7 +121,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(32, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(33, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
}
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)

View File

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
namespace Ocelot.UnitTests.ServiceDiscovery
{
public class ConsulServiceDiscoveryProviderTests : IDisposable
{
private IWebHost _fakeConsulBuilder;
private readonly List<ServiceEntry> _serviceEntries;
private readonly ConsulServiceDiscoveryProvider _provider;
private readonly string _serviceName;
private readonly int _port;
private readonly string _consulHost;
private readonly string _fakeConsulServiceDiscoveryUrl;
private List<Service> _services;
private Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IOcelotLogger> _logger;
public ConsulServiceDiscoveryProviderTests()
{
_serviceName = "test";
_port = 8500;
_consulHost = "localhost";
_fakeConsulServiceDiscoveryUrl = $"http://{_consulHost}:{_port}";
_serviceEntries = new List<ServiceEntry>();
_factory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_factory.Setup(x => x.CreateLogger<ConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName);
_provider = new ConsulServiceDiscoveryProvider(config, _factory.Object);
}
[Fact]
public void should_return_service_from_consul()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x =>GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(1))
.BDDfy();
}
[Fact]
public void should_not_return_services_with_invalid_address()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "http://localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "http://localhost",
Port = 50888,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(0))
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress())
.BDDfy();
}
[Fact]
public void should_not_return_services_with_invalid_port()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = -1,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = 0,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(0))
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts())
.BDDfy();
}
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()
{
_logger.Verify(
x => x.LogError(
"Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
_logger.Verify(
x => x.LogError(
"Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
}
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()
{
_logger.Verify(
x => x.LogError(
"Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
_logger.Verify(
x => x.LogError(
"Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
}
private void ThenTheCountIs(int count)
{
_services.Count.ShouldBe(count);
}
private void WhenIGetTheServices()
{
_services = _provider.Get().GetAwaiter().GetResult();
}
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
{
foreach (var serviceEntry in serviceEntries)
{
_serviceEntries.Add(serviceEntry);
}
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
{
_fakeConsulBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
{
await context.Response.WriteJsonAsync(_serviceEntries);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
public void Dispose()
{
_fakeConsulBuilder?.Dispose();
}
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Shouldly;
using TestStack.BDDfy;
@ -15,10 +17,12 @@ namespace Ocelot.UnitTests.ServiceDiscovery
private IServiceDiscoveryProvider _result;
private readonly ServiceDiscoveryProviderFactory _factory;
private ReRoute _reRoute;
private Mock<IOcelotLoggerFactory> _loggerFactory;
public ServiceProviderFactoryTests()
{
_factory = new ServiceDiscoveryProviderFactory();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object);
}
[Fact]
@ -104,4 +108,4 @@ namespace Ocelot.UnitTests.ServiceDiscovery
_result.ShouldBeOfType<T>();
}
}
}
}