#568 worked out how to check if qos handler present and kill Ocelot i… (#578)

* #568 worked out how to check if qos handler present and kill Ocelot if options specified but no handler, need to refactor this into fluent validation style

* #568 acceptance tests to make sure Ocelot won't start if the user specifies QoSOptions but doesnt have a QoSHandler registered
This commit is contained in:
Tom Pallister 2018-08-28 18:57:21 +01:00 committed by GitHub
parent 8db5570840
commit 29a7af9486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1694 additions and 1377 deletions

View File

@ -1,24 +1,30 @@
using System.Collections.Generic;
using Ocelot.Errors;
namespace Ocelot.Configuration.Validator
{
public class ConfigurationValidationResult
{
public ConfigurationValidationResult(bool isError)
{
IsError = isError;
Errors = new List<Error>();
}
public ConfigurationValidationResult(bool isError, List<Error> errors)
{
IsError = isError;
Errors = errors;
}
public bool IsError { get; }
public List<Error> Errors { get; }
}
}
using System.Collections.Generic;
using Ocelot.Errors;
namespace Ocelot.Configuration.Validator
{
public class ConfigurationValidationResult
{
public ConfigurationValidationResult(bool isError)
{
IsError = isError;
Errors = new List<Error>();
}
public ConfigurationValidationResult(bool isError, Error error)
{
IsError = isError;
Errors = new List<Error> { error };
}
public ConfigurationValidationResult(bool isError, List<Error> errors)
{
IsError = isError;
Errors = errors;
}
public bool IsError { get; }
public List<Error> Errors { get; }
}
}

View File

@ -1,129 +1,133 @@
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File;
using Ocelot.Errors;
using Ocelot.Responses;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Configuration.Validator
{
using System;
using Microsoft.Extensions.DependencyInjection;
using Requester;
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
{
private readonly IServiceProvider _provider;
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
{
_provider = provider;
RuleFor(configuration => configuration.ReRoutes)
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, provider));
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
}
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
{
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
}
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
{
var validateResult = await ValidateAsync(configuration);
if (validateResult.IsValid)
{
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
}
var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList());
return new OkResponse<ConfigurationValidationResult>(result);
}
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List<FileReRoute> reRoutes)
{
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
}
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileReRoute> reRoutes)
{
var matchingReRoutes = reRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
.ToList();
if(matchingReRoutes.Count == 1)
{
return true;
}
var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0);
var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0);
var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs))
{
return false;
}
return true;
}
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes)
{
var duplicate = aggregateReRoutes
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& a.UpstreamHost == reRoute.UpstreamHost
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
return !duplicate;
}
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes)
{
var matchingReRoutes = aggregateReRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& r.UpstreamHost == reRoute.UpstreamHost)
.ToList();
return matchingReRoutes.Count <= 1;
}
}
}
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File;
using Ocelot.Errors;
using Ocelot.Responses;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Configuration.Validator
{
using System;
using Microsoft.Extensions.DependencyInjection;
using Requester;
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
{
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
{
_qosDelegatingHandlerDelegate = provider.GetService<QosDelegatingHandlerDelegate>();
RuleFor(configuration => configuration.ReRoutes)
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, _qosDelegatingHandlerDelegate));
RuleFor(configuration => configuration.GlobalConfiguration)
.SetValidator(new FileGlobalConfigurationFluentValidator(_qosDelegatingHandlerDelegate));
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
RuleForEach(configuration => configuration.ReRoutes)
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
RuleForEach(configuration => configuration.Aggregates)
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
}
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
{
var validateResult = await ValidateAsync(configuration);
if (validateResult.IsValid)
{
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
}
var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList());
return new OkResponse<ConfigurationValidationResult>(result);
}
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
{
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
}
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
List<FileReRoute> reRoutes)
{
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
}
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileReRoute> reRoutes)
{
var matchingReRoutes = reRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
.ToList();
if (matchingReRoutes.Count == 1)
{
return true;
}
var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0);
var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0);
var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs))
{
return false;
}
return true;
}
private static bool IsNotDuplicateIn(FileReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes)
{
var duplicate = aggregateReRoutes
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& a.UpstreamHost == reRoute.UpstreamHost
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
return !duplicate;
}
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
List<FileAggregateReRoute> aggregateReRoutes)
{
var matchingReRoutes = aggregateReRoutes
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
&& r.UpstreamHost == reRoute.UpstreamHost)
.ToList();
return matchingReRoutes.Count <= 1;
}
}
}

View File

@ -0,0 +1,21 @@
using FluentValidation;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Validator
{
using Requester;
public class FileGlobalConfigurationFluentValidator : AbstractValidator<FileGlobalConfiguration>
{
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileGlobalConfigurationFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
RuleFor(configuration => configuration.QoSOptions)
.SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
}
}
}

View File

@ -0,0 +1,28 @@
using FluentValidation;
using Ocelot.Configuration.File;
namespace Ocelot.Configuration.Validator
{
using Requester;
public class FileQoSOptionsFluentValidator : AbstractValidator<FileQoSOptions>
{
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public FileQoSOptionsFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => {
RuleFor(qosOptions => qosOptions)
.Must(HaveQosHandlerRegistered)
.WithMessage("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 bool HaveQosHandlerRegistered(FileQoSOptions arg)
{
return _qosDelegatingHandlerDelegate != null;
}
}
}

View File

@ -1,92 +1,95 @@
namespace Ocelot.Configuration.Validator
{
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System;
using Microsoft.Extensions.DependencyInjection;
using Requester;
public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
{
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly IServiceProvider _serviceProvider;
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider serviceProvider)
{
_authenticationSchemeProvider = authenticationSchemeProvider;
_serviceProvider = serviceProvider;
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => path.StartsWith("/"))
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => !path.Contains("//"))
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => !path.Contains("//"))
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => path.StartsWith("/"))
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.RateLimitOptions)
.Must(IsValidPeriod)
.WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
RuleFor(reRoute => reRoute.AuthenticationOptions)
.MustAsync(IsSupportedAuthenticationProviders)
.WithMessage("{PropertyValue} is unsupported authentication provider");
When(reRoute => reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.ServiceName).NotEmpty()
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
});
When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
});
When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
.SetCollectionValidator(new HostAndPortValidator());
});
}
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
{
return true;
}
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
}
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
{
string period = rateLimitOptions.Period;
return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
}
}
}
namespace Ocelot.Configuration.Validator
{
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration.File;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System;
using Microsoft.Extensions.DependencyInjection;
using Requester;
public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
{
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
{
_authenticationSchemeProvider = authenticationSchemeProvider;
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
RuleFor(reRoute => reRoute.QoSOptions)
.SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => path.StartsWith("/"))
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => !path.Contains("//"))
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => !path.Contains("//"))
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => path.StartsWith("/"))
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
RuleFor(reRoute => reRoute.RateLimitOptions)
.Must(IsValidPeriod)
.WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
RuleFor(reRoute => reRoute.AuthenticationOptions)
.MustAsync(IsSupportedAuthenticationProviders)
.WithMessage("{PropertyValue} is unsupported authentication provider");
When(reRoute => reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.ServiceName).NotEmpty()
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
});
When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
});
When(reRoute => !reRoute.UseServiceDiscovery, () => {
RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
.SetCollectionValidator(new HostAndPortValidator());
});
}
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
{
return true;
}
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
}
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
{
string period = rateLimitOptions.Period;
return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
}
}
}

View File

@ -1,53 +1,148 @@
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using Ocelot.Configuration.File;
using Shouldly;
using Xunit;
public class CannotStartOcelotTests : IDisposable
{
private readonly Steps _steps;
public CannotStartOcelotTests()
{
_steps = new Steps();
}
[Fact]
public void should_throw_exception_if_cannot_start()
{
var invalidConfig = new FileConfiguration()
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "api",
DownstreamPathTemplate = "test"
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch(Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)");
}
public void Dispose()
{
_steps.Dispose();
}
}
}
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using Ocelot.Configuration.File;
using Shouldly;
using Xunit;
public class CannotStartOcelotTests : IDisposable
{
private readonly Steps _steps;
public CannotStartOcelotTests()
{
_steps = new Steps();
}
[Fact]
public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_globally()
{
var invalidConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51878,
}
},
UpstreamPathTemplate = "/laura",
UpstreamHttpMethod = new List<string> { "Get" },
Key = "Laura",
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
QoSOptions = new FileQoSOptions
{
TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: 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()?)");
}
[Fact]
public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_for_re_route()
{
var invalidConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51878,
}
},
UpstreamPathTemplate = "/laura",
UpstreamHttpMethod = new List<string> { "Get" },
Key = "Laura",
QoSOptions = new FileQoSOptions
{
TimeoutValue = 1,
ExceptionsAllowedBeforeBreaking = 1
}
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: 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()?)");
}
[Fact]
public void should_throw_exception_if_cannot_start()
{
var invalidConfig = new FileConfiguration()
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "api",
DownstreamPathTemplate = "test"
}
}
};
Exception exception = null;
_steps.GivenThereIsAConfiguration(invalidConfig);
try
{
_steps.GivenOcelotIsRunning();
}
catch (Exception ex)
{
exception = ex;
}
exception.ShouldNotBeNull();
exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)");
}
public void Dispose()
{
_steps.Dispose();
}
}
}