Feature/transform headers (#204)

* New feature that lets a user do find and replace on an upstream header

* can transform downstream and upstream headers, not sure if interface is good

* can replace location header with placeholder

* added some syntax
This commit is contained in:
Tom Pallister
2018-01-22 20:21:29 +00:00
committed by GitHub
parent 9c048ba615
commit d0eee70c46
28 changed files with 1028 additions and 9 deletions

View File

@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class HeaderTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
public HeaderTests()
{
_steps = new Steps();
}
[Fact]
public void should_transform_upstream_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamHeaderTransform = new Dictionary<string,string>
{
{"Laz", "D, GP"}
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Laz"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Laz", "D"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("GP"))
.BDDfy();
}
[Fact]
public void should_transform_downstream_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"Location", "http://www.bbc.co.uk/, http://ocelot.com/"}
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Location", "http://www.bbc.co.uk/"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://ocelot.com/"))
.BDDfy();
}
[Fact]
public void should_fix_issue_190()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHost = "localhost",
DownstreamPort = 6773,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DownstreamHeaderTransform = new Dictionary<string,string>
{
{"Location", "http://localhost:6773, {BaseUrl}"}
},
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:6773", "/", 302, "Location", "http://localhost:6773/pay/Receive"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Redirect))
.And(x => _steps.ThenTheResponseHeaderIs("Location", "http://localhost:5000/pay/Receive"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(context.Request.Headers.TryGetValue(headerKey, out var values))
{
var result = values.First();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result);
}
});
})
.Build();
_builder.Start();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.OnStarting(() => {
context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
});
});
})
.Build();
_builder.Start();
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -108,6 +108,12 @@ namespace Ocelot.AcceptanceTests
_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();
@ -326,6 +332,11 @@ namespace Ocelot.AcceptanceTests
_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];

View File

@ -39,6 +39,7 @@ namespace Ocelot.UnitTests.Configuration
private Mock<IRegionCreator> _regionCreator;
private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
private Mock<IAdministrationPath> _adminPath;
private readonly Mock<IHeaderFindAndReplaceCreator> _headerFindAndReplaceCreator;
public FileConfigurationCreatorTests()
{
@ -56,6 +57,7 @@ namespace Ocelot.UnitTests.Configuration
_regionCreator = new Mock<IRegionCreator>();
_httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
_adminPath = new Mock<IAdministrationPath>();
_headerFindAndReplaceCreator = new Mock<IHeaderFindAndReplaceCreator>();
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object,
@ -71,7 +73,8 @@ namespace Ocelot.UnitTests.Configuration
_rateLimitOptions.Object,
_regionCreator.Object,
_httpHandlerOptionsCreator.Object,
_adminPath.Object);
_adminPath.Object,
_headerFindAndReplaceCreator.Object);
}
[Fact]
@ -91,6 +94,7 @@ namespace Ocelot.UnitTests.Configuration
}
}))
.And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig))
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly())
@ -121,10 +125,12 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingRegionIsReturned("region"))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region"))
.And(x => x.ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly())
.BDDfy();
}
@ -148,6 +154,7 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly())
@ -187,6 +194,7 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
.And(x => x.GivenTheQosOptionsCreatorReturns(expected))
.When(x => x.WhenICreateTheConfig())
@ -214,6 +222,7 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
@ -248,6 +257,7 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
@ -290,6 +300,7 @@ namespace Ocelot.UnitTests.Configuration
}
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
@ -325,6 +336,7 @@ namespace Ocelot.UnitTests.Configuration
}
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
@ -359,6 +371,7 @@ namespace Ocelot.UnitTests.Configuration
}
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$"))
.When(x => x.WhenICreateTheConfig())
@ -398,6 +411,7 @@ namespace Ocelot.UnitTests.Configuration
}
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheRequestIdCreatorReturns("blahhhh"))
.When(x => x.WhenICreateTheConfig())
@ -435,6 +449,7 @@ namespace Ocelot.UnitTests.Configuration
},
}))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions))
.When(x => x.WhenICreateTheConfig())
@ -470,6 +485,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
@ -504,6 +520,7 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.When(x => x.WhenICreateTheConfig())
@ -661,6 +678,17 @@ namespace Ocelot.UnitTests.Configuration
.Verify(x => x.Create(_fileConfiguration.GlobalConfiguration), Times.Once);
}
private void ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly()
{
_headerFindAndReplaceCreator
.Verify(x => x.Create(It.IsAny<FileReRoute>()), Times.Once);
}
private void GivenTheHeaderFindAndReplaceCreatorReturns()
{
_headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>()));
}
private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)
{
_serviceProviderConfigCreator

View File

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Middleware;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class HeaderFindAndReplaceCreatorTests
{
private HeaderFindAndReplaceCreator _creator;
private FileReRoute _reRoute;
private HeaderTransformations _result;
private Mock<IBaseUrlFinder> _finder;
public HeaderFindAndReplaceCreatorTests()
{
_finder = new Mock<IBaseUrlFinder>();
_creator = new HeaderFindAndReplaceCreator(_finder.Object);
}
[Fact]
public void should_create()
{
var reRoute = new FileReRoute
{
UpstreamHeaderTransform = new Dictionary<string, string>
{
{"Test", "Test, Chicken"},
{"Moop", "o, a"}
},
DownstreamHeaderTransform = new Dictionary<string, string>
{
{"Pop", "West, East"},
{"Bop", "e, r"}
}
};
var upstream = new List<HeaderFindAndReplace>
{
new HeaderFindAndReplace("Test", "Test", "Chicken", 0),
new HeaderFindAndReplace("Moop", "o", "a", 0)
};
var downstream = new List<HeaderFindAndReplace>
{
new HeaderFindAndReplace("Pop", "West", "East", 0),
new HeaderFindAndReplace("Bop", "e", "r", 0)
};
this.Given(x => GivenTheReRoute(reRoute))
.When(x => WhenICreate())
.Then(x => ThenTheFollowingUpstreamIsReturned(upstream))
.Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
.BDDfy();
}
[Fact]
public void should_use_base_url_placeholder()
{
var reRoute = new FileReRoute
{
DownstreamHeaderTransform = new Dictionary<string, string>
{
{"Location", "http://www.bbc.co.uk/, {BaseUrl}"},
}
};
var downstream = new List<HeaderFindAndReplace>
{
new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/", "http://ocelot.com/", 0),
};
this.Given(x => GivenTheReRoute(reRoute))
.And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
.When(x => WhenICreate())
.Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
.BDDfy();
}
[Fact]
public void should_use_base_url_partial_placeholder()
{
var reRoute = new FileReRoute
{
DownstreamHeaderTransform = new Dictionary<string, string>
{
{"Location", "http://www.bbc.co.uk/pay, {BaseUrl}pay"},
}
};
var downstream = new List<HeaderFindAndReplace>
{
new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/pay", "http://ocelot.com/pay", 0),
};
this.Given(x => GivenTheReRoute(reRoute))
.And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
.When(x => WhenICreate())
.Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
.BDDfy();
}
private void GivenTheBaseUrlIs(string baseUrl)
{
_finder.Setup(x => x.Find()).Returns(baseUrl);
}
private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream)
{
_result.Downstream.Count.ShouldBe(downstream.Count);
for (int i = 0; i < _result.Downstream.Count; i++)
{
var result = _result.Downstream[i];
var expected = downstream[i];
result.Find.ShouldBe(expected.Find);
result.Index.ShouldBe(expected.Index);
result.Key.ShouldBe(expected.Key);
result.Replace.ShouldBe(expected.Replace);
}
}
private void GivenTheReRoute(FileReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenICreate()
{
_result = _creator.Create(_reRoute);
}
private void ThenTheFollowingUpstreamIsReturned(List<HeaderFindAndReplace> expecteds)
{
_result.Upstream.Count.ShouldBe(expecteds.Count);
for (int i = 0; i < _result.Upstream.Count; i++)
{
var result = _result.Upstream[i];
var expected = expecteds[i];
result.Find.ShouldBe(expected.Find);
result.Index.ShouldBe(expected.Index);
result.Key.ShouldBe(expected.Key);
result.Replace.ShouldBe(expected.Replace);
}
}
}
}

View File

@ -0,0 +1,91 @@
using Xunit;
using Shouldly;
using Ocelot.Headers.Middleware;
using TestStack.BDDfy;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using Ocelot.Responses;
using Ocelot.Configuration;
using Ocelot.Headers;
namespace Ocelot.UnitTests.Headers
{
public class HttpContextRequestHeaderReplacerTests
{
private HttpContext _context;
private List<HeaderFindAndReplace> _fAndRs;
private HttpContextRequestHeaderReplacer _replacer;
private Response _result;
public HttpContextRequestHeaderReplacerTests()
{
_replacer = new HttpContextRequestHeaderReplacer();
}
[Fact]
public void should_replace_headers()
{
var context = new DefaultHttpContext();
context.Request.Headers.Add("test", "test");
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("test", "test", "chiken", 0));
this.Given(x => GivenTheFollowingHttpRequest(context))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeadersAreReplaced())
.BDDfy();
}
[Fact]
public void should_not_replace_headers()
{
var context = new DefaultHttpContext();
context.Request.Headers.Add("test", "test");
var fAndRs = new List<HeaderFindAndReplace>();
this.Given(x => GivenTheFollowingHttpRequest(context))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeadersAreNotReplaced())
.BDDfy();
}
private void ThenTheHeadersAreNotReplaced()
{
_result.ShouldBeOfType<OkResponse>();
foreach (var f in _fAndRs)
{
_context.Request.Headers.TryGetValue(f.Key, out var values);
values[f.Index].ShouldBe("test");
}
}
private void GivenTheFollowingHttpRequest(HttpContext context)
{
_context = context;
}
private void GivenTheFollowingHeaderReplacements(List<HeaderFindAndReplace> fAndRs)
{
_fAndRs = fAndRs;
}
private void WhenICallTheReplacer()
{
_result = _replacer.Replace(_context, _fAndRs);
}
private void ThenTheHeadersAreReplaced()
{
_result.ShouldBeOfType<OkResponse>();
foreach (var f in _fAndRs)
{
_context.Request.Headers.TryGetValue(f.Key, out var values);
values[f.Index].ShouldBe(f.Replace);
}
}
}
}

View File

@ -0,0 +1,93 @@
using Xunit;
using Shouldly;
using Ocelot.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Ocelot.Headers.Middleware;
using TestStack.BDDfy;
using System.Linq;
using System.Threading.Tasks;
using System;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Responses;
using Ocelot.Configuration.Builder;
using Ocelot.Headers;
using System.Net.Http;
namespace Ocelot.UnitTests.Headers
{
public class HttpHeadersTransformationMiddlewareTests : ServerHostedMiddlewareTest
{
private Mock<IHttpContextRequestHeaderReplacer> _preReplacer;
private Mock<IHttpResponseHeaderReplacer> _postReplacer;
public HttpHeadersTransformationMiddlewareTests()
{
_preReplacer = new Mock<IHttpContextRequestHeaderReplacer>();
_postReplacer = new Mock<IHttpResponseHeaderReplacer>();
GivenTheTestServerIsConfigured();
}
[Fact]
public void should_call_pre_and_post_header_transforms()
{
this.Given(x => GivenTheFollowingRequest())
.And(x => GivenTheReRouteHasPreFindAndReplaceSetUp())
.And(x => GivenTheHttpResponseMessageIs())
.When(x => WhenICallTheMiddleware())
.Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly())
.And(x => ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly())
.BDDfy();
}
private void GivenTheHttpResponseMessageIs()
{
var httpResponseMessage = new HttpResponseMessage();
var response = new OkResponse<HttpResponseMessage>(httpResponseMessage);
ScopedRepository.Setup(x => x.Get<HttpResponseMessage>("HttpResponseMessage")).Returns(response);
}
private void GivenTheReRouteHasPreFindAndReplaceSetUp()
{
var fAndRs = new List<HeaderFindAndReplace>();
var reRoute = new ReRouteBuilder().WithUpstreamHeaderFindAndReplace(fAndRs).WithDownstreamHeaderFindAndReplace(fAndRs).Build();
var dR = new DownstreamRoute(null, reRoute);
var response = new OkResponse<DownstreamRoute>(dR);
ScopedRepository.Setup(x => x.Get<DownstreamRoute>("DownstreamRoute")).Returns(response);
}
private void ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly()
{
_preReplacer.Verify(x => x.Replace(It.IsAny<HttpContext>(), It.IsAny<List<HeaderFindAndReplace>>()), Times.Once);
}
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
{
_postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>()), Times.Once);
}
private void GivenTheFollowingRequest()
{
Client.DefaultRequestHeaders.Add("test", "test");
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(ScopedRepository.Object);
services.AddSingleton(_preReplacer.Object);
services.AddSingleton(_postReplacer.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseHttpHeadersTransformationMiddleware();
}
}
}

View File

@ -0,0 +1,91 @@
using Xunit;
using Shouldly;
using TestStack.BDDfy;
using System.Net.Http;
using Ocelot.Headers;
using Ocelot.Configuration;
using System.Collections.Generic;
using Ocelot.Responses;
using System.Linq;
namespace Ocelot.UnitTests.Headers
{
public class HttpResponseHeaderReplacerTests
{
private HttpResponseMessage _response;
private HttpResponseHeaderReplacer _replacer;
private List<HeaderFindAndReplace> _headerFindAndReplaces;
private Response _result;
public HttpResponseHeaderReplacerTests()
{
_replacer = new HttpResponseHeaderReplacer();
}
[Fact]
public void should_replace_headers()
{
var response = new HttpResponseMessage();
response.Headers.Add("test", "test");
var fAndRs = new List<HeaderFindAndReplace>();
fAndRs.Add(new HeaderFindAndReplace("test", "test", "chiken", 0));
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeadersAreReplaced())
.BDDfy();
}
[Fact]
public void should_not_replace_headers()
{
var response = new HttpResponseMessage();
response.Headers.Add("test", "test");
var fAndRs = new List<HeaderFindAndReplace>();
this.Given(x => GivenTheHttpResponse(response))
.And(x => GivenTheFollowingHeaderReplacements(fAndRs))
.When(x => WhenICallTheReplacer())
.Then(x => ThenTheHeadersAreNotReplaced())
.BDDfy();
}
private void ThenTheHeadersAreNotReplaced()
{
_result.ShouldBeOfType<OkResponse>();
foreach (var f in _headerFindAndReplaces)
{
_response.Headers.TryGetValues(f.Key, out var values);
values.ToList()[f.Index].ShouldBe("test");
}
}
private void GivenTheFollowingHeaderReplacements(List<HeaderFindAndReplace> fAndRs)
{
_headerFindAndReplaces = fAndRs;
}
private void GivenTheHttpResponse(HttpResponseMessage response)
{
_response = response;
}
private void WhenICallTheReplacer()
{
_result = _replacer.Replace(_response, _headerFindAndReplaces);
}
private void ThenTheHeadersAreReplaced()
{
_result.ShouldBeOfType<OkResponse>();
foreach (var f in _headerFindAndReplaces)
{
_response.Headers.TryGetValues(f.Key, out var values);
values.ToList()[f.Index].ShouldBe(f.Replace);
}
}
}
}