#298 initial hacking around better aggregation (#310)

* #298 initial hacking around better aggregation

* #298 bit more hacking around

* #298 abstraction over httpresponsemessage

* #298 tidying up

* #298 docs

* #298 missed this
This commit is contained in:
Tom Pallister
2018-04-12 17:35:04 +01:00
committed by GitHub
parent 982eebfc74
commit a15f75dda8
63 changed files with 1203 additions and 410 deletions

View File

@ -0,0 +1,86 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
namespace Ocelot.UnitTests.Middleware
{
public class DefinedAggregatorProviderTests
{
private ServiceLocatorDefinedAggregatorProvider _provider;
private Response<IDefinedAggregator> _aggregator;
private ReRoute _reRoute;
[Fact]
public void should_find_aggregator()
{
var reRoute = new ReRouteBuilder()
.WithAggregator("TestDefinedAggregator")
.Build();
this.Given(_ => GivenDefinedAggregator())
.And(_ => GivenReRoute(reRoute))
.When(_ => WhenIGet())
.Then(_ => ThenTheAggregatorIsReturned())
.BDDfy();
}
[Fact]
public void should_not_find_aggregator()
{
var reRoute = new ReRouteBuilder()
.WithAggregator("TestDefinedAggregator")
.Build();
this.Given(_ => GivenNoDefinedAggregator())
.And(_ => GivenReRoute(reRoute))
.When(_ => WhenIGet())
.Then(_ => ThenAnErrorIsReturned())
.BDDfy();
}
private void GivenDefinedAggregator()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IDefinedAggregator, TestDefinedAggregator>();
var services = serviceCollection.BuildServiceProvider();
_provider = new ServiceLocatorDefinedAggregatorProvider(services);
}
private void ThenTheAggregatorIsReturned()
{
_aggregator.Data.ShouldNotBeNull();
_aggregator.Data.ShouldBeOfType<TestDefinedAggregator>();
_aggregator.IsError.ShouldBeFalse();
}
private void GivenNoDefinedAggregator()
{
var serviceCollection = new ServiceCollection();
var services = serviceCollection.BuildServiceProvider();
_provider = new ServiceLocatorDefinedAggregatorProvider(services);
}
private void GivenReRoute(ReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenIGet()
{
_aggregator = _provider.Get(_reRoute);
}
private void ThenAnErrorIsReturned()
{
_aggregator.IsError.ShouldBeTrue();
_aggregator.Errors[0].Message.ShouldBe("Could not find Aggregator: TestDefinedAggregator");
_aggregator.Errors[0].ShouldBeOfType<CouldNotFindAggregatorError>();
}
}
}

View File

@ -19,13 +19,16 @@ namespace Ocelot.UnitTests.Middleware
private readonly OcelotRequestDelegate _pipeline;
private int _count;
private Mock<IResponseAggregator> _aggregator;
private Mock<IResponseAggregatorFactory> _factory;
public MultiplexerTests()
{
_factory = new Mock<IResponseAggregatorFactory>();
_aggregator = new Mock<IResponseAggregator>();
_context = new DownstreamContext(new DefaultHttpContext());
_pipeline = context => Task.FromResult(_count++);
_multiplexer = new Multiplexer(_aggregator.Object);
_factory.Setup(x => x.Get(It.IsAny<ReRoute>())).Returns(_aggregator.Object);
_multiplexer = new Multiplexer(_factory.Object);
}
[Fact]

View File

@ -62,7 +62,6 @@ namespace Ocelot.UnitTests.Middleware
{
_errors.Add(error);
}
}
public class FakeMiddleware : OcelotMiddleware
@ -72,4 +71,4 @@ namespace Ocelot.UnitTests.Middleware
{
}
}
}
}

View File

@ -0,0 +1,64 @@
namespace Ocelot.UnitTests.Middleware
{
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.Middleware.Multiplexer;
using Shouldly;
using Xunit;
using Ocelot.Configuration;
using TestStack.BDDfy;
public class ResponseAggregatorFactoryTests
{
private readonly InMemoryResponseAggregatorFactory _factory;
private Mock<IDefinedAggregatorProvider> _provider;
private ReRoute _reRoute;
private IResponseAggregator _aggregator;
public ResponseAggregatorFactoryTests()
{
_provider = new Mock<IDefinedAggregatorProvider>();
_factory = new InMemoryResponseAggregatorFactory(_provider.Object);
}
[Fact]
public void should_return_simple_json_aggregator()
{
var reRoute = new ReRouteBuilder()
.Build();
this.Given(_ => GivenReRoute(reRoute))
.When(_ => WhenIGet())
.Then(_ => ThenTheAggregatorIs<SimpleJsonResponseAggregator>())
.BDDfy();
}
[Fact]
public void should_return_user_defined_aggregator()
{
var reRoute = new ReRouteBuilder()
.WithAggregator("doesntmatter")
.Build();
this.Given(_ => GivenReRoute(reRoute))
.When(_ => WhenIGet())
.Then(_ => ThenTheAggregatorIs<UserDefinedResponseAggregator>())
.BDDfy();
}
private void GivenReRoute(ReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenIGet()
{
_aggregator = _factory.Get(_reRoute);
}
private void ThenTheAggregatorIs<T>()
{
_aggregator.ShouldBeOfType<T>();
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using Castle.Components.DictionaryAdapter;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
@ -29,40 +30,6 @@ namespace Ocelot.UnitTests.Middleware
_aggregator = new SimpleJsonResponseAggregator();
}
[Fact]
public void should_map_all_downstream_to_upstream_when_not_aggregate()
{
var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build();
var downstreamReRoutes = new List<DownstreamReRoute>
{
billDownstreamReRoute,
};
var reRoute = new ReRouteBuilder()
.WithDownstreamReRoutes(downstreamReRoutes)
.Build();
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse =
new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") },
DownstreamReRoute = billDownstreamReRoute,
Errors = new List<Error> { new AnyError() },
DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk"))),
};
var downstreamContexts = new List<DownstreamContext> { billDownstreamContext };
this.Given(x => GivenTheUpstreamContext(new DownstreamContext(new DefaultHttpContext())))
.And(x => GivenTheReRoute(reRoute))
.And(x => GivenTheDownstreamContext(downstreamContexts))
.When(x => WhenIAggregate())
.Then(x => ThenTheContentIs("Bill says hi"))
.And(x => ThenTheUpstreamContextIsMappedForNonAggregate())
.BDDfy();
}
[Fact]
public void should_aggregate_n_responses_and_set_response_content_on_upstream_context()
{
@ -82,15 +49,13 @@ namespace Ocelot.UnitTests.Middleware
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse =
new HttpResponseMessage(HttpStatusCode.OK) {Content = new StringContent("Bill says hi")},
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList<KeyValuePair<string, IEnumerable<string>>>()),
DownstreamReRoute = billDownstreamReRoute
};
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse =
new HttpResponseMessage(HttpStatusCode.OK) {Content = new StringContent("George says hi")},
DownstreamResponse = new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
DownstreamReRoute = georgeDownstreamReRoute
};
@ -126,19 +91,18 @@ namespace Ocelot.UnitTests.Middleware
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse =
new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") },
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
DownstreamReRoute = billDownstreamReRoute
};
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse =
new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Error") },
DownstreamResponse = new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
DownstreamReRoute = georgeDownstreamReRoute,
Errors = new List<Error>() { new AnyError() }
};
georgeDownstreamContext.Errors.Add(new AnyError());
var downstreamContexts = new List<DownstreamContext> { billDownstreamContext, georgeDownstreamContext };
var expected = "Error";

View File

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
using Ocelot.Responses;
using Ocelot.UnitTests.Responder;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Middleware
{
public class UserDefinedResponseAggregatorTests
{
private readonly UserDefinedResponseAggregator _aggregator;
private readonly Mock<IDefinedAggregatorProvider> _provider;
private ReRoute _reRoute;
private List<DownstreamContext> _contexts;
private DownstreamContext _context;
public UserDefinedResponseAggregatorTests()
{
_provider = new Mock<IDefinedAggregatorProvider>();
_aggregator = new UserDefinedResponseAggregator(_provider.Object);
}
[Fact]
public void should_call_aggregator()
{
var reRoute = new ReRouteBuilder().Build();
var context = new DownstreamContext(new DefaultHttpContext());
var contexts = new List<DownstreamContext>
{
new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
},
new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
}
};
this.Given(_ => GivenTheProviderReturnsAggregator())
.And(_ => GivenReRoute(reRoute))
.And(_ => GivenContexts(contexts))
.And(_ => GivenContext(context))
.When(_ => WhenIAggregate())
.Then(_ => ThenTheProviderIsCalled())
.And(_ => ThenTheContentIsCorrect())
.BDDfy();
}
[Fact]
public void should_not_find_aggregator()
{
var reRoute = new ReRouteBuilder().Build();
var context = new DownstreamContext(new DefaultHttpContext());
var contexts = new List<DownstreamContext>
{
new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
},
new DownstreamContext(new DefaultHttpContext())
{
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
}
};
this.Given(_ => GivenTheProviderReturnsError())
.And(_ => GivenReRoute(reRoute))
.And(_ => GivenContexts(contexts))
.And(_ => GivenContext(context))
.When(_ => WhenIAggregate())
.Then(_ => ThenTheProviderIsCalled())
.And(_ => ThenTheErrorIsReturned())
.BDDfy();
}
private void ThenTheErrorIsReturned()
{
_context.IsError.ShouldBeTrue();
_context.Errors.Count.ShouldBe(1);
}
private void GivenTheProviderReturnsError()
{
_provider.Setup(x => x.Get(It.IsAny<ReRoute>())).Returns(new ErrorResponse<IDefinedAggregator>(new AnyError()));
}
private async Task ThenTheContentIsCorrect()
{
var content = await _context.DownstreamResponse.Content.ReadAsStringAsync();
content.ShouldBe("Tom, Laura");
}
private void ThenTheProviderIsCalled()
{
_provider.Verify(x => x.Get(_reRoute), Times.Once);
}
private void GivenContext(DownstreamContext context)
{
_context = context;
}
private void GivenContexts(List<DownstreamContext> contexts)
{
_contexts = contexts;
}
private async Task WhenIAggregate()
{
await _aggregator.Aggregate(_reRoute, _context, _contexts);
}
private void GivenTheProviderReturnsAggregator()
{
var aggregator = new TestDefinedAggregator();
_provider.Setup(x => x.Get(It.IsAny<ReRoute>())).Returns(new OkResponse<IDefinedAggregator>(aggregator));
}
private void GivenReRoute(ReRoute reRoute)
{
_reRoute = reRoute;
}
public class TestDefinedAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<DownstreamResponse> responses)
{
var tom = await responses[0].Content.ReadAsStringAsync();
var laura = await responses[1].Content.ReadAsStringAsync();
var content = $"{tom}, {laura}";
var headers = responses.SelectMany(x => x.Headers).ToList();
return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers);
}
}
}
}