diff --git a/codeanalysis.ruleset b/codeanalysis.ruleset index ee66e99d..4b278ba8 100644 --- a/codeanalysis.ruleset +++ b/codeanalysis.ruleset @@ -1,74 +1,10 @@  - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -77,76 +13,76 @@ - + - - + + - + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + - - - + + + - - - + + + + - - - + + + - \ No newline at end of file diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst index a6f2e4e9..b3108534 100644 --- a/docs/features/delegatinghandlers.rst +++ b/docs/features/delegatinghandlers.rst @@ -4,28 +4,21 @@ Delegating Handers Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 `_ and I decided that it was going to be useful in various ways. Usage -^^^^^^ +^^^^^ In order to add delegating handlers to the HttpClient transport you need to do the following. -.. code-block:: csharp - - services.AddOcelot() - .AddDelegatingHandler(() => new FakeHandler()) - .AddDelegatingHandler(() => new FakeHandler()); - -Or for singleton like behaviour.. +This will register the Handlers as singletons. Because Ocelot caches the HttpClient for the downstream services to avoid +socket exhaustion (well known http client issue) you can only register singleton handlers. .. code-block:: csharp - var handlerOne = new FakeHandler(); - var handlerTwo = new FakeHandler(); - services.AddOcelot() - .AddDelegatingHandler(() => handlerOne) - .AddDelegatingHandler(() => handlerTwo); + .AddDelegatingHandler() + .AddDelegatingHandler() -You can have as many DelegatingHandlers as you want and they are run in a first in first out order. If you are using Ocelot's QoS functionality then that will always be run after your last delegating handler. +You can have as many DelegatingHandlers as you want and they are run in a first in first out order. If you are using Ocelot's QoS functionality then that will always be run after your last delegating handler. If you are also registering handlers in DI these will be +run first. In order to create a class that can be used a delegating handler it must look as follows diff --git a/docs/images/OcelotServiceFabric.jpg b/docs/images/OcelotServiceFabric.jpg new file mode 100644 index 00000000..e4aaab7a Binary files /dev/null and b/docs/images/OcelotServiceFabric.jpg differ diff --git a/docs/introduction/bigpicture.rst b/docs/introduction/bigpicture.rst index 95e62fdc..989c0f5c 100644 --- a/docs/introduction/bigpicture.rst +++ b/docs/introduction/bigpicture.rst @@ -36,3 +36,7 @@ Multiple Instances With Consul ^^^^^^^^^^^ .. image:: ../images/OcelotMultipleInstancesConsul.jpg + +With Service Fabric +^^^^^^^^^^^^^^^^^^^ +.. image:: ../images/OcelotServiceFabric.jpg diff --git a/docs/introduction/notsupported.rst b/docs/introduction/notsupported.rst index 8ef9b5e7..35c916a0 100644 --- a/docs/introduction/notsupported.rst +++ b/docs/introduction/notsupported.rst @@ -5,4 +5,28 @@ Ocelot does not support... * Chunked Encoding - Ocelot will always get the body size and return Content-Length header. Sorry if this doesn't work for your use case! -* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :( \ No newline at end of file +* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :( + +* Swagger - I have looked multiple times at building swagger.json out of the Ocelot configuration.json but it doesnt fit into the vision I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore + +.. code-block:: csharp + + app.Map("/swagger/v1/swagger.json", b => + { + b.Run(async x => { + var json = File.ReadAllText("swagger.json"); + await x.Response.WriteAsync(json); + }); + }); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ocelot"); + }); + + app.UseOcelot().Wait(); + +The main reasons why I don't think Swagger makes sense is we already hand roll our definition in configuration.json. If we want people developing against Ocelot to be able to see what routes are available then either share the configuration.json with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration. + +In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to there product service and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot information would not match. Unless I rolled my own Swagger implementation. + +If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might even be possible to write something that maps configuration.json to the postman json spec. However I don't intend to do this. \ No newline at end of file diff --git a/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs index 29a0c36e..ff5e3876 100644 --- a/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/FileConfigurationRepository.cs @@ -18,7 +18,7 @@ namespace Ocelot.Configuration.Repository _configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json"; } - public async Task> Get() + public Task> Get() { string jsonConfiguration; @@ -29,10 +29,10 @@ namespace Ocelot.Configuration.Repository var fileConfiguration = JsonConvert.DeserializeObject(jsonConfiguration); - return new OkResponse(fileConfiguration); + return Task.FromResult>(new OkResponse(fileConfiguration)); } - public async Task Set(FileConfiguration fileConfiguration) + public Task Set(FileConfiguration fileConfiguration) { string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); @@ -45,8 +45,8 @@ namespace Ocelot.Configuration.Repository System.IO.File.WriteAllText(_configFilePath, jsonConfiguration); } - - return new OkResponse(); + + return Task.FromResult(new OkResponse()); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs index 99f6a51b..9ce50ba6 100644 --- a/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/InMemoryOcelotConfigurationRepository.cs @@ -12,19 +12,19 @@ namespace Ocelot.Configuration.Repository private IOcelotConfiguration _ocelotConfiguration; - public async Task> Get() + public Task> Get() { - return new OkResponse(_ocelotConfiguration); + return Task.FromResult>(new OkResponse(_ocelotConfiguration)); } - public async Task AddOrReplace(IOcelotConfiguration ocelotConfiguration) + public Task AddOrReplace(IOcelotConfiguration ocelotConfiguration) { lock (LockObject) { _ocelotConfiguration = ocelotConfiguration; } - return new OkResponse(); + return Task.FromResult(new OkResponse()); } } -} \ No newline at end of file +} diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 48db5ef7..ad1e4777 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -3,16 +3,22 @@ using CacheManager.Core; using System; using System.Net.Http; using IdentityServer4.AccessTokenValidation; +using Ocelot.Requester; namespace Ocelot.DependencyInjection { public interface IOcelotBuilder { IOcelotBuilder AddStoreOcelotConfigurationInConsul(); + IOcelotBuilder AddCacheManager(Action settings); + IOcelotBuilder AddOpenTracing(Action settings); + IOcelotAdministrationBuilder AddAdministration(string path, string secret); + IOcelotAdministrationBuilder AddAdministration(string path, Action configOptions); - IOcelotBuilder AddDelegatingHandler(Func delegatingHandler); + + IOcelotBuilder AddDelegatingHandler() where T : DelegatingHandler; } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index e1ef6b42..75dddb1d 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -57,7 +57,6 @@ namespace Ocelot.DependencyInjection { private readonly IServiceCollection _services; private readonly IConfiguration _configurationRoot; - private readonly IDelegatingHandlerHandlerProvider _provider; public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) { @@ -120,8 +119,7 @@ namespace Ocelot.DependencyInjection _services.TryAddSingleton(); _services.TryAddSingleton(); _services.TryAddSingleton(); - _services.TryAddSingleton(); - _services.TryAddSingleton(); + _services.TryAddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository @@ -144,9 +142,6 @@ namespace Ocelot.DependencyInjection _services.AddWebEncoders(); _services.AddSingleton(new NullAdministrationPath()); - //these get picked out later and added to http request - _provider = new DelegatingHandlerHandlerProvider(); - _services.TryAddSingleton(_provider); _services.TryAddSingleton(); _services.TryAddSingleton(); _services.AddSingleton(); @@ -187,9 +182,10 @@ namespace Ocelot.DependencyInjection return new OcelotAdministrationBuilder(_services, _configurationRoot); } - public IOcelotBuilder AddDelegatingHandler(Func delegatingHandler) + public IOcelotBuilder AddDelegatingHandler() + where THandler : DelegatingHandler { - _provider.Add(delegatingHandler); + _services.AddSingleton(); return this; } diff --git a/src/Ocelot/Request/Mapper/RequestMapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs index 9af0d08e..ee04a551 100644 --- a/src/Ocelot/Request/Mapper/RequestMapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -47,11 +47,28 @@ var content = new ByteArrayContent(await ToByteArray(request.Body)); - content.Headers.TryAddWithoutValidation("Content-Type", new[] {request.ContentType}); + content.Headers + .TryAddWithoutValidation("Content-Type", new[] {request.ContentType}); + AddHeaderIfExistsOnRequest("Content-Language", content, request); + AddHeaderIfExistsOnRequest("Content-Location", content, request); + AddHeaderIfExistsOnRequest("Content-Range", content, request); + AddHeaderIfExistsOnRequest("Content-MD5", content, request); + AddHeaderIfExistsOnRequest("Content-Disposition", content, request); + AddHeaderIfExistsOnRequest("Content-Encoding", content, request); + return content; } + private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request) + { + if(request.Headers.ContainsKey(key)) + { + content.Headers + .TryAddWithoutValidation(key, request.Headers[key].ToList()); + } + } + private HttpMethod MapMethod(HttpRequest request) { return new HttpMethod(request.Method); diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs deleted file mode 100644 index eec395fb..00000000 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using Ocelot.Configuration; -using Ocelot.Errors; -using Ocelot.Responses; - -namespace Ocelot.Requester -{ - public class DelegatingHandlerHandlerHouse : IDelegatingHandlerHandlerHouse - { - private readonly IDelegatingHandlerHandlerProviderFactory _factory; - private readonly ConcurrentDictionary _housed; - - public DelegatingHandlerHandlerHouse(IDelegatingHandlerHandlerProviderFactory factory) - { - _factory = factory; - _housed = new ConcurrentDictionary(); - } - - public Response Get(DownstreamReRoute request) - { - try - { - if (_housed.TryGetValue(request.ReRouteKey, out var provider)) - { - //todo once day we might need a check here to see if we need to create a new provider - provider = _housed[request.ReRouteKey]; - return new OkResponse(provider); - } - - //todo - unit test for this - var providerResponse = _factory.Get(request); - - if (providerResponse.IsError) - { - return new ErrorResponse(providerResponse.Errors); - } - - provider = providerResponse.Data; - AddHoused(request.ReRouteKey, provider); - return new OkResponse(provider); - } - catch (Exception ex) - { - return new ErrorResponse(new List() - { - new UnableToFindDelegatingHandlerProviderError($"unabe to find delegating handler provider for {request.ReRouteKey} exception is {ex}") - }); - } - } - - private void AddHoused(string key, IDelegatingHandlerHandlerProvider provider) - { - _housed.AddOrUpdate(key, provider, (k, v) => provider); - } - } -} diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs deleted file mode 100644 index 71684c52..00000000 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; - -namespace Ocelot.Requester -{ - public class DelegatingHandlerHandlerProvider : IDelegatingHandlerHandlerProvider - { - private readonly Dictionary> _handlers; - - public DelegatingHandlerHandlerProvider() - { - _handlers = new Dictionary>(); - } - - public void Add(Func handler) - { - var key = _handlers.Count == 0 ? 0 : _handlers.Count + 1; - _handlers[key] = handler; - } - - public List> Get() - { - return _handlers.Count > 0 ? _handlers.OrderBy(x => x.Key).Select(x => x.Value).ToList() : new List>(); - } - } -} diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs index 468b6013..4e242137 100644 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs +++ b/src/Ocelot/Requester/DelegatingHandlerHandlerProviderFactory.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; using Ocelot.Configuration; using Ocelot.Logging; using Ocelot.Requester.QoS; @@ -7,38 +9,38 @@ using Ocelot.Responses; namespace Ocelot.Requester { - public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory + public class DelegatingHandlerHandlerFactory : IDelegatingHandlerHandlerFactory { private readonly ITracingHandlerFactory _factory; private readonly IOcelotLoggerFactory _loggerFactory; - private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider; private readonly IQosProviderHouse _qosProviderHouse; + private readonly IServiceProvider _serviceProvider; - public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, - IDelegatingHandlerHandlerProvider allRoutesProvider, + public DelegatingHandlerHandlerFactory(IOcelotLoggerFactory loggerFactory, ITracingHandlerFactory factory, - IQosProviderHouse qosProviderHouse) + IQosProviderHouse qosProviderHouse, + IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; _factory = factory; _loggerFactory = loggerFactory; - _allRoutesProvider = allRoutesProvider; _qosProviderHouse = qosProviderHouse; } - public Response Get(DownstreamReRoute request) + public Response>> Get(DownstreamReRoute request) { - var handlersAppliedToAll = _allRoutesProvider.Get(); + var handlersAppliedToAll = _serviceProvider.GetServices(); - var provider = new DelegatingHandlerHandlerProvider(); + var handlers = new List>(); foreach (var handler in handlersAppliedToAll) { - provider.Add(handler); + handlers.Add(() => handler); } if (request.HttpHandlerOptions.UseTracing) { - provider.Add(() => (DelegatingHandler)_factory.Get()); + handlers.Add(() => (DelegatingHandler)_factory.Get()); } if (request.IsQos) @@ -47,13 +49,13 @@ namespace Ocelot.Requester if (qosProvider.IsError) { - return new ErrorResponse(qosProvider.Errors); + return new ErrorResponse>>(qosProvider.Errors); } - provider.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory)); + handlers.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory)); } - return new OkResponse(provider); + return new OkResponse>>(handlers); } } } diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 2d3a0f36..b5604081 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -6,11 +6,11 @@ namespace Ocelot.Requester { public class HttpClientBuilder : IHttpClientBuilder { - private readonly IDelegatingHandlerHandlerHouse _house; + private readonly IDelegatingHandlerHandlerFactory _factory; - public HttpClientBuilder(IDelegatingHandlerHandlerHouse house) + public HttpClientBuilder(IDelegatingHandlerHandlerFactory house) { - _house = house; + _factory = house; } public IHttpClient Create(DownstreamReRoute request) @@ -24,11 +24,9 @@ namespace Ocelot.Requester private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request) { - var provider = _house.Get(request); - - var handlers = provider.Data.Get(); - //todo handle error + var handlers = _factory.Get(request).Data; + handlers .Select(handler => handler) .Reverse() diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index de9ea1c6..f2d32733 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -13,20 +13,20 @@ namespace Ocelot.Requester { private readonly IHttpClientCache _cacheHandlers; private readonly IOcelotLogger _logger; - private readonly IDelegatingHandlerHandlerHouse _house; + private readonly IDelegatingHandlerHandlerFactory _factory; public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers, - IDelegatingHandlerHandlerHouse house) + IDelegatingHandlerHandlerFactory house) { _logger = loggerFactory.CreateLogger(); _cacheHandlers = cacheHandlers; - _house = house; + _factory = house; } public async Task> GetResponse(DownstreamContext request) { - var builder = new HttpClientBuilder(_house); + var builder = new HttpClientBuilder(_factory); var cacheKey = GetCacheKey(request); diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs deleted file mode 100644 index b236ed16..00000000 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Ocelot.Configuration; -using Ocelot.Responses; - -namespace Ocelot.Requester -{ - public interface IDelegatingHandlerHandlerHouse - { - Response Get(DownstreamReRoute request); - } -} diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs deleted file mode 100644 index addaaeb7..00000000 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; - -namespace Ocelot.Requester -{ - public interface IDelegatingHandlerHandlerProvider - { - void Add(Func handler); - List> Get(); - } -} diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs index c77a62bd..53b6a73c 100644 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs +++ b/src/Ocelot/Requester/IDelegatingHandlerHandlerProviderFactory.cs @@ -1,10 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; using Ocelot.Configuration; using Ocelot.Responses; namespace Ocelot.Requester { - public interface IDelegatingHandlerHandlerProviderFactory + public interface IDelegatingHandlerHandlerFactory { - Response Get(DownstreamReRoute request); + Response>> Get(DownstreamReRoute request); } } diff --git a/src/Ocelot/ServiceDiscovery/ServiceFabricServiceDiscoveryProvider.cs b/src/Ocelot/ServiceDiscovery/ServiceFabricServiceDiscoveryProvider.cs index 28f9bb65..257298b7 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceFabricServiceDiscoveryProvider.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceFabricServiceDiscoveryProvider.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Ocelot.Values; @@ -14,16 +13,16 @@ namespace Ocelot.ServiceDiscovery _configuration = configuration; } - public async Task> Get() + public Task> Get() { - return new List + return Task.FromResult(new List { new Service(_configuration.ServiceName, new ServiceHostAndPort(_configuration.HostName, _configuration.Port), "doesnt matter with service fabric", "doesnt matter with service fabric", new List()) - }; + }); } } } diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs new file mode 100644 index 00000000..9c04314c --- /dev/null +++ b/test/Ocelot.AcceptanceTests/GzipTests.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +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 GzipTests : IDisposable + { + private IWebHost _builder; + private readonly Steps _steps; + + public GzipTests() + { + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51879, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + } + } + }; + + var input = "people"; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura", "\"people\"")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenThePostHasGzipContent(input)) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expected) + { + _builder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + if(context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding)) + { + contentEncoding.First().ShouldBe("gzip"); + + string text = null; + using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress)) + { + using (var sr = new StreamReader(decompress)) { + text = sr.ReadToEnd(); + } + } + + if(text != expected) + { + throw new Exception("not gzipped"); + } + + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + }); + }) + .Build(); + + _builder.Start(); + } + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index b5a2711e..5257b448 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -18,7 +18,6 @@ namespace Ocelot.AcceptanceTests { private IWebHost _builder; private readonly Steps _steps; - private string _downstreamPath; public HeaderTests() { @@ -221,13 +220,15 @@ namespace Ocelot.AcceptanceTests .Configure(app => { app.UsePathBase(basePath); - app.Run(async context => + app.Run(context => { context.Response.OnStarting(() => { context.Response.Headers.Add(headerKey, headerValue); context.Response.StatusCode = statusCode; return Task.CompletedTask; }); + + return Task.CompletedTask; }); }) .Build(); @@ -235,11 +236,6 @@ namespace Ocelot.AcceptanceTests _builder.Start(); } - internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath) - { - _downstreamPath.ShouldBe(expectedDownstreamPath); - } - public void Dispose() { _builder?.Dispose(); diff --git a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs index c54294cd..72f1f341 100644 --- a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs @@ -26,8 +26,9 @@ namespace Ocelot.AcceptanceTests _steps = new Steps(); } + [Fact] - public void should_call_handlers() + public void should_call_di_handlers() { var configuration = new FileConfiguration { @@ -42,7 +43,7 @@ namespace Ocelot.AcceptanceTests new FileHostAndPort { Host = "localhost", - Port = 61879, + Port = 7187, } }, UpstreamPathTemplate = "/", @@ -51,27 +52,98 @@ namespace Ocelot.AcceptanceTests } }; - var handlerOne = new FakeHandler(); - var handlerTwo = new FakeHandler(); - - this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", "/", 200, "Hello from Laura")) + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7187", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithHandlers(handlerOne, handlerTwo)) + .And(x => _steps.GivenOcelotIsRunningWithHandlersRegisteredInDi()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => ThenTheHandlersAreCalledCorrectly(handlerOne, handlerTwo)) + .And(x => ThenTheHandlersAreCalledCorrectly()) .BDDfy(); } - private void ThenTheHandlersAreCalledCorrectly(FakeHandler one, FakeHandler two) + + [Fact] + public void should_call_di_handlers_with_dependency() { - one.TimeCalled.ShouldBeLessThan(two.TimeCalled); + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 7188, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + var dependency = new FakeDependency(); + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7188", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithHandlersRegisteredInDi(dependency)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => ThenTheDependencyIsCalled(dependency)) + .BDDfy(); } + private void ThenTheDependencyIsCalled(FakeDependency dependency) + { + dependency.Called.ShouldBeTrue(); + } + + private void ThenTheHandlersAreCalledCorrectly() + { + FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled); + } + + public class FakeDependency + { + public bool Called; + } + + class FakeHandlerWithDependency : DelegatingHandler + { + private FakeDependency _dependency; + + public FakeHandlerWithDependency(FakeDependency dependency) + { + _dependency = dependency; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + _dependency.Called = true; + return await base.SendAsync(request, cancellationToken); + } + } + class FakeHandler : DelegatingHandler { - public DateTime TimeCalled { get; private set; } + public static DateTime TimeCalled { get; private set; } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + TimeCalled = DateTime.Now; + return await base.SendAsync(request, cancellationToken); + } + } + class FakeHandlerTwo : DelegatingHandler + { + public static DateTime TimeCalled { get; private set; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 61a01059..0ea0e6b6 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -22,6 +22,9 @@ using Ocelot.Middleware; using Shouldly; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; using Ocelot.AcceptanceTests.Caching; +using System.IO.Compression; +using System.Text; +using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests; namespace Ocelot.AcceptanceTests { @@ -172,10 +175,9 @@ namespace Ocelot.AcceptanceTests _ocelotClient = _ocelotServer.CreateClient(); } - /// - /// 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. - /// - public void GivenOcelotIsRunningWithHandlers(DelegatingHandler handlerOne, DelegatingHandler handlerTwo) + public void GivenOcelotIsRunningWithHandlersRegisteredInDi() + where TOne : DelegatingHandler + where TWo : DelegatingHandler { _webHostBuilder = new WebHostBuilder(); @@ -193,8 +195,40 @@ namespace Ocelot.AcceptanceTests { s.AddSingleton(_webHostBuilder); s.AddOcelot() - .AddDelegatingHandler(() => handlerOne) - .AddDelegatingHandler(() => handlerTwo); + .AddDelegatingHandler() + .AddDelegatingHandler(); + }) + .Configure(a => + { + a.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningWithHandlersRegisteredInDi(FakeDependency dependency) + where TOne : DelegatingHandler + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddSingleton(_webHostBuilder); + s.AddSingleton(dependency); + s.AddOcelot() + .AddDelegatingHandler(); }) .Configure(a => { @@ -582,6 +616,22 @@ namespace Ocelot.AcceptanceTests _postContent = new StringContent(postcontent); } + public void GivenThePostHasGzipContent(object input) + { + string json = JsonConvert.SerializeObject(input); + byte[] jsonBytes = Encoding.UTF8.GetBytes(json); + MemoryStream ms = new MemoryStream(); + using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true)) + { + gzip.Write(jsonBytes, 0, jsonBytes.Length); + } + ms.Position = 0; + StreamContent content = new StreamContent(ms); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + content.Headers.ContentEncoding.Add("gzip"); + _postContent = content; + } + public void ThenTheResponseBodyShouldBe(string expectedBody) { _response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody); diff --git a/test/Ocelot.AcceptanceTests/Steps.cs.orig b/test/Ocelot.AcceptanceTests/Steps.cs.orig new file mode 100644 index 00000000..84c119ab --- /dev/null +++ b/test/Ocelot.AcceptanceTests/Steps.cs.orig @@ -0,0 +1,722 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using CacheManager.Core; +using IdentityServer4.AccessTokenValidation; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Shouldly; +using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; +using Ocelot.AcceptanceTests.Caching; +<<<<<<< HEAD +using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests; +||||||| merged common ancestors +======= +using System.IO.Compression; +using System.Text; +>>>>>>> develop + +namespace Ocelot.AcceptanceTests +{ + public class Steps : IDisposable + { + private TestServer _ocelotServer; + private HttpClient _ocelotClient; + private HttpResponseMessage _response; + private HttpContent _postContent; + private BearerToken _token; + public HttpClient OcelotClient => _ocelotClient; + public string RequestIdKey = "OcRequestId"; + private readonly Random _random; + private IWebHostBuilder _webHostBuilder; + + public Steps() + { + _random = new Random(); + } + + public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = TestConfiguration.ConfigurationPath; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + } + + public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration, string configurationPath) + { + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + } + + /// + /// 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. + /// + public void GivenOcelotIsRunning() + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl) + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot() + .AddOpenTracing(option => + { + //this is the url that the butterfly collector server is running on... + option.CollectorUrl = butterflyUrl; + option.Service = "Ocelot"; + }); + }) + .Configure(app => + { + app.Use(async (context, next) => + { + await next.Invoke(); + }); + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + +/* + public void GivenIHaveAddedXForwardedForHeader(string value) + { + _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Forwarded-For", value); + }*/ + + public void GivenOcelotIsRunningWithMiddleareBeforePipeline(Func callback) + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + }) + .Configure(app => + { + app.UseMiddleware(callback); + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningWithHandlersRegisteredInDi() + where TOne : DelegatingHandler + where TWo : DelegatingHandler + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddSingleton(_webHostBuilder); + s.AddOcelot() + .AddDelegatingHandler() + .AddDelegatingHandler(); + }) + .Configure(a => + { + a.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningWithHandlersRegisteredInDi(FakeDependency dependency) + where TOne : DelegatingHandler + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddSingleton(_webHostBuilder); + s.AddSingleton(dependency); + s.AddOcelot() + .AddDelegatingHandler(); + }) + .Configure(a => + { + a.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + /// + /// 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. + /// + public void GivenOcelotIsRunning(Action options, string authenticationProviderKey) + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + s.AddAuthentication() + .AddIdentityServerAuthentication(authenticationProviderKey, options); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void ThenTheResponseHeaderIs(string key, string value) + { + var header = _response.Headers.GetValues(key); + header.First().ShouldBe(value); + } + + public void GivenOcelotIsRunningUsingJsonSerializedCache() + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot() + .AddCacheManager((x) => + { + x.WithMicrosoftLogging(log => + { + log.AddConsole(LogLevel.Debug); + }) + .WithJsonSerializer() + .WithHandle(typeof(InMemoryJsonHandle<>)); + }); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningUsingConsulToStoreConfig() + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot().AddStoreOcelotConfigurationInConsul(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache() + { + _webHostBuilder = new WebHostBuilder(); + + _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(); + }) + .ConfigureServices(s => + { + s.AddOcelot() + .AddCacheManager((x) => + { + x.WithMicrosoftLogging(log => + { + log.AddConsole(LogLevel.Debug); + }) + .WithJsonSerializer() + .WithHandle(typeof(InMemoryJsonHandle<>)); + }) + .AddStoreOcelotConfigurationInConsul(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _ocelotServer = new TestServer(_webHostBuilder); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + internal void ThenTheResponseShouldBe(FileConfiguration expecteds) + { + var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); + + response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < response.ReRoutes.Count; i++) + { + for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) + { + var result = response.ReRoutes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); + response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); + response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate); + response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod); + } + } + + /// + /// 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. + /// + public void GivenOcelotIsRunning(OcelotPipelineConfiguration ocelotPipelineConfig) + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("configuration.json") + .AddEnvironmentVariables(); + + var configuration = builder.Build(); + _webHostBuilder = new WebHostBuilder(); + _webHostBuilder.ConfigureServices(s => + { + s.AddSingleton(_webHostBuilder); + }); + + _ocelotServer = new TestServer(_webHostBuilder + .UseConfiguration(configuration) + .ConfigureServices(s => + { + Action settings = (x) => + { + x.WithMicrosoftLogging(log => + { + log.AddConsole(LogLevel.Debug); + }) + .WithDictionaryHandle(); + }; + + s.AddOcelot(configuration); + }) + .ConfigureLogging(l => + { + l.AddConsole(); + l.AddDebug(); + }) + .Configure(a => + { + a.UseOcelot(ocelotPipelineConfig).Wait(); + })); + + _ocelotClient = _ocelotServer.CreateClient(); + } + + public void GivenIHaveAddedATokenToMyRequest() + { + _ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + public void GivenIHaveAToken(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + public void GivenIHaveATokenForApiReadOnlyScope(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api.readOnly"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + public void GivenIHaveATokenForApi2(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api2"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + public void GivenIHaveAnOcelotToken(string adminPath) + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("username", "admin"), + new KeyValuePair("password", "admin"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + var response = _ocelotClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + + public void VerifyIdentiryServerStarted(string url) + { + using (var httpClient = new HttpClient()) + { + var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; + response.EnsureSuccessStatusCode(); + } + } + + public void WhenIGetUrlOnTheApiGateway(string url) + { + _response = _ocelotClient.GetAsync(url).Result; + } + + public void GivenIAddAHeader(string key, string value) + { + _ocelotClient.DefaultRequestHeaders.Add(key, value); + } + + public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times) + { + var tasks = new Task[times]; + + for (int i = 0; i < times; i++) + { + var urlCopy = url; + tasks[i] = GetForServiceDiscoveryTest(urlCopy); + Thread.Sleep(_random.Next(40, 60)); + } + + Task.WaitAll(tasks); + } + + private async Task GetForServiceDiscoveryTest(string url) + { + var response = await _ocelotClient.GetAsync(url); + var content = await response.Content.ReadAsStringAsync(); + int count = int.Parse(content); + count.ShouldBeGreaterThan(0); + } + + public void WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit(string url, int times) + { + for (int i = 0; i < times; i++) + { + var clientId = "ocelotclient1"; + var request = new HttpRequestMessage(new HttpMethod("GET"), url); + request.Headers.Add("ClientId", clientId); + _response = _ocelotClient.SendAsync(request).Result; + } + } + + public void WhenIGetUrlOnTheApiGateway(string url, string requestId) + { + _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId); + + _response = _ocelotClient.GetAsync(url).Result; + } + + public void WhenIPostUrlOnTheApiGateway(string url) + { + _response = _ocelotClient.PostAsync(url, _postContent).Result; + } + + public void GivenThePostHasContent(string postcontent) + { + _postContent = new StringContent(postcontent); + } + + public void GivenThePostHasGzipContent(object input) + { + string json = JsonConvert.SerializeObject(input); + byte[] jsonBytes = Encoding.UTF8.GetBytes(json); + MemoryStream ms = new MemoryStream(); + using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true)) + { + gzip.Write(jsonBytes, 0, jsonBytes.Length); + } + ms.Position = 0; + StreamContent content = new StreamContent(ms); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + content.Headers.ContentEncoding.Add("gzip"); + _postContent = content; + } + + public void ThenTheResponseBodyShouldBe(string expectedBody) + { + _response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody); + } + + public void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) + { + _response.StatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode) + { + var responseStatusCode = (int)_response.StatusCode; + responseStatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void Dispose() + { + _ocelotClient?.Dispose(); + _ocelotServer?.Dispose(); + } + + public void ThenTheRequestIdIsReturned() + { + _response.Headers.GetValues(RequestIdKey).First().ShouldNotBeNullOrEmpty(); + } + + public void ThenTheRequestIdIsReturned(string expected) + { + _response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected); + } + + public void ThenTheContentLengthIs(int expected) + { + _response.Content.Headers.ContentLength.ShouldBe(expected); + } + + public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway() + { + int numberOfRequests = 100; + var aggregateUrl = "/"; + var aggregateExpected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}"; + var tomUrl = "/tom"; + var tomExpected = "{Hello from Tom}"; + var lauraUrl = "/laura"; + var lauraExpected = "{Hello from Laura}"; + var random = new Random(); + + var aggregateTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + aggregateTasks[i] = Fire(aggregateUrl, aggregateExpected, random); + } + + var tomTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + tomTasks[i] = Fire(tomUrl, tomExpected, random); + } + + var lauraTasks = new Task[numberOfRequests]; + + for (int i = 0; i < numberOfRequests; i++) + { + lauraTasks[i] = Fire(lauraUrl, lauraExpected, random); + } + + Task.WaitAll(lauraTasks); + Task.WaitAll(tomTasks); + Task.WaitAll(aggregateTasks); + } + + private async Task Fire(string url, string expectedBody, Random random) + { + var request = new HttpRequestMessage(new HttpMethod("GET"), url); + await Task.Delay(random.Next(0, 2)); + var response = await _ocelotClient.SendAsync(request); + var content = await response.Content.ReadAsStringAsync(); + content.ShouldBe(expectedBody); + } + } +} diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index ed0e2cb8..1e708c68 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -1,47 +1,41 @@ - - - - 0.0.0-dev - netcoreapp2.0 - 2.0.0 - true - Ocelot.ManualTest - Exe - Ocelot.ManualTest - osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 - ..\..\codeanalysis.ruleset - - - - - PreserveNewest - - - - - - PreserveNewest - - - - - - - - - - - - - - - - - - - - all - - - - + + + 0.0.0-dev + netcoreapp2.0 + 2.0.0 + true + Ocelot.ManualTest + Exe + Ocelot.ManualTest + osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64 + ..\..\codeanalysis.ruleset + + + + PreserveNewest + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + all + + + \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs index e5cb64ae..e287d5f4 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs @@ -6,6 +6,7 @@ namespace Ocelot.UnitTests.Authentication using System.Collections.Generic; using System.IO; using System.Text; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Moq; using Ocelot.Authentication.Middleware; @@ -44,10 +45,11 @@ namespace Ocelot.UnitTests.Authentication private void WhenICallTheMiddleware() { - _next = async (context) => { + _next = (context) => { byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); - MemoryStream stream = new MemoryStream(byteArray); + var stream = new MemoryStream(byteArray); context.HttpContext.Response.Body = stream; + return Task.CompletedTask; }; _middleware = new AuthenticationMiddleware(_next, _factory.Object); _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); @@ -55,10 +57,11 @@ namespace Ocelot.UnitTests.Authentication private void GivenTheTestServerPipelineIsConfigured() { - _next = async (context) => { + _next = (context) => { byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); - MemoryStream stream = new MemoryStream(byteArray); + var stream = new MemoryStream(byteArray); context.HttpContext.Response.Body = stream; + return Task.CompletedTask; }; } diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index 6f1d54f3..472c8643 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -4,18 +4,17 @@ namespace Ocelot.UnitTests.Authorization { using System.Collections.Generic; using System.Security.Claims; + using System.Threading.Tasks; using Moq; using Ocelot.Authorisation; using Ocelot.Authorisation.Middleware; using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Logging; using Ocelot.Responses; using TestStack.BDDfy; using Xunit; using Microsoft.AspNetCore.Http; - using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Configuration; public class AuthorisationMiddlewareTests @@ -36,9 +35,7 @@ namespace Ocelot.UnitTests.Authorization _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new AuthorisationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object); } diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs index c0a76b2a..210b1cb5 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareRealCacheTests.cs @@ -10,6 +10,7 @@ namespace Ocelot.UnitTests.Cache using Shouldly; using System.Collections.Generic; using System.Net.Http; + using System.Threading.Tasks; using Moq; using Ocelot.Cache; using Ocelot.Cache.Middleware; @@ -43,9 +44,7 @@ namespace Ocelot.UnitTests.Cache _cacheManager = new OcelotCacheManagerCache(cacheManagerOutputCache); _downstreamContext = new DownstreamContext(new DefaultHttpContext()); _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); - _next = async context => { - //do nothing.. - }; + _next = context => Task.CompletedTask; _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator); } diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index 31cc11a8..78911787 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -7,19 +7,16 @@ namespace Ocelot.UnitTests.Cache using System; using System.Collections.Generic; using System.Net.Http; - using Microsoft.AspNetCore.Builder; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Cache; using Ocelot.Cache.Middleware; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Logging; - using Ocelot.Responses; using TestStack.BDDfy; using Xunit; @@ -42,10 +39,7 @@ namespace Ocelot.UnitTests.Cache _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; - + _next = context => Task.CompletedTask; _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); } diff --git a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs index f6bf052c..02e9d8fc 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsBuilderMiddlewareTests.cs @@ -3,6 +3,7 @@ namespace Ocelot.UnitTests.Claims { using System.Collections.Generic; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Moq; using Ocelot.Claims; @@ -32,9 +33,7 @@ namespace Ocelot.UnitTests.Claims _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); } diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index c2f35990..4b4e328b 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -41,15 +41,13 @@ namespace Ocelot.UnitTests.DependencyInjection private Exception _ex; [Fact] - public void should_add_delegating_handlers() + public void should_add_delegating_handlers_with_di() { - 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()) + .When(x => AddDelegate()) + .And(x => AddDelegate()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) .BDDfy(); } @@ -164,15 +162,12 @@ namespace Ocelot.UnitTests.DependencyInjection path.Path.ShouldBe("/administration"); } - private void ThenTheProviderIsRegisteredAndReturnsHandlers() + private void ThenTheProviderIsRegisteredAndReturnsHandlers() { _serviceProvider = _services.BuildServiceProvider(); - var provider = _serviceProvider.GetService(); - var handlers = provider.Get(); - var handler = (FakeDelegatingHandler)handlers[0].Invoke(); - handler.Order.ShouldBe(0); - handler = (FakeDelegatingHandler)handlers[1].Invoke(); - handler.Order.ShouldBe(1); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].ShouldBeOfType(); + handlers[1].ShouldBeOfType(); } private void OnlyOneVersionOfEachCacheIsRegistered() @@ -213,9 +208,9 @@ namespace Ocelot.UnitTests.DependencyInjection } } - private void AddDelegate(DelegatingHandler handler) + private void AddDelegate() where T : DelegatingHandler { - _ocelotBuilder.AddDelegatingHandler(() => handler); + _ocelotBuilder.AddDelegatingHandler(); } private void ThenAnOcelotBuilderIsReturned() diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index a5c18c22..d3363b00 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -4,9 +4,8 @@ using Ocelot.Middleware.Multiplexer; namespace Ocelot.UnitTests.DownstreamRouteFinder { using System.Collections.Generic; - using Microsoft.AspNetCore.Builder; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -42,9 +41,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _multiplexer = new Mock(); _middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _provider.Object, _multiplexer.Object); } diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 0d678a7b..c226f245 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -6,23 +6,19 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator using System; using System.Collections.Generic; using System.Net.Http; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.DependencyInjection; + using System.Threading.Tasks; using Moq; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.DownstreamUrlCreator; using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; - using Ocelot.Infrastructure.RequestData; using Ocelot.Logging; using Ocelot.Responses; using Ocelot.Values; using TestStack.BDDfy; using Xunit; using Shouldly; - using Ocelot.DownstreamRouteFinder.Middleware; using Microsoft.AspNetCore.Http; public class DownstreamUrlCreatorMiddlewareTests @@ -43,9 +39,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _downstreamUrlTemplateVariableReplacer = new Mock(); _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; } [Fact] diff --git a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs index 0a69bf89..3085a64d 100644 --- a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs @@ -11,11 +11,12 @@ using Ocelot.Configuration.Builder; using Ocelot.Headers; using System.Net.Http; using Ocelot.Authorisation.Middleware; -using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Middleware; namespace Ocelot.UnitTests.Headers { + using System.Threading.Tasks; + public class HttpHeadersTransformationMiddlewareTests { private Mock _preReplacer; @@ -34,9 +35,7 @@ namespace Ocelot.UnitTests.Headers _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object); } diff --git a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs index 2f3a54d3..7f96b247 100644 --- a/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpRequestHeadersBuilderMiddlewareTests.cs @@ -4,14 +4,12 @@ namespace Ocelot.UnitTests.Headers { using System.Collections.Generic; using System.Net.Http; - using Microsoft.AspNetCore.Builder; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Headers; using Ocelot.Headers.Middleware; @@ -37,9 +35,7 @@ namespace Ocelot.UnitTests.Headers _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); _downstreamContext.DownstreamRequest = new HttpRequestMessage(); } diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs index e8808f17..1e30a0d1 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs @@ -19,12 +19,13 @@ namespace Ocelot.UnitTests.LoadBalancer private readonly LoadBalancerHouse _loadBalancerHouse; private Response _getResult; private readonly Mock _factory; - private ServiceProviderConfiguration _serviceProviderConfig; + private readonly ServiceProviderConfiguration _serviceProviderConfig; public LoadBalancerHouseTests() { _factory = new Mock(); _loadBalancerHouse = new LoadBalancerHouse(_factory.Object); + _serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123); } [Fact] diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 242463a2..e03fe6e1 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -4,6 +4,7 @@ namespace Ocelot.UnitTests.LoadBalancer { using System.Collections.Generic; using System.Net.Http; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Moq; using Ocelot.Configuration; @@ -43,9 +44,7 @@ namespace Ocelot.UnitTests.LoadBalancer _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _downstreamContext.DownstreamRequest = _downstreamRequest; } diff --git a/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs b/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs index adef00d5..1584c987 100644 --- a/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs +++ b/test/Ocelot.UnitTests/Middleware/MultiplexerTests.cs @@ -24,7 +24,7 @@ namespace Ocelot.UnitTests.Middleware { _aggregator = new Mock(); _context = new DownstreamContext(new DefaultHttpContext()); - _pipeline = async context => { _count++; }; + _pipeline = context => Task.FromResult(_count++); _multiplexer = new Multiplexer(_aggregator.Object); } diff --git a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs index cfc0395d..163a0411 100644 --- a/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/QueryStringBuilderMiddlewareTests.cs @@ -4,7 +4,6 @@ namespace Ocelot.UnitTests.QueryStrings { using System.Collections.Generic; using System.Net.Http; - using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -17,9 +16,8 @@ namespace Ocelot.UnitTests.QueryStrings using TestStack.BDDfy; using Xunit; using System.Security.Claims; - using Microsoft.AspNetCore.Builder; - using Ocelot.DownstreamRouteFinder.Middleware; using Microsoft.AspNetCore.Http; + using System.Threading.Tasks; public class QueryStringBuilderMiddlewareTests { @@ -36,9 +34,7 @@ namespace Ocelot.UnitTests.QueryStrings _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _addQueries = new Mock(); _downstreamContext.DownstreamRequest = new HttpRequestMessage(); _middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object); diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index 57d2fdc8..819e38dd 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -15,9 +15,9 @@ namespace Ocelot.UnitTests.RateLimit using Shouldly; using TestStack.BDDfy; using Xunit; - using Ocelot.DownstreamRouteFinder.Middleware; using Microsoft.Extensions.Caching.Memory; using System.IO; + using System.Threading.Tasks; public class ClientRateLimitMiddlewareTests { @@ -42,8 +42,7 @@ namespace Ocelot.UnitTests.RateLimit _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async (context) => { - }; + _next = context => Task.CompletedTask; _middleware = new ClientRateLimitMiddleware(_next, _loggerFactory.Object, _rateLimitCounterHandler); } diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index e30a772a..ba719a45 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -15,6 +15,7 @@ using System; using System.IO; using System.Text; + using System.Security.Cryptography; public class RequestMapperTests { @@ -118,19 +119,151 @@ } [Fact] - public void Should_map_content_type_header() + public void Should_handle_no_content() { + this.Given(_ => GivenTheInputRequestHasNoContent()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoContent()) + .BDDfy(); + } + + [Fact] + public void Should_map_content_headers() + { + byte[] md5bytes = new byte[0]; + using (var md5 = MD5.Create()) + { + md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5")); + } + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) .And(_ => GivenTheContentTypeIs("application/json")) + .And(_ => GivenTheContentEncodingIs("gzip, compress")) + .And(_ => GivenTheContentLanguageIs("english")) + .And(_ => GivenTheContentLocationIs("/my-receipts/38")) + .And(_ => GivenTheContentRangeIs("bytes 1-2/*")) + .And(_ => GivenTheContentDispositionIs("inline")) + .And(_ => GivenTheContentMD5Is(md5bytes)) .And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri()) .When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) + .And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress")) + .And(_ => ThenTheMappedRequestHasContentLanguageHeader("english")) + .And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38")) + .And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes)) + .And(_ => ThenTheMappedRequestHasContentRangeHeader()) + .And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline")) .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) + .And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders()) .BDDfy(); } + + [Fact] + public void should_not_add_content_headers() + { + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) + .And(_ => GivenTheContentTypeIs("application/json")) + .And(_ => GivenTheInputRequestHasMethod("POST")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) + .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) + .And(_ => ThenTheOtherContentTypeHeadersAreNotMapped()) + .BDDfy(); + } + + private void ThenTheContentHeadersAreNotAddedToNonContentHeaders() + { + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type"); + } + + private void ThenTheOtherContentTypeHeadersAreNotMapped() + { + _mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty(); + _mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty(); + _mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull(); + } + + private void ThenTheMappedRequestHasContentDispositionHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected); + } + + private void GivenTheContentDispositionIs(string input) + { + _inputRequest.Headers.Add("Content-Disposition", input); + } + + private void ThenTheMappedRequestHasContentMD5Header(byte[] expected) + { + _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected); + } + + private void GivenTheContentMD5Is(byte[] input) + { + var base64 = Convert.ToBase64String(input); + _inputRequest.Headers.Add("Content-MD5", base64); + } + + private void ThenTheMappedRequestHasContentRangeHeader() + { + _mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1); + _mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2); + } + + private void GivenTheContentRangeIs(string input) + { + _inputRequest.Headers.Add("Content-Range", input); + } + + private void ThenTheMappedRequestHasContentLocationHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected); + } + + private void GivenTheContentLocationIs(string input) + { + _inputRequest.Headers.Add("Content-Location", input); + } + + private void ThenTheMappedRequestHasContentLanguageHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected); + } + + private void GivenTheContentLanguageIs(string input) + { + _inputRequest.Headers.Add("Content-Language", input); + } + + private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo) + { + _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected); + _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo); + } + + private void GivenTheContentEncodingIs(string input) + { + _inputRequest.Headers.Add("Content-Encoding", input); + } + private void GivenTheContentTypeIs(string contentType) { _inputRequest.ContentType = contentType; @@ -146,18 +279,6 @@ _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected); } - [Fact] - public void Should_handle_no_content() - { - this.Given(_ => GivenTheInputRequestHasNoContent()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasNoContent()) - .BDDfy(); - } - private void GivenTheInputRequestHasMethod(string method) { _inputRequest.Method = method; diff --git a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs index f6bf4eb7..e27fc50f 100644 --- a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs @@ -9,6 +9,7 @@ namespace Ocelot.UnitTests.RequestId using System.Collections.Generic; using System.Linq; using System.Net.Http; + using System.Threading.Tasks; using Moq; using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; @@ -40,8 +41,10 @@ namespace Ocelot.UnitTests.RequestId _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { + _next = context => + { context.HttpContext.Response.Headers.Add("LSRequestId", context.HttpContext.TraceIdentifier); + return Task.CompletedTask; }; _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object); _downstreamContext.DownstreamRequest = _downstreamRequest; diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs deleted file mode 100644 index 8df95ec1..00000000 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerHouseTests.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Net.Http; -using Moq; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Errors; -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 _factory; - private readonly Mock _provider; - private DownstreamReRoute _request; - private Response _result; - - public DelegatingHandlerHandlerHouseTests() - { - _provider = new Mock(); - _factory = new Mock(); - _house = new DelegatingHandlerHandlerHouse(_factory.Object); - } - - [Fact] - public void should_create_and_store_provider() - { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - - this.Given(x => GivenTheRequest(reRoute)) - .And(x => GivenTheProviderReturns()) - .When(x => WhenIGet()) - .Then(x => ThenTheFactoryIsCalled(1)) - .And(x => ThenTheProviderIsNotNull()) - .BDDfy(); - } - - [Fact] - public void should_get_provider() - { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - - this.Given(x => GivenTheRequest(reRoute)) - .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 reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - - this.Given(x => GivenTheRequest(reRoute)) - .And(x => GivenTheProviderThrows()) - .When(x => WhenIGet()) - .And(x => ThenAnErrorIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_error_if_factory_errors() - { - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build(); - - this.Given(x => GivenTheRequest(reRoute)) - .And(x => GivenTheProviderReturnsError()) - .When(x => WhenIGet()) - .Then(x => ThenAnUnknownErrorIsReturned()) - .BDDfy(); - } - - private void ThenAnUnknownErrorIsReturned() - { - _result.IsError.ShouldBeTrue(); - } - - private void ThenAnErrorIsReturned() - { - _result.IsError.ShouldBeTrue(); - _result.Errors[0].ShouldBeOfType(); - } - - private void GivenTheProviderThrows() - { - _factory.Setup(x => x.Get(It.IsAny())).Throws(); - } - - private void GivenTheFactoryIsCleared() - { - _factory = new Mock(); - } - - private void ThenTheProviderIsNotNull() - { - _result.Data.ShouldBe(_provider.Object); - } - - private void WhenIGet() - { - _result = _house.Get(_request); - } - - private void GivenTheRequest(DownstreamReRoute request) - { - _request = request; - } - - private void GivenTheProviderReturns() - { - _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(_provider.Object)); - } - - private void GivenTheProviderReturnsError() - { - _factory.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(It.IsAny())); - } - - private void ThenTheFactoryIsCalled(int times) - { - _factory.Verify(x => x.Get(_request), Times.Exactly(times)); - } - } -} diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs index c63e04d2..793f3601 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; @@ -17,50 +18,35 @@ namespace Ocelot.UnitTests.Requester { public class DelegatingHandlerHandlerProviderFactoryTests { - private readonly DelegatingHandlerHandlerProviderFactory _factory; + private DelegatingHandlerHandlerFactory _factory; private Mock _loggerFactory; private DownstreamReRoute _request; - private Response _provider; - private readonly Mock _allRoutesProvider; + private Response>> _provider; private readonly Mock _qosProviderHouse; private readonly Mock _tracingFactory; + private IServiceProvider _serviceProvider; public DelegatingHandlerHandlerProviderFactoryTests() { _tracingFactory = new Mock(); _qosProviderHouse = new Mock(); - _allRoutesProvider = new Mock(); _loggerFactory = new Mock(); - _factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, _tracingFactory.Object, _qosProviderHouse.Object); - } - - private void GivenTheQosProviderHouseReturns(Response qosProvider) - { - _qosProviderHouse - .Setup(x => x.Get(It.IsAny())) - .Returns(qosProvider); } [Fact] public void should_all_from_all_routes_provider_and_qos() { - var handlers = new List> - { - () => new FakeDelegatingHandler(0), - () => new FakeDelegatingHandler(1) - }; - var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) - .And(x => GivenTheAllRoutesProviderReturns(handlers)) + .And(x => GivenTheServiceProviderReturns()) .When(x => WhenIGet()) .Then(x => ThenThereIsDelegatesInProvider(3)) .And(x => ThenTheDelegatesAreAddedCorrectly()) .And(x => ThenItIsPolly(2)) - .BDDfy(); + .BDDfy(); } [Fact] @@ -70,7 +56,7 @@ namespace Ocelot.UnitTests.Requester .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheAllRoutesProviderReturns()) + .And(x => GivenTheServiceProviderReturnsNothing()) .When(x => WhenIGet()) .Then(x => ThenNoDelegatesAreInTheProvider()) .BDDfy(); @@ -84,7 +70,7 @@ namespace Ocelot.UnitTests.Requester this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new OkResponse(It.IsAny()))) - .And(x => GivenTheAllRoutesProviderReturns()) + .And(x => GivenTheServiceProviderReturnsNothing()) .When(x => WhenIGet()) .Then(x => ThenThereIsDelegatesInProvider(1)) .And(x => ThenItIsPolly(0)) @@ -99,12 +85,28 @@ namespace Ocelot.UnitTests.Requester this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosProviderHouseReturns(new ErrorResponse(It.IsAny()))) - .And(x => GivenTheAllRoutesProviderReturns()) + .And(x => GivenTheServiceProviderReturnsNothing()) .When(x => WhenIGet()) .Then(x => ThenAnErrorIsReturned()) .BDDfy(); } + private void GivenTheServiceProviderReturns() + where TOne : DelegatingHandler + where TTwo : DelegatingHandler + { + IServiceCollection services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + _serviceProvider = services.BuildServiceProvider(); + } + + private void GivenTheServiceProviderReturnsNothing() + { + IServiceCollection services = new ServiceCollection(); + _serviceProvider = services.BuildServiceProvider(); + } + private void ThenAnErrorIsReturned() { _provider.IsError.ShouldBeTrue(); @@ -112,29 +114,27 @@ namespace Ocelot.UnitTests.Requester private void ThenTheDelegatesAreAddedCorrectly() { - var delegates = _provider.Data.Get(); + var delegates = _provider.Data; + var del = delegates[0].Invoke(); var handler = (FakeDelegatingHandler) del; - handler.Order.ShouldBe(0); + handler.Order.ShouldBe(1); del = delegates[1].Invoke(); - handler = (FakeDelegatingHandler)del; - handler.Order.ShouldBe(1); + var handlerTwo = (FakeDelegatingHandlerTwo) del; + handlerTwo.Order.ShouldBe(2); } - private void GivenTheAllRoutesProviderReturns() + private void GivenTheQosProviderHouseReturns(Response qosProvider) { - _allRoutesProvider.Setup(x => x.Get()).Returns(new List>()); - } - - private void GivenTheAllRoutesProviderReturns(List> handlers) - { - _allRoutesProvider.Setup(x => x.Get()).Returns(handlers); + _qosProviderHouse + .Setup(x => x.Get(It.IsAny())) + .Returns(qosProvider); } private void ThenItIsPolly(int i) { - var delegates = _provider.Data.Get(); + var delegates = _provider.Data; var del = delegates[i].Invoke(); del.ShouldBeOfType(); } @@ -142,7 +142,7 @@ namespace Ocelot.UnitTests.Requester private void ThenThereIsDelegatesInProvider(int count) { _provider.ShouldNotBeNull(); - _provider.Data.Get().Count.ShouldBe(count); + _provider.Data.Count.ShouldBe(count); } private void GivenTheFollowingRequest(DownstreamReRoute request) @@ -152,13 +152,14 @@ namespace Ocelot.UnitTests.Requester private void WhenIGet() { + _factory = new DelegatingHandlerHandlerFactory(_loggerFactory.Object, _tracingFactory.Object, _qosProviderHouse.Object, _serviceProvider); _provider = _factory.Get(_request); } private void ThenNoDelegatesAreInTheProvider() { _provider.ShouldNotBeNull(); - _provider.Data.Get().Count.ShouldBe(0); + _provider.Data.Count.ShouldBe(0); } } } diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderTests.cs deleted file mode 100644 index d93e291a..00000000 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -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> _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)); - } - } -} diff --git a/test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs b/test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs index a53487a3..df68978b 100644 --- a/test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs +++ b/test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs @@ -9,6 +9,7 @@ namespace Ocelot.UnitTests.Requester { public FakeDelegatingHandler() { + Order = 1; } public FakeDelegatingHandler(int order) @@ -16,6 +17,24 @@ namespace Ocelot.UnitTests.Requester Order = order; } + public int Order {get;private set;} + + public DateTime TimeCalled {get;private set;} + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + TimeCalled = DateTime.Now; + return Task.FromResult(new HttpResponseMessage()); + } + } + + public class FakeDelegatingHandlerTwo : DelegatingHandler + { + public FakeDelegatingHandlerTwo() + { + Order = 2; + } + public int Order {get;private set;} public DateTime TimeCalled {get;private set;} diff --git a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs index b788c826..c4ab1341 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs @@ -15,25 +15,22 @@ namespace Ocelot.UnitTests.Requester public class HttpClientBuilderTests { private readonly HttpClientBuilder _builder; - private readonly Mock _house; - private readonly Mock _provider; + private readonly Mock _factory; private IHttpClient _httpClient; private HttpResponseMessage _response; private DownstreamReRoute _request; public HttpClientBuilderTests() { - _provider = new Mock(); - _house = new Mock(); - _builder = new HttpClientBuilder(_house.Object); + _factory = new Mock(); + _builder = new HttpClientBuilder(_factory.Object); } [Fact] public void should_build_http_client() { - this.Given(x => GivenTheProviderReturns()) + this.Given(x => GivenTheFactoryReturns()) .And(x => GivenARequest()) - .And(x => GivenTheHouseReturns()) .When(x => WhenIBuild()) .Then(x => ThenTheHttpClientShouldNotBeNull()) .BDDfy(); @@ -51,9 +48,8 @@ namespace Ocelot.UnitTests.Requester () => fakeTwo }; - this.Given(x => GivenTheProviderReturns(handlers)) + this.Given(x => GivenTheFactoryReturns(handlers)) .And(x => GivenARequest()) - .And(x => GivenTheHouseReturns()) .And(x => WhenIBuild()) .When(x => WhenICallTheClient()) .Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo)) @@ -69,13 +65,6 @@ namespace Ocelot.UnitTests.Requester _request = reRoute; } - private void GivenTheHouseReturns() - { - _house - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse(_provider.Object)); - } - private void ThenSomethingIsReturned() { _response.ShouldNotBeNull(); @@ -91,18 +80,20 @@ namespace Ocelot.UnitTests.Requester fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled); } - private void GivenTheProviderReturns() + private void GivenTheFactoryReturns() { - _provider - .Setup(x => x.Get()) - .Returns(new List>(){ () => new FakeDelegatingHandler()}); + var handlers = new List>(){ () => new FakeDelegatingHandler()}; + + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse>>(handlers)); } - private void GivenTheProviderReturns(List> handlers) + private void GivenTheFactoryReturns(List> handlers) { - _provider - .Setup(x => x.Get()) - .Returns(handlers); + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse>>(handlers)); } private void WhenIBuild() diff --git a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs index 6b01a8c1..f2c1f3a0 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs @@ -18,8 +18,7 @@ namespace Ocelot.UnitTests.Requester public class HttpClientHttpRequesterTest { private readonly Mock _cacheHandlers; - private Mock _house; - private Mock _provider; + private Mock _house; private Response _response; private readonly HttpClientHttpRequester _httpClientRequester; private DownstreamContext _request; @@ -28,10 +27,8 @@ namespace Ocelot.UnitTests.Requester public HttpClientHttpRequesterTest() { - _provider = new Mock(); - _provider.Setup(x => x.Get()).Returns(new List>()); - _house = new Mock(); - _house.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(_provider.Object)); + _house = new Mock(); + _house.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(new List>())); _logger = new Mock(); _loggerFactory = new Mock(); _loggerFactory diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index 154beee8..f4facf5e 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -13,6 +13,7 @@ namespace Ocelot.UnitTests.Requester using TestStack.BDDfy; using Xunit; using Shouldly; + using System.Threading.Tasks; public class HttpRequesterMiddlewareTests { @@ -30,9 +31,7 @@ namespace Ocelot.UnitTests.Requester _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object); } diff --git a/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs b/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs index 54b795a2..2e9ffcd6 100644 --- a/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Responder/ResponderMiddlewareTests.cs @@ -5,6 +5,7 @@ namespace Ocelot.UnitTests.Responder { using Microsoft.AspNetCore.Http; using System.Net.Http; + using System.Threading.Tasks; using Moq; using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.Errors; @@ -32,9 +33,7 @@ namespace Ocelot.UnitTests.Responder _loggerFactory = new Mock(); _logger = new Mock(); _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = async context => { - //do nothing - }; + _next = context => Task.CompletedTask; _middleware = new ResponderMiddleware(_next, _responder.Object, _loggerFactory.Object, _codeMapper.Object); }