mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-18 23:38:16 +08:00
Added ability to strip claims and forward to downstream service as headers
This commit is contained in:
@ -144,49 +144,6 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_and_foward_claim_as_header()
|
||||
{
|
||||
|
||||
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
|
||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
|
||||
.And(x => x.GivenIHaveAToken("http://localhost:51888"))
|
||||
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
|
||||
{
|
||||
ReRoutes = new List<YamlReRoute>
|
||||
{
|
||||
new YamlReRoute
|
||||
{
|
||||
DownstreamTemplate = "http://localhost:51876/",
|
||||
UpstreamTemplate = "/",
|
||||
UpstreamHttpMethod = "Get",
|
||||
AuthenticationOptions = new YamlAuthenticationOptions
|
||||
{
|
||||
AdditionalScopes = new List<string>(),
|
||||
Provider = "IdentityServer",
|
||||
ProviderRootUrl = "http://localhost:51888",
|
||||
RequireHttps = false,
|
||||
ScopeName = "api",
|
||||
ScopeSecret = "secret"
|
||||
},
|
||||
AddHeadersToRequest =
|
||||
{
|
||||
{ "CustomerId", "Claims[CustomerId] -> value" },
|
||||
{ "LocationId", "Claims[LocationId] -> value"},
|
||||
{ "UserId", "Claims[Subject] -> delimiter(|) -> value[0]" },
|
||||
{ "UserId", "Claims[Subject] -> delimiter(|) -> value[1]" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheApiGatewayIsRunning())
|
||||
.And(x => x.GivenIHaveAddedATokenToMyRequest())
|
||||
.When(x => x.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => x.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_201_using_identity_server_access_token()
|
||||
{
|
||||
|
291
test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs
Normal file
291
test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs
Normal file
@ -0,0 +1,291 @@
|
||||
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.Security.Claims;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services.InMemory;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Library.Configuration.Yaml;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class ClaimsToHeadersForwardingTests : IDisposable
|
||||
{
|
||||
private TestServer _ocelotServer;
|
||||
private HttpClient _ocelotClient;
|
||||
private HttpResponseMessage _response;
|
||||
private readonly string _configurationPath;
|
||||
private IWebHost _servicebuilder;
|
||||
|
||||
// Sadly we need to change this when we update the netcoreapp version to make the test update the config correctly
|
||||
private double _netCoreAppVersion = 1.4;
|
||||
private BearerToken _token;
|
||||
private IWebHost _identityServerBuilder;
|
||||
|
||||
public ClaimsToHeadersForwardingTests()
|
||||
{
|
||||
_configurationPath = $"./bin/Debug/netcoreapp{_netCoreAppVersion}/configuration.yaml";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_and_foward_claim_as_header()
|
||||
{
|
||||
var user = new InMemoryUser
|
||||
{
|
||||
Username = "test",
|
||||
Password = "test",
|
||||
Enabled = true,
|
||||
Subject = "registered|1231231",
|
||||
Claims = new List<Claim>
|
||||
{
|
||||
new Claim("CustomerId", "123"),
|
||||
new Claim("LocationId", "1")
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user))
|
||||
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200))
|
||||
.And(x => x.GivenIHaveAToken("http://localhost:52888"))
|
||||
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
|
||||
{
|
||||
ReRoutes = new List<YamlReRoute>
|
||||
{
|
||||
new YamlReRoute
|
||||
{
|
||||
DownstreamTemplate = "http://localhost:52876/",
|
||||
UpstreamTemplate = "/",
|
||||
UpstreamHttpMethod = "Get",
|
||||
AuthenticationOptions = new YamlAuthenticationOptions
|
||||
{
|
||||
AdditionalScopes = new List<string>
|
||||
{
|
||||
"openid", "offline_access"
|
||||
},
|
||||
Provider = "IdentityServer",
|
||||
ProviderRootUrl = "http://localhost:52888",
|
||||
RequireHttps = false,
|
||||
ScopeName = "api",
|
||||
ScopeSecret = "secret",
|
||||
},
|
||||
AddHeadersToRequest =
|
||||
{
|
||||
{"CustomerId", "Claims[CustomerId] > value"},
|
||||
{"LocationId", "Claims[LocationId] > value"},
|
||||
{"UserType", "Claims[sub] > value[0] > |"},
|
||||
{"UserId", "Claims[sub] > value[1] > |"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheApiGatewayIsRunning())
|
||||
.And(x => x.GivenIHaveAddedATokenToMyRequest())
|
||||
.When(x => x.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => x.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIGetUrlOnTheApiGateway(string url)
|
||||
{
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
}
|
||||
|
||||
private void ThenTheResponseBodyShouldBe(string expectedBody)
|
||||
{
|
||||
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is annoying cos it should be in the constructor but we need to set up the yaml file before calling startup so its a step.
|
||||
/// </summary>
|
||||
private void GivenTheApiGatewayIsRunning()
|
||||
{
|
||||
_ocelotServer = new TestServer(new WebHostBuilder()
|
||||
.UseStartup<Startup>());
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
private void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
|
||||
{
|
||||
var serializer = new Serializer();
|
||||
|
||||
if (File.Exists(_configurationPath))
|
||||
{
|
||||
File.Delete(_configurationPath);
|
||||
}
|
||||
|
||||
using (TextWriter writer = File.CreateText(_configurationPath))
|
||||
{
|
||||
serializer.Serialize(writer, yamlConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
|
||||
{
|
||||
_servicebuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
|
||||
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
|
||||
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
|
||||
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
|
||||
|
||||
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_servicebuilder.Start();
|
||||
}
|
||||
|
||||
private void GivenThereIsAnIdentityServerOn(string url, string scopeName, AccessTokenType tokenType, InMemoryUser user)
|
||||
{
|
||||
_identityServerBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddLogging();
|
||||
services.AddDeveloperIdentityServer()
|
||||
.AddInMemoryScopes(new List<Scope>
|
||||
{
|
||||
new Scope
|
||||
{
|
||||
Name = scopeName,
|
||||
Description = "My API",
|
||||
Enabled = true,
|
||||
AllowUnrestrictedIntrospection = true,
|
||||
ScopeSecrets = new List<Secret>()
|
||||
{
|
||||
new Secret
|
||||
{
|
||||
Value = "secret".Sha256()
|
||||
}
|
||||
},
|
||||
IncludeAllClaimsForUser = true
|
||||
},
|
||||
|
||||
StandardScopes.OpenId,
|
||||
StandardScopes.OfflineAccess
|
||||
})
|
||||
.AddInMemoryClients(new List<Client>
|
||||
{
|
||||
new Client
|
||||
{
|
||||
ClientId = "client",
|
||||
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
|
||||
AllowedScopes = new List<string> { scopeName, "openid", "offline_access" },
|
||||
AccessTokenType = tokenType,
|
||||
Enabled = true,
|
||||
RequireClientSecret = false
|
||||
}
|
||||
})
|
||||
.AddInMemoryUsers(new List<InMemoryUser>
|
||||
{
|
||||
user
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseIdentityServer();
|
||||
})
|
||||
.Build();
|
||||
|
||||
_identityServerBuilder.Start();
|
||||
|
||||
VerifyIdentiryServerStarted(url);
|
||||
|
||||
}
|
||||
|
||||
private void VerifyIdentiryServerStarted(string url)
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenIHaveAToken(string url)
|
||||
{
|
||||
var tokenUrl = $"{url}/connect/token";
|
||||
var formData = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("client_id", "client"),
|
||||
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||
new KeyValuePair<string, string>("scope", "api"),
|
||||
new KeyValuePair<string, string>("username", "test"),
|
||||
new KeyValuePair<string, string>("password", "test"),
|
||||
new KeyValuePair<string, string>("grant_type", "password")
|
||||
};
|
||||
var content = new FormUrlEncodedContent(formData);
|
||||
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var response = httpClient.PostAsync(tokenUrl, content).Result;
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenIHaveAddedATokenToMyRequest()
|
||||
{
|
||||
_ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||
}
|
||||
|
||||
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
||||
{
|
||||
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_servicebuilder?.Dispose();
|
||||
_ocelotClient?.Dispose();
|
||||
_ocelotServer?.Dispose();
|
||||
_identityServerBuilder?.Dispose();
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
class BearerToken
|
||||
{
|
||||
[JsonProperty("access_token")]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[JsonProperty("expires_in")]
|
||||
public int ExpiresIn { get; set; }
|
||||
|
||||
[JsonProperty("token_type")]
|
||||
public string TokenType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
123
test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
Normal file
123
test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Ocelot.Library.Configuration.Yaml;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class ReturnsErrorTests : IDisposable
|
||||
{
|
||||
private TestServer _ocelotServer;
|
||||
private HttpClient _ocelotClient;
|
||||
private HttpResponseMessage _response;
|
||||
private readonly string _configurationPath;
|
||||
private IWebHost _servicebuilder;
|
||||
|
||||
// Sadly we need to change this when we update the netcoreapp version to make the test update the config correctly
|
||||
private double _netCoreAppVersion = 1.4;
|
||||
|
||||
public ReturnsErrorTests()
|
||||
{
|
||||
_configurationPath = $"./bin/Debug/netcoreapp{_netCoreAppVersion}/configuration.yaml";
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_and_foward_claim_as_header()
|
||||
{
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:53876"))
|
||||
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
|
||||
{
|
||||
ReRoutes = new List<YamlReRoute>
|
||||
{
|
||||
new YamlReRoute
|
||||
{
|
||||
DownstreamTemplate = "http://localhost:53876/",
|
||||
UpstreamTemplate = "/",
|
||||
UpstreamHttpMethod = "Get",
|
||||
}
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheApiGatewayIsRunning())
|
||||
.When(x => x.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.InternalServerError))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIGetUrlOnTheApiGateway(string url)
|
||||
{
|
||||
_response = _ocelotClient.GetAsync(url).Result;
|
||||
}
|
||||
|
||||
private void ThenTheResponseBodyShouldBe(string expectedBody)
|
||||
{
|
||||
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is annoying cos it should be in the constructor but we need to set up the yaml file before calling startup so its a step.
|
||||
/// </summary>
|
||||
private void GivenTheApiGatewayIsRunning()
|
||||
{
|
||||
_ocelotServer = new TestServer(new WebHostBuilder()
|
||||
.UseStartup<Startup>());
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
|
||||
private void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
|
||||
{
|
||||
var serializer = new Serializer();
|
||||
|
||||
if (File.Exists(_configurationPath))
|
||||
{
|
||||
File.Delete(_configurationPath);
|
||||
}
|
||||
|
||||
using (TextWriter writer = File.CreateText(_configurationPath))
|
||||
{
|
||||
serializer.Serialize(writer, yamlConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url)
|
||||
{
|
||||
_servicebuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(context =>
|
||||
{
|
||||
throw new Exception("BLAMMMM");
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_servicebuilder.Start();
|
||||
}
|
||||
|
||||
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
||||
{
|
||||
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_servicebuilder?.Dispose();
|
||||
_ocelotClient?.Dispose();
|
||||
_ocelotServer?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@
|
||||
|
||||
"buildOptions": {
|
||||
"copyToOutput": {
|
||||
"include": [
|
||||
"configuration.yaml"
|
||||
]
|
||||
"include": [
|
||||
"configuration.yaml"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
@ -18,9 +20,13 @@ namespace Ocelot.UnitTests.Configuration
|
||||
private readonly Mock<IConfigurationValidator> _validator;
|
||||
private OcelotConfiguration _config;
|
||||
private YamlConfiguration _yamlConfiguration;
|
||||
private readonly Mock<IConfigurationHeaderExtrator> _configExtractor;
|
||||
private readonly Mock<ILogger<OcelotConfiguration>> _logger;
|
||||
|
||||
public OcelotConfigurationTests()
|
||||
{
|
||||
_logger = new Mock<ILogger<OcelotConfiguration>>();
|
||||
_configExtractor = new Mock<IConfigurationHeaderExtrator>();
|
||||
_validator = new Mock<IConfigurationValidator>();
|
||||
_yamlConfig = new Mock<IOptions<YamlConfiguration>>();
|
||||
}
|
||||
@ -54,6 +60,114 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_with_headers_to_extract()
|
||||
{
|
||||
var expected = new List<ReRoute>
|
||||
{
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamTemplate("/products/{productId}")
|
||||
.WithUpstreamTemplate("/api/products/{productId}")
|
||||
.WithUpstreamHttpMethod("Get")
|
||||
.WithUpstreamTemplatePattern("/api/products/.*$")
|
||||
.WithAuthenticationProvider("IdentityServer")
|
||||
.WithAuthenticationProviderUrl("http://localhost:51888")
|
||||
.WithRequireHttps(false)
|
||||
.WithScopeSecret("secret")
|
||||
.WithAuthenticationProviderScopeName("api")
|
||||
.WithConfigurationHeaderExtractorProperties(new List<ConfigurationHeaderExtractorProperties>
|
||||
{
|
||||
new ConfigurationHeaderExtractorProperties("CustomerId", "CustomerId", "", 0),
|
||||
})
|
||||
.Build()
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheYamlConfigIs(new YamlConfiguration
|
||||
{
|
||||
ReRoutes = new List<YamlReRoute>
|
||||
{
|
||||
new YamlReRoute
|
||||
{
|
||||
UpstreamTemplate = "/api/products/{productId}",
|
||||
DownstreamTemplate = "/products/{productId}",
|
||||
UpstreamHttpMethod = "Get",
|
||||
AuthenticationOptions = new YamlAuthenticationOptions
|
||||
{
|
||||
AdditionalScopes = new List<string>(),
|
||||
Provider = "IdentityServer",
|
||||
ProviderRootUrl = "http://localhost:51888",
|
||||
RequireHttps = false,
|
||||
ScopeName = "api",
|
||||
ScopeSecret = "secret"
|
||||
},
|
||||
AddHeadersToRequest =
|
||||
{
|
||||
{"CustomerId", "Claims[CustomerId] > value"},
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheYamlConfigIsValid())
|
||||
.And(x => x.GivenTheConfigHeaderExtractorReturns(new ConfigurationHeaderExtractorProperties("CustomerId", "CustomerId", "", 0)))
|
||||
.When(x => x.WhenIInstanciateTheOcelotConfig())
|
||||
.Then(x => x.ThenTheReRoutesAre(expected))
|
||||
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheConfigHeaderExtractorReturns(ConfigurationHeaderExtractorProperties expected)
|
||||
{
|
||||
_configExtractor
|
||||
.Setup(x => x.Extract(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns(new OkResponse<ConfigurationHeaderExtractorProperties>(expected));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_with_authentication_properties()
|
||||
{
|
||||
var expected = new List<ReRoute>
|
||||
{
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamTemplate("/products/{productId}")
|
||||
.WithUpstreamTemplate("/api/products/{productId}")
|
||||
.WithUpstreamHttpMethod("Get")
|
||||
.WithUpstreamTemplatePattern("/api/products/.*$")
|
||||
.WithAuthenticationProvider("IdentityServer")
|
||||
.WithAuthenticationProviderUrl("http://localhost:51888")
|
||||
.WithRequireHttps(false)
|
||||
.WithScopeSecret("secret")
|
||||
.WithAuthenticationProviderScopeName("api")
|
||||
.Build()
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenTheYamlConfigIs(new YamlConfiguration
|
||||
{
|
||||
ReRoutes = new List<YamlReRoute>
|
||||
{
|
||||
new YamlReRoute
|
||||
{
|
||||
UpstreamTemplate = "/api/products/{productId}",
|
||||
DownstreamTemplate = "/products/{productId}",
|
||||
UpstreamHttpMethod = "Get",
|
||||
AuthenticationOptions = new YamlAuthenticationOptions
|
||||
{
|
||||
AdditionalScopes = new List<string>(),
|
||||
Provider = "IdentityServer",
|
||||
ProviderRootUrl = "http://localhost:51888",
|
||||
RequireHttps = false,
|
||||
ScopeName = "api",
|
||||
ScopeSecret = "secret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
.And(x => x.GivenTheYamlConfigIsValid())
|
||||
.When(x => x.WhenIInstanciateTheOcelotConfig())
|
||||
.Then(x => x.ThenTheReRoutesAre(expected))
|
||||
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_template_pattern_that_matches_more_than_one_placeholder()
|
||||
{
|
||||
@ -158,7 +272,8 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void WhenIInstanciateTheOcelotConfig()
|
||||
{
|
||||
_config = new OcelotConfiguration(_yamlConfig.Object, _validator.Object);
|
||||
_config = new OcelotConfiguration(_yamlConfig.Object, _validator.Object,
|
||||
_configExtractor.Object, _logger.Object);
|
||||
}
|
||||
|
||||
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes)
|
||||
@ -174,5 +289,22 @@ namespace Ocelot.UnitTests.Configuration
|
||||
result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes)
|
||||
{
|
||||
for (int i = 0; i < _config.ReRoutes.Count; i++)
|
||||
{
|
||||
var result = _config.ReRoutes[i].AuthenticationOptions;
|
||||
var expected = expectedReRoutes[i].AuthenticationOptions;
|
||||
|
||||
result.AdditionalScopes.ShouldBe(expected.AdditionalScopes);
|
||||
result.Provider.ShouldBe(expected.Provider);
|
||||
result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
|
||||
result.RequireHttps.ShouldBe(expected.RequireHttps);
|
||||
result.ScopeName.ShouldBe(expected.ScopeName);
|
||||
result.ScopeSecret.ShouldBe(expected.ScopeSecret);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Library.Builder;
|
||||
using Ocelot.Library.DownstreamRouteFinder;
|
||||
using Ocelot.Library.Middleware;
|
||||
using Ocelot.Library.Repository;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Ocelot.Library.Responses;
|
||||
using Ocelot.Library.UrlMatcher;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Middleware
|
||||
{
|
||||
public class HttpRequestHeadersBuilderMiddlewareTests : IDisposable
|
||||
{
|
||||
private readonly Mock<IScopedRequestDataRepository> _scopedRepository;
|
||||
private readonly Mock<IAddHeadersToRequest> _addHeaders;
|
||||
private readonly string _url;
|
||||
private readonly TestServer _server;
|
||||
private readonly HttpClient _client;
|
||||
private Response<DownstreamRoute> _downstreamRoute;
|
||||
private HttpResponseMessage _result;
|
||||
|
||||
public HttpRequestHeadersBuilderMiddlewareTests()
|
||||
{
|
||||
_url = "http://localhost:51879";
|
||||
_scopedRepository = new Mock<IScopedRequestDataRepository>();
|
||||
_addHeaders = new Mock<IAddHeadersToRequest>();
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
x.AddSingleton(_addHeaders.Object);
|
||||
x.AddSingleton(_scopedRepository.Object);
|
||||
})
|
||||
.UseUrls(_url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(_url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHttpRequestHeadersBuilderMiddleware();
|
||||
});
|
||||
|
||||
_server = new TestServer(builder);
|
||||
_client = _server.CreateClient();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void happy_path()
|
||||
{
|
||||
var downstreamRoute = new DownstreamRoute(new List<TemplateVariableNameAndValue>(),
|
||||
new ReRouteBuilder()
|
||||
.WithDownstreamTemplate("any old string")
|
||||
.WithConfigurationHeaderExtractorProperties(new List<ConfigurationHeaderExtractorProperties>
|
||||
{
|
||||
new ConfigurationHeaderExtractorProperties("UserId", "Subject", "", 0)
|
||||
})
|
||||
.Build());
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||
.And(x => x.GivenTheAddHeadersToRequestReturns("123"))
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheAddHeadersToRequestIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheAddHeadersToRequestReturns(string claimValue)
|
||||
{
|
||||
_addHeaders
|
||||
.Setup(x => x.SetHeadersOnContext(It.IsAny<List<ConfigurationHeaderExtractorProperties>>(),
|
||||
It.IsAny<HttpContext>()))
|
||||
.Returns(new OkResponse());
|
||||
}
|
||||
|
||||
private void ThenTheAddHeadersToRequestIsCalledCorrectly()
|
||||
{
|
||||
_addHeaders
|
||||
.Verify(x => x.SetHeadersOnContext(It.IsAny<List<ConfigurationHeaderExtractorProperties>>(),
|
||||
It.IsAny<HttpContext>()), Times.Once);
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_result = _client.GetAsync(_url).Result;
|
||||
}
|
||||
|
||||
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
|
||||
{
|
||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||
_scopedRepository
|
||||
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
|
||||
.Returns(_downstreamRoute);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client.Dispose();
|
||||
_server.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
152
test/Ocelot.UnitTests/RequestBuilder/AddHeadersToRequestTests.cs
Normal file
152
test/Ocelot.UnitTests/RequestBuilder/AddHeadersToRequestTests.cs
Normal file
@ -0,0 +1,152 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Moq;
|
||||
using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Ocelot.Library.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.RequestBuilder
|
||||
{
|
||||
public class AddHeadersToRequestTests
|
||||
{
|
||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||
private readonly Mock<IClaimsParser> _parser;
|
||||
private List<ConfigurationHeaderExtractorProperties> _configuration;
|
||||
private HttpContext _context;
|
||||
private Response _result;
|
||||
private Response<string> _claimValue;
|
||||
|
||||
public AddHeadersToRequestTests()
|
||||
{
|
||||
_parser = new Mock<IClaimsParser>();
|
||||
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_headers_to_context()
|
||||
{
|
||||
var context = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
|
||||
{
|
||||
new Claim("test", "data")
|
||||
}))
|
||||
};
|
||||
|
||||
this.Given(
|
||||
x => x.GivenConfigurationHeaderExtractorProperties(new List<ConfigurationHeaderExtractorProperties>
|
||||
{
|
||||
new ConfigurationHeaderExtractorProperties("header-key", "", "", 0)
|
||||
}))
|
||||
.Given(x => x.GivenHttpContext(context))
|
||||
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
|
||||
.When(x => x.WhenIAddHeadersToTheRequest())
|
||||
.Then(x => x.ThenTheResultIsSuccess())
|
||||
.And(x => x.ThenTheHeaderIsAdded())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void if_header_exists_should_replace_it()
|
||||
{
|
||||
var context = new DefaultHttpContext
|
||||
{
|
||||
User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
|
||||
{
|
||||
new Claim("test", "data")
|
||||
})),
|
||||
};
|
||||
|
||||
context.Request.Headers.Add("header-key", new StringValues("initial"));
|
||||
|
||||
this.Given(
|
||||
x => x.GivenConfigurationHeaderExtractorProperties(new List<ConfigurationHeaderExtractorProperties>
|
||||
{
|
||||
new ConfigurationHeaderExtractorProperties("header-key", "", "", 0)
|
||||
}))
|
||||
.Given(x => x.GivenHttpContext(context))
|
||||
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
|
||||
.When(x => x.WhenIAddHeadersToTheRequest())
|
||||
.Then(x => x.ThenTheResultIsSuccess())
|
||||
.And(x => x.ThenTheHeaderIsAdded())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error()
|
||||
{
|
||||
this.Given(
|
||||
x => x.GivenConfigurationHeaderExtractorProperties(new List<ConfigurationHeaderExtractorProperties>
|
||||
{
|
||||
new ConfigurationHeaderExtractorProperties("", "", "", 0)
|
||||
}))
|
||||
.Given(x => x.GivenHttpContext(new DefaultHttpContext()))
|
||||
.And(x => x.GivenTheClaimParserReturns(new ErrorResponse<string>(new List<Error>
|
||||
{
|
||||
new AnyError()
|
||||
})))
|
||||
.When(x => x.WhenIAddHeadersToTheRequest())
|
||||
.Then(x => x.ThenTheResultIsError())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheHeaderIsAdded()
|
||||
{
|
||||
var header = _context.Request.Headers.First(x => x.Key == "header-key");
|
||||
header.Value.First().ShouldBe(_claimValue.Data);
|
||||
}
|
||||
|
||||
private void GivenConfigurationHeaderExtractorProperties(List<ConfigurationHeaderExtractorProperties> configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
private void GivenHttpContext(HttpContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private void GivenTheClaimParserReturns(Response<string> claimValue)
|
||||
{
|
||||
_claimValue = claimValue;
|
||||
_parser
|
||||
.Setup(
|
||||
x =>
|
||||
x.GetValue(It.IsAny<IEnumerable<Claim>>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>()))
|
||||
.Returns(_claimValue);
|
||||
}
|
||||
|
||||
private void WhenIAddHeadersToTheRequest()
|
||||
{
|
||||
_result = _addHeadersToRequest.SetHeadersOnContext(_configuration, _context);
|
||||
}
|
||||
|
||||
private void ThenTheResultIsSuccess()
|
||||
{
|
||||
_result.IsError.ShouldBe(false);
|
||||
}
|
||||
|
||||
private void ThenTheResultIsError()
|
||||
{
|
||||
|
||||
_result.IsError.ShouldBe(true);
|
||||
}
|
||||
|
||||
class AnyError : Error
|
||||
{
|
||||
public AnyError()
|
||||
: base("blahh", OcelotErrorCode.UnknownError)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
test/Ocelot.UnitTests/RequestBuilder/ClaimParserTests.cs
Normal file
123
test/Ocelot.UnitTests/RequestBuilder/ClaimParserTests.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Ocelot.Library.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.RequestBuilder
|
||||
{
|
||||
public class ClaimParserTests
|
||||
{
|
||||
private readonly IClaimsParser _claimsParser;
|
||||
private readonly List<Claim> _claims;
|
||||
private string _key;
|
||||
private Response<string> _result;
|
||||
private string _delimiter;
|
||||
private int _index;
|
||||
|
||||
public ClaimParserTests()
|
||||
{
|
||||
_claims = new List<Claim>();
|
||||
_claimsParser = new Library.RequestBuilder.ClaimsParser();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void can_parse_claims_dictionary_access_string_returning_value_to_function()
|
||||
{
|
||||
this.Given(x => x.GivenAClaimOf(new Claim("CustomerId", "1234")))
|
||||
.And(x => x.GivenTheKeyIs("CustomerId"))
|
||||
.When(x => x.WhenICallTheParser())
|
||||
.Then(x => x.ThenTheResultIs(new OkResponse<string>("1234")))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error_response_when_cannot_find_requested_claim()
|
||||
{
|
||||
this.Given(x => x.GivenAClaimOf(new Claim("BallsId", "1234")))
|
||||
.And(x => x.GivenTheKeyIs("CustomerId"))
|
||||
.When(x => x.WhenICallTheParser())
|
||||
.Then(x => x.ThenTheResultIs(new ErrorResponse<string>(new List<Error>
|
||||
{
|
||||
new CannotFindClaimError($"Cannot find claim for key: {_key}")
|
||||
})))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void can_parse_claims_dictionary_access_string_using_delimiter_and_retuning_at_correct_index()
|
||||
{
|
||||
this.Given(x => x.GivenAClaimOf(new Claim("Subject", "registered|4321")))
|
||||
.And(x => x.GivenTheDelimiterIs("|"))
|
||||
.And(x => x.GivenTheIndexIs(1))
|
||||
.And(x => x.GivenTheKeyIs("Subject"))
|
||||
.When(x => x.WhenICallTheParser())
|
||||
.Then(x => x.ThenTheResultIs(new OkResponse<string>("4321")))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error_response_if_index_too_large()
|
||||
{
|
||||
this.Given(x => x.GivenAClaimOf(new Claim("Subject", "registered|4321")))
|
||||
.And(x => x.GivenTheDelimiterIs("|"))
|
||||
.And(x => x.GivenTheIndexIs(24))
|
||||
.And(x => x.GivenTheKeyIs("Subject"))
|
||||
.When(x => x.WhenICallTheParser())
|
||||
.Then(x => x.ThenTheResultIs(new ErrorResponse<string>(new List<Error>
|
||||
{
|
||||
new CannotFindClaimError($"Cannot find claim for key: {_key}, delimiter: {_delimiter}, index: {_index}")
|
||||
})))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_error_response_if_index_too_small()
|
||||
{
|
||||
this.Given(x => x.GivenAClaimOf(new Claim("Subject", "registered|4321")))
|
||||
.And(x => x.GivenTheDelimiterIs("|"))
|
||||
.And(x => x.GivenTheIndexIs(-1))
|
||||
.And(x => x.GivenTheKeyIs("Subject"))
|
||||
.When(x => x.WhenICallTheParser())
|
||||
.Then(x => x.ThenTheResultIs(new ErrorResponse<string>(new List<Error>
|
||||
{
|
||||
new CannotFindClaimError($"Cannot find claim for key: {_key}, delimiter: {_delimiter}, index: {_index}")
|
||||
})))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheIndexIs(int index)
|
||||
{
|
||||
_index = index;
|
||||
}
|
||||
|
||||
private void GivenTheDelimiterIs(string delimiter)
|
||||
{
|
||||
_delimiter = delimiter;
|
||||
}
|
||||
|
||||
private void GivenAClaimOf(Claim claim)
|
||||
{
|
||||
_claims.Add(claim);
|
||||
}
|
||||
|
||||
private void GivenTheKeyIs(string key)
|
||||
{
|
||||
_key = key;
|
||||
}
|
||||
|
||||
private void WhenICallTheParser()
|
||||
{
|
||||
_result = _claimsParser.GetValue(_claims, _key, _delimiter, _index);
|
||||
}
|
||||
|
||||
private void ThenTheResultIs(Response<string> expected)
|
||||
{
|
||||
_result.Data.ShouldBe(expected.Data);
|
||||
_result.IsError.ShouldBe(expected.IsError);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Library.Errors;
|
||||
using Ocelot.Library.RequestBuilder;
|
||||
using Ocelot.Library.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.RequestBuilder
|
||||
{
|
||||
public class ConfigurationHeadersExtractorTests
|
||||
{
|
||||
private Dictionary<string, string> _dictionary;
|
||||
private readonly IConfigurationHeaderExtrator _configurationHeaderExtrator;
|
||||
private Response<ConfigurationHeaderExtractorProperties> _result;
|
||||
|
||||
public ConfigurationHeadersExtractorTests()
|
||||
{
|
||||
_configurationHeaderExtrator = new ConfigurationHeaderExtrator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void returns_no_instructions_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheDictionaryIs(new Dictionary<string, string>()
|
||||
{
|
||||
{"CustomerId", ""},
|
||||
}))
|
||||
.When(x => x.WhenICallTheExtractor())
|
||||
.Then(
|
||||
x =>
|
||||
x.ThenAnErrorIsReturned(new ErrorResponse<ConfigurationHeaderExtractorProperties>(
|
||||
new List<Error>
|
||||
{
|
||||
new NoInstructionsError(">")
|
||||
})))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void returns_no_instructions_not_for_claims_error()
|
||||
{
|
||||
this.Given(x => x.GivenTheDictionaryIs(new Dictionary<string, string>()
|
||||
{
|
||||
{"CustomerId", "Cheese[CustomerId] > value"},
|
||||
}))
|
||||
.When(x => x.WhenICallTheExtractor())
|
||||
.Then(
|
||||
x =>
|
||||
x.ThenAnErrorIsReturned(new ErrorResponse<ConfigurationHeaderExtractorProperties>(
|
||||
new List<Error>
|
||||
{
|
||||
new InstructionNotForClaimsError()
|
||||
})))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void can_parse_entry_to_work_out_properties_with_key()
|
||||
{
|
||||
this.Given(x => x.GivenTheDictionaryIs(new Dictionary<string, string>()
|
||||
{
|
||||
{"CustomerId", "Claims[CustomerId] > value"},
|
||||
}))
|
||||
.When(x => x.WhenICallTheExtractor())
|
||||
.Then(
|
||||
x =>
|
||||
x.ThenTheClaimParserPropertiesAreReturned(
|
||||
new OkResponse<ConfigurationHeaderExtractorProperties>(
|
||||
new ConfigurationHeaderExtractorProperties("CustomerId", "CustomerId", "", 0))))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void can_parse_entry_to_work_out_properties_with_key_delimiter_and_index()
|
||||
{
|
||||
this.Given(x => x.GivenTheDictionaryIs(new Dictionary<string, string>()
|
||||
{
|
||||
{"UserId", "Claims[Subject] > value[0] > |"},
|
||||
}))
|
||||
.When(x => x.WhenICallTheExtractor())
|
||||
.Then(
|
||||
x =>
|
||||
x.ThenTheClaimParserPropertiesAreReturned(
|
||||
new OkResponse<ConfigurationHeaderExtractorProperties>(
|
||||
new ConfigurationHeaderExtractorProperties("UserId", "Subject", "|", 0))))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsReturned(Response<ConfigurationHeaderExtractorProperties> expected)
|
||||
{
|
||||
_result.IsError.ShouldBe(expected.IsError);
|
||||
_result.Errors[0].ShouldBeOfType(expected.Errors[0].GetType());
|
||||
}
|
||||
|
||||
private void ThenTheClaimParserPropertiesAreReturned(Response<ConfigurationHeaderExtractorProperties> expected)
|
||||
{
|
||||
_result.Data.ClaimKey.ShouldBe(expected.Data.ClaimKey);
|
||||
_result.Data.Delimiter.ShouldBe(expected.Data.Delimiter);
|
||||
_result.Data.Index.ShouldBe(expected.Data.Index);
|
||||
_result.IsError.ShouldBe(expected.IsError);
|
||||
}
|
||||
|
||||
private void WhenICallTheExtractor()
|
||||
{
|
||||
var first = _dictionary.First();
|
||||
_result = _configurationHeaderExtrator.Extract(first.Key, first.Value);
|
||||
}
|
||||
|
||||
private void GivenTheDictionaryIs(Dictionary<string, string> dictionary)
|
||||
{
|
||||
_dictionary = dictionary;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user