using System.Collections.Generic; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Ocelot.Configuration.File; using Ocelot.Configuration.Validator; using Ocelot.Responses; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.Configuration { public class ConfigurationValidationTests { private readonly IConfigurationValidator _configurationValidator; private FileConfiguration _fileConfiguration; private Response _result; private Mock _provider; public ConfigurationValidationTests() { _provider = new Mock(); _configurationValidator = new FileConfigurationValidator(_provider.Object); } [Fact] public void configuration_is_invalid_if_scheme_in_downstream_template() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}", UpstreamPathTemplate = "http://asdf.com" } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) .BDDfy(); } [Fact] public void configuration_is_valid_with_one_reroute() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/", UpstreamPathTemplate = "/asdf/" } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsValid()) .BDDfy(); } [Fact] public void configuration_is_invalid_without_slash_prefix_downstream_path_template() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "api/products/", UpstreamPathTemplate = "/asdf/" } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) .BDDfy(); } [Fact] public void configuration_is_invalid_without_slash_prefix_upstream_path_template() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/", UpstreamPathTemplate = "api/prod/", } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) .BDDfy(); } [Fact] public void configuration_is_valid_with_valid_authentication_provider() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/", UpstreamPathTemplate = "/asdf/", AuthenticationOptions = new FileAuthenticationOptions() { AuthenticationProviderKey = "Test" } } } })) .And(x => x.GivenTheAuthSchemeExists("Test")) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsValid()) .BDDfy(); } [Fact] public void configuration_is_invalid_with_invalid_authentication_provider() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/", UpstreamPathTemplate = "/asdf/", AuthenticationOptions = new FileAuthenticationOptions() { AuthenticationProviderKey = "Test" } } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) .And(x => x.ThenTheErrorIs()) .BDDfy(); } [Fact] public void configuration_is_not_valid_with_duplicate_reroutes() { this.Given(x => x.GivenAConfiguration(new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/", UpstreamPathTemplate = "/asdf/" }, new FileReRoute { DownstreamPathTemplate = "http://www.bbc.co.uk", UpstreamPathTemplate = "/asdf/" } } })) .When(x => x.WhenIValidateTheConfiguration()) .Then(x => x.ThenTheResultIsNotValid()) .And(x => x.ThenTheErrorIs()) .BDDfy(); } private void GivenAConfiguration(FileConfiguration fileConfiguration) { _fileConfiguration = fileConfiguration; } private void WhenIValidateTheConfiguration() { _result = _configurationValidator.IsValid(_fileConfiguration).Result; } private void ThenTheResultIsValid() { _result.Data.IsError.ShouldBeFalse(); } private void ThenTheResultIsNotValid() { _result.Data.IsError.ShouldBeTrue(); } private void ThenTheErrorIs() { _result.Data.Errors[0].ShouldBeOfType(); } private void GivenTheAuthSchemeExists(string name) { _provider.Setup(x => x.GetAllSchemesAsync()).ReturnsAsync(new List { new AuthenticationScheme(name, name, typeof(TestHandler)) }); } private class TestOptions : AuthenticationSchemeOptions { } private class TestHandler : AuthenticationHandler { public TestHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } protected override Task HandleAuthenticateAsync() { var principal = new ClaimsPrincipal(); return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); } } } }