using System.Collections.Generic; using Microsoft.Extensions.Options; using Moq; using Ocelot.Cache; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Creator; using Ocelot.Configuration.File; using Ocelot.Configuration.Validator; using Ocelot.Logging; using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.Configuration { using System; using Ocelot.DependencyInjection; using Ocelot.Errors; using Ocelot.UnitTests.TestData; using Ocelot.Values; public class FileConfigurationCreatorTests { private readonly Mock> _fileConfig; private readonly Mock _validator; private Response _config; private FileConfiguration _fileConfiguration; private readonly Mock _logger; private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator; private Mock _claimsToThingCreator; private Mock _authOptionsCreator; private Mock _upstreamTemplatePatternCreator; private Mock _requestIdKeyCreator; private Mock _serviceProviderConfigCreator; private Mock _qosOptionsCreator; private Mock _fileReRouteOptionsCreator; private Mock _rateLimitOptions; private Mock _regionCreator; private Mock _httpHandlerOptionsCreator; private Mock _adminPath; private readonly Mock _headerFindAndReplaceCreator; private readonly Mock _downstreamAddressesCreator; public FileConfigurationCreatorTests() { _logger = new Mock(); _validator = new Mock(); _fileConfig = new Mock>(); _claimsToThingCreator = new Mock(); _authOptionsCreator = new Mock(); _upstreamTemplatePatternCreator = new Mock(); _requestIdKeyCreator = new Mock(); _serviceProviderConfigCreator = new Mock(); _qosOptionsCreator = new Mock(); _fileReRouteOptionsCreator = new Mock(); _rateLimitOptions = new Mock(); _regionCreator = new Mock(); _httpHandlerOptionsCreator = new Mock(); _adminPath = new Mock(); _headerFindAndReplaceCreator = new Mock(); _downstreamAddressesCreator = new Mock(); _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _fileConfig.Object, _validator.Object, _logger.Object, _claimsToThingCreator.Object, _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, _rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.Object, _adminPath.Object, _headerFindAndReplaceCreator.Object, _downstreamAddressesCreator.Object); } [Fact] public void should_call_service_provider_config_creator() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { GlobalConfiguration = new FileGlobalConfiguration { ServiceDiscoveryProvider = new FileServiceDiscoveryProvider { Host = "localhost", Port = 8500, } } })) .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig)) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheConfigIsValid()) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_call_region_creator() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "127.0.0.1", } }, UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, FileCacheOptions = new FileCacheOptions { Region = "region" } } }, })) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => GivenTheDownstreamAddresses()) .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(); } [Fact] public void should_call_rate_limit_options_creator() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "127.0.0.1", } }, UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } }, })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_call_qos_options_creator() { var expected = new QoSOptionsBuilder() .WithDurationOfBreak(1) .WithExceptionsAllowedBeforeBreaking(1) .WithTimeoutValue(1) .Build(); var serviceOptions = new ReRouteOptionsBuilder() .WithIsQos(true) .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "127.0.0.1", } }, UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions { TimeoutValue = 1, DurationOfBreak = 1, ExceptionsAllowedBeforeBreaking = 1 } } }, })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) .And(x => x.GivenTheQosOptionsCreatorReturns(expected)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheQosOptionsAre(expected)) .BDDfy(); } [Fact] public void should_use_downstream_host() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "127.0.0.1", } }, UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } }, })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamAddresses(new List(){new DownstreamHostAndPort("127.0.0.1", 80) }) .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .Build() })) .BDDfy(); } [Fact] public void should_use_downstream_scheme() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamScheme = "https", UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } }, })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamScheme("https") .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .Build() })) .BDDfy(); } [Fact] public void should_use_service_discovery_for_downstream_service_host() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false, ServiceName = "ProductService" } }, GlobalConfiguration = new FileGlobalConfiguration { ServiceDiscoveryProvider = new FileServiceDiscoveryProvider { Host = "127.0.0.1" } } })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithUseServiceDiscovery(true) .WithServiceName("ProductService") .Build() })) .BDDfy(); } [Fact] public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false, } } })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithUseServiceDiscovery(false) .Build() })) .BDDfy(); } [Fact] public void should_call_template_pattern_creator_correctly() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = false } } })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$")) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("(?i)/api/products/.*/$", 1)) .Build() })) .BDDfy(); } [Fact] public void should_call_request_id_creator() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, ReRouteIsCaseSensitive = true } }, GlobalConfiguration = new FileGlobalConfiguration { RequestIdKey = "blahhhh" } })) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh")) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithRequestIdKey("blahhhh") .Build() })) .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_call_httpHandler_creator() { var reRouteOptions = new ReRouteOptionsBuilder() .Build(); var httpHandlerOptions = new HttpHandlerOptions(true, true); this.Given(x => x.GivenTheConfigIs(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "127.0.0.1", } }, UpstreamPathTemplate = "/api/products/{productId}", DownstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" } } }, })) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => GivenTheDownstreamAddresses()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()) .BDDfy(); } [Theory] [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] public void should_create_with_headers_to_extract(FileConfiguration fileConfig) { var reRouteOptions = new ReRouteOptionsBuilder() .WithIsAuthenticated(true) .Build(); var authenticationOptions = new AuthenticationOptionsBuilder() .WithAllowedScopes(new List()) .Build(); var expected = new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithAuthenticationOptions(authenticationOptions) .WithClaimsToHeaders(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0), }) .Build() }; this.Given(x => x.GivenTheConfigIs(fileConfig)) .And(x => GivenTheDownstreamAddresses()) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheClaimsToThingCreatorReturns(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .BDDfy(); } [Theory] [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] public void should_create_with_authentication_properties(FileConfiguration fileConfig) { var reRouteOptions = new ReRouteOptionsBuilder() .WithIsAuthenticated(true) .Build(); var authenticationOptions = new AuthenticationOptionsBuilder() .WithAllowedScopes(new List()) .Build(); var expected = new List { new ReRouteBuilder() .WithDownstreamPathTemplate("/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamHttpMethod(new List { "Get" }) .WithAuthenticationOptions(authenticationOptions) .Build() }; this.Given(x => x.GivenTheConfigIs(fileConfig)) .And(x => GivenTheDownstreamAddresses()) .And(x => x.GivenTheConfigIsValid()) .And(x => GivenTheHeaderFindAndReplaceCreatorReturns()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_return_validation_errors() { var errors = new List {new FileValidationFailedError("some message")}; this.Given(x => x.GivenTheConfigIs(new FileConfiguration())) .And(x => GivenTheDownstreamAddresses()) .And(x => x.GivenTheConfigIsInvalid(errors)) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheErrorsAreReturned(errors)) .BDDfy(); } private void GivenTheConfigIsInvalid(List errors) { _validator .Setup(x => x.IsValid(It.IsAny())) .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(true, errors))); } private void ThenTheErrorsAreReturned(List errors) { _config.IsError.ShouldBeTrue(); _config.Errors[0].ShouldBe(errors[0]); } private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions) { _fileReRouteOptionsCreator .Setup(x => x.Create(It.IsAny())) .Returns(fileReRouteOptions); } private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly() { _rateLimitOptions .Verify(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } private void GivenTheConfigIsValid() { _validator .Setup(x => x.IsValid(It.IsAny())) .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(false))); } private void GivenTheConfigIs(FileConfiguration fileConfiguration) { _fileConfiguration = fileConfiguration; _fileConfig .Setup(x => x.Value) .Returns(_fileConfiguration); } private void WhenICreateTheConfig() { _config = _ocelotConfigurationCreator.Create(_fileConfiguration).Result; } private void ThenTheReRoutesAre(List expectedReRoutes) { for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { var result = _config.Data.ReRoutes[i]; var expected = expectedReRoutes[i]; result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value); result.UpstreamTemplatePattern?.Template.ShouldBe(expected.UpstreamTemplatePattern?.Template); result.ClaimsToClaims.Count.ShouldBe(expected.ClaimsToClaims.Count); result.ClaimsToHeaders.Count.ShouldBe(expected.ClaimsToHeaders.Count); result.ClaimsToQueries.Count.ShouldBe(expected.ClaimsToQueries.Count); result.RequestIdKey.ShouldBe(expected.RequestIdKey); } } private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) { for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { var result = _config.Data.ReRoutes[i].AuthenticationOptions; var expected = expectedReRoutes[i].AuthenticationOptions; result.AllowedScopes.ShouldBe(expected.AllowedScopes); } } private void GivenTheClaimsToThingCreatorReturns(List claimsToThing) { _claimsToThingCreator .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].AddHeadersToRequest)) .Returns(claimsToThing); } private void GivenTheAuthOptionsCreatorReturns(AuthenticationOptions authOptions) { _authOptionsCreator .Setup(x => x.Create(It.IsAny())) .Returns(authOptions); } private void ThenTheAuthOptionsCreatorIsCalledCorrectly() { _authOptionsCreator .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); } private void GivenTheUpstreamTemplatePatternCreatorReturns(string pattern) { _upstreamTemplatePatternCreator .Setup(x => x.Create(It.IsAny())) .Returns(new UpstreamPathTemplate(pattern, 1)); } private void ThenTheRequestIdKeyCreatorIsCalledCorrectly() { _requestIdKeyCreator .Verify(x => x.Create(_fileConfiguration.ReRoutes[0], _fileConfiguration.GlobalConfiguration), Times.Once); } private void GivenTheRequestIdCreatorReturns(string requestId) { _requestIdKeyCreator .Setup(x => x.Create(It.IsAny(), It.IsAny())) .Returns(requestId); } private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) { _qosOptionsCreator .Setup(x => x.Create(_fileConfiguration.ReRoutes[0])) .Returns(qosOptions); } private void ThenTheQosOptionsAre(QoSOptions qosOptions) { _config.Data.ReRoutes[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); _config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); _config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); } private void ThenTheServiceProviderCreatorIsCalledCorrectly() { _serviceProviderConfigCreator .Verify(x => x.Create(_fileConfiguration.GlobalConfiguration), Times.Once); } private void ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly() { _headerFindAndReplaceCreator .Verify(x => x.Create(It.IsAny()), Times.Once); } private void GivenTheHeaderFindAndReplaceCreatorReturns() { _headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny())).Returns(new HeaderTransformations(new List(), new List())); } private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration) { _serviceProviderConfigCreator .Setup(x => x.Create(It.IsAny())).Returns(serviceProviderConfiguration); } private void GivenTheFollowingRegionIsReturned(string region) { _regionCreator .Setup(x => x.Create(It.IsAny())) .Returns(region); } private void ThenTheRegionCreatorIsCalledCorrectly(string expected) { _regionCreator .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); } private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions) { _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny())) .Returns(httpHandlerOptions); } private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly() { _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once()); } private void GivenTheDownstreamAddresses() { _downstreamAddressesCreator.Setup(x => x.Create(It.IsAny())).Returns(new List()); } } }