Feature/adding some re route specific validation tests (#590)

* started refactoring json config validation stack because ive made it crap

* inject validators from DI rather than instanciating, next step mock them

* added some unit tests for specific validators on the train yey
This commit is contained in:
Tom Pallister
2018-09-04 22:27:54 +01:00
committed by GitHub
parent ef6db657b7
commit a5c1839ed7
15 changed files with 693 additions and 124 deletions

View File

@ -1,4 +1,4 @@
namespace Ocelot.UnitTests.Configuration
namespace Ocelot.UnitTests.Configuration.Validation
{
using System.Collections.Generic;
using System.Security.Claims;
@ -33,7 +33,8 @@
_authProvider = new Mock<IAuthenticationSchemeProvider>();
var provider = new ServiceCollection()
.BuildServiceProvider();
_configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
// Todo - replace with mocks
_configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
}
[Fact]
@ -766,10 +767,11 @@
.Then(x => x.ThenTheResultIsNotValid())
.Then(x => x.ThenTheErrorIs<FileValidationFailedError>())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} doesnt start with forward slash"))
.And(x => x.ThenTheErrorMessageAtPositionIs(1, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com doesnt start with forward slash"))
.And(x => x.ThenTheErrorMessageAtPositionIs(4, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme"))
.And(x => x.ThenTheErrorMessageAtPositionIs(1, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme"))
.And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.And(x => x.ThenTheErrorMessageAtPositionIs(4, "Upstream Path Template http://asdf.com doesnt start with forward slash"))
.And(x => x.ThenTheErrorMessageAtPositionIs(5, "Upstream Path Template http://asdf.com contains scheme"))
.BDDfy();
}
@ -947,7 +949,7 @@
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider"))
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "Authentication Options AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider"))
.BDDfy();
}
@ -1140,7 +1142,7 @@
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contains (s,m,h,d)"))
.And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"))
.BDDfy();
}
@ -1383,7 +1385,7 @@
QosDelegatingHandlerDelegate del = (a,b) => new FakeDelegatingHandler();
collection.AddSingleton<QosDelegatingHandlerDelegate>(del);
var provider = collection.BuildServiceProvider();
_configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
_configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
}
private void GivenAServiceDiscoveryHandler()
@ -1392,7 +1394,7 @@
ServiceDiscoveryFinderDelegate del = (a,b,c) => new FakeServiceDiscoveryProvider();
collection.AddSingleton<ServiceDiscoveryFinderDelegate>(del);
var provider = collection.BuildServiceProvider();
_configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
_configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
}
private class FakeServiceDiscoveryProvider : IServiceDiscoveryProvider

View File

@ -0,0 +1,106 @@
using System;
using FluentValidation.Results;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration.Validation
{
public class FileQoSOptionsFluentValidatorTests
{
private FileQoSOptionsFluentValidator _validator;
private ServiceCollection _services;
private ValidationResult _result;
private FileQoSOptions _qosOptions;
public FileQoSOptionsFluentValidatorTests()
{
_services = new ServiceCollection();
var provider = _services.BuildServiceProvider();
_validator = new FileQoSOptionsFluentValidator(provider);
}
[Fact]
public void should_be_valid_as_nothing_set()
{
this.Given(_ => GivenThe(new FileQoSOptions()))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void should_be_valid_as_qos_delegate_set()
{
var qosOptions = new FileQoSOptions
{
TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
};
this.Given(_ => GivenThe(qosOptions))
.And(_ => GivenAQosDelegate())
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void should_be_invalid_as_no_qos_delegate()
{
var qosOptions = new FileQoSOptions
{
TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
};
this.Given(_ => GivenThe(qosOptions))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInValid())
.And(_ => ThenTheErrorIs())
.BDDfy();
}
private void ThenTheErrorIs()
{
_result.Errors[0].ErrorMessage.ShouldBe("Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?");
}
private void ThenTheResultIsInValid()
{
_result.IsValid.ShouldBeFalse();
}
private void GivenAQosDelegate()
{
QosDelegatingHandlerDelegate fake = (a, b) =>
{
return null;
};
_services.AddSingleton<QosDelegatingHandlerDelegate>(fake);
var provider = _services.BuildServiceProvider();
_validator = new FileQoSOptionsFluentValidator(provider);
}
private void GivenThe(FileQoSOptions qosOptions)
{
_qosOptions = qosOptions;
}
private void WhenIValidate()
{
_result = _validator.Validate(_qosOptions);
}
private void ThenTheResultIsValid()
{
_result.IsValid.ShouldBeTrue();
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using FluentValidation.Results;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration.Validation
{
public class HostAndPortValidatorTests
{
private HostAndPortValidator _validator;
private ValidationResult _result;
private FileHostAndPort _hostAndPort;
public HostAndPortValidatorTests()
{
_validator = new HostAndPortValidator();
}
[Theory]
[InlineData(null)]
[InlineData("")]
public void should_be_invalid_because_host_empty(string host)
{
var fileHostAndPort = new FileHostAndPort
{
Host = host
};
this.Given(_ => GivenThe(fileHostAndPort))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInValid())
.And(_ => ThenTheErorrIs())
.BDDfy();
}
[Fact]
public void should_be_valid_because_host_set()
{
var fileHostAndPort = new FileHostAndPort
{
Host = "test"
};
this.Given(_ => GivenThe(fileHostAndPort))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
private void GivenThe(FileHostAndPort hostAndPort)
{
_hostAndPort = hostAndPort;
}
private void WhenIValidate()
{
_result = _validator.Validate(_hostAndPort);
}
private void ThenTheResultIsValid()
{
_result.IsValid.ShouldBeTrue();
}
private void ThenTheErorrIs()
{
_result.Errors[0].ErrorMessage.ShouldBe("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!");
}
private void ThenTheResultIsInValid()
{
_result.IsValid.ShouldBeFalse();
}
}
}

View File

@ -0,0 +1,368 @@
namespace Ocelot.UnitTests.Configuration.Validation
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Results;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Validator;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ReRouteFluentValidatorTests
{
private readonly ReRouteFluentValidator _validator;
private readonly Mock<IAuthenticationSchemeProvider> _authProvider;
private QosDelegatingHandlerDelegate _qosDelegatingHandler;
private Mock<IServiceProvider> _serviceProvider;
private FileReRoute _reRoute;
private ValidationResult _result;
public ReRouteFluentValidatorTests()
{
_authProvider = new Mock<IAuthenticationSchemeProvider>();
_serviceProvider = new Mock<IServiceProvider>();
// Todo - replace with mocks
_validator = new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(_serviceProvider.Object));
}
[Fact]
public void downstream_path_template_should_not_be_empty()
{
var fileReRoute = new FileReRoute();
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Downstream Path Template cannot be empty"))
.BDDfy();
}
[Fact]
public void upstream_path_template_should_not_be_empty()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "test"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Upstream Path Template cannot be empty"))
.BDDfy();
}
[Fact]
public void downstream_path_template_should_start_with_forward_slash()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "test"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Downstream Path Template test doesnt start with forward slash"))
.BDDfy();
}
[Fact]
public void downstream_path_template_should_not_contain_double_forward_slash()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "//test"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Downstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.BDDfy();
}
[Theory]
[InlineData("https://test")]
[InlineData("http://test")]
[InlineData("/test/http://")]
[InlineData("/test/https://")]
public void downstream_path_template_should_not_contain_scheme(string downstreamPathTemplate)
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = downstreamPathTemplate
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains($"Downstream Path Template {downstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.BDDfy();
}
[Fact]
public void upstream_path_template_should_start_with_forward_slash()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "test"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Upstream Path Template test doesnt start with forward slash"))
.BDDfy();
}
[Fact]
public void upstream_path_template_should_not_contain_double_forward_slash()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "//test"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("Upstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.BDDfy();
}
[Theory]
[InlineData("https://test")]
[InlineData("http://test")]
[InlineData("/test/http://")]
[InlineData("/test/https://")]
public void upstream_path_template_should_not_contain_scheme(string upstreamPathTemplate)
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = upstreamPathTemplate
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains($"Upstream Path Template {upstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
.BDDfy();
}
[Fact]
public void should_not_be_valid_if_enable_rate_limiting_true_and_period_is_empty()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
RateLimitOptions = new FileRateLimitRule
{
EnableRateLimiting = true
}
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("RateLimitOptions.Period is empty"))
.BDDfy();
}
[Fact]
public void should_not_be_valid_if_enable_rate_limiting_true_and_period_has_value()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
RateLimitOptions = new FileRateLimitRule
{
EnableRateLimiting = true,
Period = "test"
}
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"))
.BDDfy();
}
[Fact]
public void should_not_be_valid_if_specified_authentication_provider_isnt_registered()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
AuthenticationOptions = new FileAuthenticationOptions
{
AuthenticationProviderKey = "JwtLads"
}
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains($"Authentication Options AuthenticationProviderKey:JwtLads,AllowedScopes:[] is unsupported authentication provider"))
.BDDfy();
}
[Fact]
public void should_not_be_valid_if_not_using_service_discovery_and_no_host_and_ports()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsInvalid())
.And(_ => ThenTheErrorsContains("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!"))
.BDDfy();
}
[Fact]
public void should_be_valid_if_using_service_discovery_and_no_host_and_ports()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
ServiceName = "Lads"
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void should_be_valid_re_route_using_host_and_port_and_paths()
{
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 5000
}
}
};
this.Given(_ => GivenThe(fileReRoute))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void should_be_valid_if_specified_authentication_provider_is_registered()
{
const string key = "JwtLads";
var fileReRoute = new FileReRoute
{
DownstreamPathTemplate = "/test",
UpstreamPathTemplate = "/test",
AuthenticationOptions = new FileAuthenticationOptions
{
AuthenticationProviderKey = key
},
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 5000
}
}
};
this.Given(_ => GivenThe(fileReRoute))
.And(_ => GivenAnAuthProvider(key))
.When(_ => WhenIValidate())
.Then(_ => ThenTheResultIsValid())
.BDDfy();
}
private void GivenAnAuthProvider(string key)
{
var schemes = new List<AuthenticationScheme>
{
new AuthenticationScheme(key, key, typeof(FakeAutheHandler))
};
_authProvider
.Setup(x => x.GetAllSchemesAsync())
.ReturnsAsync(schemes);
}
private void ThenTheResultIsValid()
{
_result.IsValid.ShouldBeTrue();
}
private void GivenThe(FileReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenIValidate()
{
_result = _validator.Validate(_reRoute);
}
private void ThenTheResultIsInvalid()
{
_result.IsValid.ShouldBeFalse();
}
private void ThenTheErrorsContains(string expected)
{
_result.Errors.ShouldContain(x => x.ErrorMessage == expected);
}
class FakeAutheHandler : IAuthenticationHandler
{
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
throw new System.NotImplementedException();
}
public Task<AuthenticateResult> AuthenticateAsync()
{
throw new System.NotImplementedException();
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
throw new System.NotImplementedException();
}
public Task ForbidAsync(AuthenticationProperties properties)
{
throw new System.NotImplementedException();
}
}
}
}