Merge branch 'develop' into feature/config_grow_when_merged

# Conflicts:
#	src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs
#	test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs
This commit is contained in:
Sergey Borodachev
2018-08-18 05:11:35 +04:00
174 changed files with 5237 additions and 11121 deletions

View File

@ -1,91 +1,94 @@
using Ocelot.Configuration;
using Ocelot.Middleware;
namespace Ocelot.UnitTests.Authentication
{
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Authentication.Middleware;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class AuthenticationMiddlewareTests
{
private AuthenticationMiddleware _middleware;
private Mock<IOcelotLoggerFactory> _factory;
private Mock<IOcelotLogger> _logger;
private OcelotRequestDelegate _next;
private DownstreamContext _downstreamContext;
public AuthenticationMiddlewareTests()
{
_factory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_factory.Setup(x => x.CreateLogger<AuthenticationMiddleware>()).Returns(_logger.Object);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
}
[Fact]
public void should_call_next_middleware_if_route_is_not_authenticated()
{
this.Given(x => GivenTheDownStreamRouteIs(
new DownstreamReRouteBuilder().WithUpstreamHttpMethod(new List<string> { "Get" }).Build()))
.And(x => GivenTheTestServerPipelineIsConfigured())
.When(x => WhenICallTheMiddleware())
.Then(x => ThenTheUserIsAuthenticated())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
};
_middleware = new AuthenticationMiddleware(_next, _factory.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void GivenTheTestServerPipelineIsConfigured()
{
_next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
};
}
private void ThenTheUserIsAuthenticated()
{
var content = _downstreamContext.HttpContext.Response.Body.AsString();
content.ShouldBe("The user is authenticated");
}
private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute)
{
_downstreamContext.DownstreamReRoute = downstreamRoute;
}
}
public static class StreamExtensions
{
public static string AsString(this Stream stream)
{
using(var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
return text;
}
}
}
}
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.UnitTests.Authentication
{
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Authentication.Middleware;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Configuration;
using Ocelot.Middleware;
public class AuthenticationMiddlewareTests
{
private AuthenticationMiddleware _middleware;
private readonly Mock<IOcelotLoggerFactory> _factory;
private Mock<IOcelotLogger> _logger;
private OcelotRequestDelegate _next;
private readonly DownstreamContext _downstreamContext;
public AuthenticationMiddlewareTests()
{
_factory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_factory.Setup(x => x.CreateLogger<AuthenticationMiddleware>()).Returns(_logger.Object);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
}
[Fact]
public void should_call_next_middleware_if_route_is_not_authenticated()
{
this.Given(x => GivenTheDownStreamRouteIs(
new DownstreamReRouteBuilder().WithUpstreamHttpMethod(new List<string> { "Get" }).Build()))
.And(x => GivenTheTestServerPipelineIsConfigured())
.When(x => WhenICallTheMiddleware())
.Then(x => ThenTheUserIsAuthenticated())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
};
_middleware = new AuthenticationMiddleware(_next, _factory.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void GivenTheTestServerPipelineIsConfigured()
{
_next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
};
}
private void ThenTheUserIsAuthenticated()
{
var content = _downstreamContext.HttpContext.Response.Body.AsString();
content.ShouldBe("The user is authenticated");
}
private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute)
{
_downstreamContext.DownstreamReRoute = downstreamRoute;
}
}
public static class StreamExtensions
{
public static string AsString(this Stream stream)
{
using(var reader = new StreamReader(stream))
{
string text = reader.ReadToEnd();
return text;
}
}
}
}

View File

@ -1,103 +0,0 @@
using System;
using CacheManager.Core;
using Moq;
using Ocelot.Cache;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Cache
{
public class CacheManagerCacheTests
{
private OcelotCacheManagerCache<string> _ocelotOcelotCacheManager;
private Mock<ICacheManager<string>> _mockCacheManager;
private string _key;
private string _value;
private string _resultGet;
private TimeSpan _ttlSeconds;
private string _region;
public CacheManagerCacheTests()
{
_mockCacheManager = new Mock<ICacheManager<string>>();
_ocelotOcelotCacheManager = new OcelotCacheManagerCache<string>(_mockCacheManager.Object);
}
[Fact]
public void should_get_from_cache()
{
this.Given(x => x.GivenTheFollowingIsCached("someKey", "someRegion", "someValue"))
.When(x => x.WhenIGetFromTheCache())
.Then(x => x.ThenTheResultIs("someValue"))
.BDDfy();
}
[Fact]
public void should_add_to_cache()
{
this.When(x => x.WhenIAddToTheCache("someKey", "someValue", TimeSpan.FromSeconds(1)))
.Then(x => x.ThenTheCacheIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_delete_key_from_cache()
{
this.Given(_ => GivenTheFollowingRegion("fookey"))
.When(_ => WhenIDeleteTheRegion("fookey"))
.Then(_ => ThenTheRegionIsDeleted("fookey"))
.BDDfy();
}
private void WhenIDeleteTheRegion(string region)
{
_ocelotOcelotCacheManager.ClearRegion(region);
}
private void ThenTheRegionIsDeleted(string region)
{
_mockCacheManager
.Verify(x => x.ClearRegion(region), Times.Once);
}
private void GivenTheFollowingRegion(string key)
{
_ocelotOcelotCacheManager.Add(key, "doesnt matter", TimeSpan.FromSeconds(10), "region");
}
private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds)
{
_key = key;
_value = value;
_ttlSeconds = ttlSeconds;
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds, "region");
}
private void ThenTheCacheIsCalledCorrectly()
{
_mockCacheManager
.Verify(x => x.Add(It.IsAny<CacheItem<string>>()), Times.Once);
}
private void ThenTheResultIs(string expected)
{
_resultGet.ShouldBe(expected);
}
private void WhenIGetFromTheCache()
{
_resultGet = _ocelotOcelotCacheManager.Get(_key, _region);
}
private void GivenTheFollowingIsCached(string key, string region, string value)
{
_key = key;
_value = value;
_region = region;
_mockCacheManager
.Setup(x => x.Get<string>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(value);
}
}
}

View File

@ -1,95 +0,0 @@
namespace Ocelot.UnitTests.Cache
{
using System.Linq;
using System.Net;
using System.Net.Http.Headers;
using CacheManager.Core;
using Shouldly;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Moq;
using Ocelot.Cache;
using Ocelot.Cache.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.Middleware;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using Ocelot.Middleware.Multiplexer;
public class OutputCacheMiddlewareRealCacheTests
{
private readonly IOcelotCache<CachedResponse> _cacheManager;
private readonly OutputCacheMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private IRegionCreator _regionCreator;
private Mock<IOcelotLogger> _logger;
public OutputCacheMiddlewareRealCacheTests()
{
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object);
_regionCreator = new RegionCreator();
var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", x =>
{
x.WithDictionaryHandle();
});
_cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"));
_next = context => Task.CompletedTask;
_middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator);
}
[Fact]
public void should_cache_content_headers()
{
var content = new StringContent("{\"Test\": 1}")
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json")}
};
var response = new DownstreamResponse(content, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>());
this.Given(x => x.GivenResponseIsNotCached(response))
.And(x => x.GivenTheDownstreamRouteIs())
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheContentTypeHeaderIsCached())
.BDDfy();
}
private void WhenICallTheMiddleware()
{
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void ThenTheContentTypeHeaderIsCached()
{
var result = _cacheManager.Get("GET-https://some.url/blah?abcd=123", "kanken");
var header = result.ContentHeaders["Content-Type"];
header.First().ShouldBe("application/json");
}
private void GivenResponseIsNotCached(DownstreamResponse response)
{
_downstreamContext.DownstreamResponse = response;
}
private void GivenTheDownstreamRouteIs()
{
var reRoute = new DownstreamReRouteBuilder()
.WithIsCached(true)
.WithCacheOptions(new CacheOptions(100, "kanken"))
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build();
_downstreamContext.DownstreamReRoute = reRoute;
}
}
}

View File

@ -16,24 +16,22 @@
using TestStack.BDDfy;
using Xunit;
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Middleware.Multiplexer;
public class OutputCacheMiddlewareTests
{
private readonly Mock<IOcelotCache<CachedResponse>> _cacheManager;
private readonly Mock<IOcelotCache<CachedResponse>> _cache;
private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private OutputCacheMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private readonly OcelotRequestDelegate _next;
private CachedResponse _response;
private readonly IRegionCreator _regionCreator;
public OutputCacheMiddlewareTests()
{
_cacheManager = new Mock<IOcelotCache<CachedResponse>>();
_regionCreator = new RegionCreator();
_cache = new Mock<IOcelotCache<CachedResponse>>();
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
@ -91,14 +89,14 @@
private void WhenICallTheMiddleware()
{
_middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager.Object, _regionCreator);
_middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cache.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void GivenThereIsACachedResponse(CachedResponse response)
{
_response = response;
_cacheManager
_cache
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>()))
.Returns(_response);
}
@ -127,13 +125,13 @@
private void ThenTheCacheGetIsCalledCorrectly()
{
_cacheManager
_cache
.Verify(x => x.Get(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
private void ThenTheCacheAddIsCalledCorrectly()
{
_cacheManager
_cache
.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<CachedResponse>(), It.IsAny<TimeSpan>(), It.IsAny<string>()), Times.Once);
}
}

View File

@ -1,263 +0,0 @@
namespace Ocelot.UnitTests.Configuration
{
using Xunit;
using TestStack.BDDfy;
using Shouldly;
using Ocelot.Configuration.Repository;
using Moq;
using Ocelot.Infrastructure.Consul;
using Ocelot.Logging;
using Ocelot.Configuration.File;
using Ocelot.Cache;
using System;
using System.Collections.Generic;
using Ocelot.Responses;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.ServiceDiscovery.Configuration;
using Consul;
using Newtonsoft.Json;
using System.Text;
using System.Threading;
using System.Linq;
public class ConsulFileConfigurationRepositoryTests
{
private ConsulFileConfigurationRepository _repo;
private Mock<IOcelotCache<FileConfiguration>> _cache;
private Mock<IInternalConfigurationRepository> _internalRepo;
private Mock<IConsulClientFactory> _factory;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IConsulClient> _client;
private Mock<IKVEndpoint> _kvEndpoint;
private FileConfiguration _fileConfiguration;
private Response _setResult;
private Response<FileConfiguration> _getResult;
public ConsulFileConfigurationRepositoryTests()
{
_cache = new Mock<IOcelotCache<FileConfiguration>>();
_internalRepo = new Mock<IInternalConfigurationRepository>();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_factory = new Mock<IConsulClientFactory>();
_client = new Mock<IConsulClient>();
_kvEndpoint = new Mock<IKVEndpoint>();
_client
.Setup(x => x.KV)
.Returns(_kvEndpoint.Object);
_factory
.Setup(x => x.Get(It.IsAny<ConsulRegistryConfiguration>()))
.Returns(_client.Object);
_internalRepo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "", new ServiceProviderConfigurationBuilder().Build(), "", It.IsAny<LoadBalancerOptions>(), It.IsAny<string>(), It.IsAny<QoSOptions>(), It.IsAny<HttpHandlerOptions>())));
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
}
[Fact]
public void should_set_config()
{
var config = FakeFileConfiguration();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenWritingToConsulSucceeds())
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.BDDfy();
}
[Fact]
public void should_get_config()
{
var config = FakeFileConfiguration();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenFetchFromConsulSucceeds())
.When(_ => WhenIGetTheConfiguration())
.Then(_ => ThenTheConfigurationIs(config))
.BDDfy();
}
[Fact]
public void should_get_null_config()
{
this.Given(_ => GivenFetchFromConsulReturnsNull())
.When(_ => WhenIGetTheConfiguration())
.Then(_ => ThenTheConfigurationIsNull())
.BDDfy();
}
[Fact]
public void should_get_config_from_cache()
{
var config = FakeFileConfiguration();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenFetchFromCacheSucceeds())
.When(_ => WhenIGetTheConfiguration())
.Then(_ => ThenTheConfigurationIs(config))
.BDDfy();
}
[Fact]
public void should_set_config_key()
{
var config = FakeFileConfiguration();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenTheConfigKeyComesFromFileConfig("Tom"))
.And(_ => GivenFetchFromConsulSucceeds())
.When(_ => WhenIGetTheConfiguration())
.And(_ => ThenTheConfigKeyIs("Tom"))
.BDDfy();
}
[Fact]
public void should_set_default_config_key()
{
var config = FakeFileConfiguration();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenFetchFromConsulSucceeds())
.When(_ => WhenIGetTheConfiguration())
.And(_ => ThenTheConfigKeyIs("InternalConfiguration"))
.BDDfy();
}
private void ThenTheConfigKeyIs(string expected)
{
_kvEndpoint
.Verify(x => x.Get(expected, It.IsAny<CancellationToken>()), Times.Once);
}
private void GivenTheConfigKeyComesFromFileConfig(string key)
{
_internalRepo
.Setup(x => x.Get())
.Returns(new OkResponse<IInternalConfiguration>(new InternalConfiguration(new List<ReRoute>(), "",
new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), "",
new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(),
new HttpHandlerOptionsBuilder().Build())));
_repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object);
}
private void ThenTheConfigurationIsNull()
{
_getResult.Data.ShouldBeNull();
}
private void ThenTheConfigurationIs(FileConfiguration config)
{
var expected = JsonConvert.SerializeObject(config, Formatting.Indented);
var result = JsonConvert.SerializeObject(_getResult.Data, Formatting.Indented);
result.ShouldBe(expected);
}
private async Task WhenIGetTheConfiguration()
{
_getResult = await _repo.Get();
}
private void GivenWritingToConsulSucceeds()
{
var response = new WriteResult<bool>();
response.Response = true;
_kvEndpoint
.Setup(x => x.Put(It.IsAny<KVPair>(), It.IsAny<CancellationToken>())).ReturnsAsync(response);
}
private void GivenFetchFromCacheSucceeds()
{
_cache.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>())).Returns(_fileConfiguration);
}
private void GivenFetchFromConsulReturnsNull()
{
QueryResult<KVPair> result = new QueryResult<KVPair>();
_kvEndpoint
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(result);
}
private void GivenFetchFromConsulSucceeds()
{
var json = JsonConvert.SerializeObject(_fileConfiguration, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
var kvp = new KVPair("OcelotConfiguration");
kvp.Value = bytes;
var query = new QueryResult<KVPair>();
query.Response = kvp;
_kvEndpoint
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(query);
}
private void ThenTheConfigurationIsStoredAs(FileConfiguration config)
{
var json = JsonConvert.SerializeObject(config, Formatting.Indented);
var bytes = Encoding.UTF8.GetBytes(json);
_kvEndpoint
.Verify(x => x.Put(It.Is<KVPair>(k => k.Value.SequenceEqual(bytes)), It.IsAny<CancellationToken>()), Times.Once);
}
private async Task WhenISetTheConfiguration()
{
_setResult = await _repo.Set(_fileConfiguration);
}
private void GivenIHaveAConfiguration(FileConfiguration config)
{
_fileConfiguration = config;
}
private FileConfiguration FakeFileConfiguration()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.12.12.12",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/asdfs/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Port = 198,
Host = "blah"
}
};
return new FileConfiguration
{
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
}
}

View File

@ -1,242 +1,287 @@
namespace Ocelot.UnitTests.Configuration
{
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Newtonsoft.Json;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration.Repository;
public class DiskFileConfigurationRepositoryTests
{
private readonly Mock<IHostingEnvironment> _hostingEnvironment = new Mock<IHostingEnvironment>();
private IFileConfigurationRepository _repo;
private string _configurationPath;
private FileConfiguration _result;
private FileConfiguration _fileConfiguration;
// This is a bit dirty and it is dev.dev so that the ConfigurationBuilderExtensionsTests
// cant pick it up if they run in parralel..sigh these are not really unit
// tests but whatever...
private string _environmentName = "DEV.DEV";
public DiskFileConfigurationRepositoryTests()
{
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object);
}
[Fact]
public void should_return_file_configuration()
{
var config = FakeFileConfigurationForGet();
this.Given(_ => GivenTheConfigurationIs(config))
.When(_ => WhenIGetTheReRoutes())
.Then(_ => ThenTheFollowingIsReturned(config))
.BDDfy();
}
[Fact]
public void should_return_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForGet();
this.Given(_ => GivenTheEnvironmentNameIsUnavailable())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenIGetTheReRoutes())
.Then(_ => ThenTheFollowingIsReturned(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration()
{
var config = FakeFileConfigurationForSet();
this.Given(_ => GivenIHaveAConfiguration(config))
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.And(_ => ThenTheConfigurationJsonIsIndented(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForSet();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenTheEnvironmentNameIsUnavailable())
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.And(_ => ThenTheConfigurationJsonIsIndented(config))
.BDDfy();
}
private void GivenTheEnvironmentNameIsUnavailable()
{
_environmentName = null;
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object);
}
private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration)
{
_fileConfiguration = fileConfiguration;
}
private void WhenISetTheConfiguration()
{
_repo.Set(_fileConfiguration);
_result = _repo.Get().Result.Data;
}
private void ThenTheConfigurationIsStoredAs(FileConfiguration expecteds)
{
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
}
}
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
{
_configurationPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
if (File.Exists(_configurationPath))
{
File.Delete(_configurationPath);
}
File.WriteAllText(_configurationPath, jsonConfiguration);
}
private void ThenTheConfigurationJsonIsIndented(FileConfiguration expecteds)
{
var path = !string.IsNullOrEmpty(_configurationPath) ? _configurationPath : _configurationPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
var resultText = File.ReadAllText(_configurationPath);
var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented);
resultText.ShouldBe(expectedText);
}
private void WhenIGetTheReRoutes()
{
_result = _repo.Get().Result.Data;
}
private void ThenTheFollowingIsReturned(FileConfiguration expecteds)
{
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
}
}
private FileConfiguration FakeFileConfigurationForSet()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.12.12.12",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/asdfs/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Port = 198,
Host = "blah"
}
};
return new FileConfiguration
{
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
private FileConfiguration FakeFileConfigurationForGet()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/test/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Port = 198,
Host = "blah"
}
};
return new FileConfiguration
{
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
}
}
namespace Ocelot.UnitTests.Configuration
{
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Newtonsoft.Json;
using System.IO;
using System.Threading;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration.Repository;
public class DiskFileConfigurationRepositoryTests : IDisposable
{
private readonly Mock<IHostingEnvironment> _hostingEnvironment;
private IFileConfigurationRepository _repo;
private string _environmentSpecificPath;
private string _ocelotJsonPath;
private FileConfiguration _result;
private FileConfiguration _fileConfiguration;
// This is a bit dirty and it is dev.dev so that the ConfigurationBuilderExtensionsTests
// cant pick it up if they run in parralel..and the semaphore stops them running at the same time...sigh
// these are not really unit tests but whatever...
private string _environmentName = "DEV.DEV";
private static SemaphoreSlim _semaphore;
public DiskFileConfigurationRepositoryTests()
{
_semaphore = new SemaphoreSlim(1, 1);
_semaphore.Wait();
_hostingEnvironment = new Mock<IHostingEnvironment>();
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object);
}
[Fact]
public void should_return_file_configuration()
{
var config = FakeFileConfigurationForGet();
this.Given(_ => GivenTheConfigurationIs(config))
.When(_ => WhenIGetTheReRoutes())
.Then(_ => ThenTheFollowingIsReturned(config))
.BDDfy();
}
[Fact]
public void should_return_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForGet();
this.Given(_ => GivenTheEnvironmentNameIsUnavailable())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenIGetTheReRoutes())
.Then(_ => ThenTheFollowingIsReturned(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration()
{
var config = FakeFileConfigurationForSet();
this.Given(_ => GivenIHaveAConfiguration(config))
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.And(_ => ThenTheConfigurationJsonIsIndented(config))
.BDDfy();
}
[Fact]
public void should_set_file_configuration_if_environment_name_is_unavailable()
{
var config = FakeFileConfigurationForSet();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenTheEnvironmentNameIsUnavailable())
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.And(_ => ThenTheConfigurationJsonIsIndented(config))
.BDDfy();
}
[Fact]
public void should_set_environment_file_configuration_and_ocelot_file_configuration()
{
var config = FakeFileConfigurationForSet();
this.Given(_ => GivenIHaveAConfiguration(config))
.And(_ => GivenTheConfigurationIs(config))
.And(_ => GivenTheUserAddedOcelotJson())
.When(_ => WhenISetTheConfiguration())
.Then(_ => ThenTheConfigurationIsStoredAs(config))
.And(_ => ThenTheConfigurationJsonIsIndented(config))
.Then(_ => ThenTheOcelotJsonIsStoredAs(config))
.BDDfy();
}
private void GivenTheUserAddedOcelotJson()
{
_ocelotJsonPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(_ocelotJsonPath))
{
File.Delete(_ocelotJsonPath);
}
File.WriteAllText(_ocelotJsonPath, "Doesnt matter");
}
private void GivenTheEnvironmentNameIsUnavailable()
{
_environmentName = null;
_hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName);
_repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object);
}
private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration)
{
_fileConfiguration = fileConfiguration;
}
private void WhenISetTheConfiguration()
{
_repo.Set(_fileConfiguration);
_result = _repo.Get().Result.Data;
}
private void ThenTheConfigurationIsStoredAs(FileConfiguration expecteds)
{
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
}
}
private void ThenTheOcelotJsonIsStoredAs(FileConfiguration expecteds)
{
var resultText = File.ReadAllText(_ocelotJsonPath);
var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented);
resultText.ShouldBe(expectedText);
}
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
{
_environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented);
if (File.Exists(_environmentSpecificPath))
{
File.Delete(_environmentSpecificPath);
}
File.WriteAllText(_environmentSpecificPath, jsonConfiguration);
}
private void ThenTheConfigurationJsonIsIndented(FileConfiguration expecteds)
{
var path = !string.IsNullOrEmpty(_environmentSpecificPath) ? _environmentSpecificPath : _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json";
var resultText = File.ReadAllText(path);
var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented);
resultText.ShouldBe(expectedText);
}
private void WhenIGetTheReRoutes()
{
_result = _repo.Get().Result.Data;
}
private void ThenTheFollowingIsReturned(FileConfiguration expecteds)
{
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for(var i = 0; i < _result.ReRoutes.Count; i++)
{
for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = _result.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
}
}
private FileConfiguration FakeFileConfigurationForSet()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.12.12.12",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/asdfs/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Port = 198,
Host = "blah"
}
};
return new FileConfiguration
{
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
private FileConfiguration FakeFileConfigurationForGet()
{
var reRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/test/test/{test}"
}
};
var globalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Port = 198,
Host = "blah"
}
};
return new FileConfiguration
{
GlobalConfiguration = globalConfiguration,
ReRoutes = reRoutes
};
}
public void Dispose()
{
_semaphore.Release();
}
}
}

View File

@ -12,37 +12,39 @@ using TestStack.BDDfy;
using Xunit;
using Shouldly;
using static Ocelot.Infrastructure.Wait;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration;
namespace Ocelot.UnitTests.Configuration
{
public class ConsulFileConfigurationPollerTests : IDisposable
public class FileConfigurationPollerTests : IDisposable
{
private readonly ConsulFileConfigurationPoller _poller;
private readonly FileConfigurationPoller _poller;
private Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IFileConfigurationRepository> _repo;
private readonly Mock<IFileConfigurationSetter> _setter;
private readonly FileConfiguration _fileConfig;
private Mock<IConsulPollerConfiguration> _config;
private Mock<IFileConfigurationPollerOptions> _config;
private readonly Mock<IInternalConfigurationRepository> _internalConfigRepo;
private readonly Mock<IInternalConfigurationCreator> _internalConfigCreator;
private IInternalConfiguration _internalConfig;
public ConsulFileConfigurationPollerTests()
public FileConfigurationPollerTests()
{
var logger = new Mock<IOcelotLogger>();
_factory = new Mock<IOcelotLoggerFactory>();
_factory.Setup(x => x.CreateLogger<ConsulFileConfigurationPoller>()).Returns(logger.Object);
_factory.Setup(x => x.CreateLogger<FileConfigurationPoller>()).Returns(logger.Object);
_repo = new Mock<IFileConfigurationRepository>();
_setter = new Mock<IFileConfigurationSetter>();
_fileConfig = new FileConfiguration();
_config = new Mock<IConsulPollerConfiguration>();
_config = new Mock<IFileConfigurationPollerOptions>();
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig));
_config.Setup(x => x.Delay).Returns(100);
_poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object, _config.Object);
_internalConfigRepo = new Mock<IInternalConfigurationRepository>();
_internalConfigCreator = new Mock<IInternalConfigurationCreator>();
_internalConfigCreator.Setup(x => x.Create(It.IsAny<FileConfiguration>())).ReturnsAsync(new OkResponse<IInternalConfiguration>(_internalConfig));
_poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object);
_poller.StartAsync(new CancellationToken());
}
public void Dispose()
{
_poller.Dispose();
}
[Fact]
public void should_start()
{
@ -69,7 +71,7 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenTheConfigIsChangedInConsul(newConfig, 0))
this.Given(x => WhenTheConfigIsChanged(newConfig, 0))
.Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1))
.BDDfy();
}
@ -94,13 +96,13 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenTheConfigIsChangedInConsul(newConfig, 10))
this.Given(x => WhenTheConfigIsChanged(newConfig, 10))
.Then(x => ThenTheSetterIsCalled(newConfig, 1))
.BDDfy();
}
[Fact]
public void should_do_nothing_if_call_to_consul_fails()
public void should_do_nothing_if_call_to_provider_fails()
{
var newConfig = new FileConfiguration
{
@ -119,19 +121,19 @@ namespace Ocelot.UnitTests.Configuration
}
};
this.Given(x => WhenConsulErrors())
this.Given(x => WhenProviderErrors())
.Then(x => ThenTheSetterIsCalled(newConfig, 0))
.BDDfy();
}
private void WhenConsulErrors()
private void WhenProviderErrors()
{
_repo
.Setup(x => x.Get())
.ReturnsAsync(new ErrorResponse<FileConfiguration>(new AnyError()));
}
private void WhenTheConfigIsChangedInConsul(FileConfiguration newConfig, int delay)
private void WhenTheConfigIsChanged(FileConfiguration newConfig, int delay)
{
_repo
.Setup(x => x.Get())
@ -141,10 +143,11 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times)
{
var result = WaitFor(2000).Until(() => {
var result = WaitFor(4000).Until(() => {
try
{
_setter.Verify(x => x.Set(fileConfig), Times.Exactly(times));
_internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.Exactly(times));
_internalConfigCreator.Verify(x => x.Create(fileConfig), Times.Exactly(times));
return true;
}
catch(Exception)
@ -157,10 +160,11 @@ namespace Ocelot.UnitTests.Configuration
private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times)
{
var result = WaitFor(2000).Until(() => {
var result = WaitFor(4000).Until(() => {
try
{
_setter.Verify(x => x.Set(fileConfig), Times.AtLeast(times));
_internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.AtLeast(times));
_internalConfigCreator.Verify(x => x.Create(fileConfig), Times.AtLeast(times));
return true;
}
catch(Exception)
@ -170,5 +174,10 @@ namespace Ocelot.UnitTests.Configuration
});
result.ShouldBeTrue();
}
public void Dispose()
{
_poller.Dispose();
}
}
}

View File

@ -17,6 +17,7 @@
using Ocelot.Errors;
using Ocelot.UnitTests.TestData;
using Ocelot.Values;
using System;
public class FileInternalConfigurationCreatorTests
{
@ -35,7 +36,7 @@
private readonly Mock<IRateLimitOptionsCreator> _rateLimitOptions;
private readonly Mock<IRegionCreator> _regionCreator;
private readonly Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
private readonly Mock<IAdministrationPath> _adminPath;
private readonly Mock<IServiceProvider> _serviceProvider;
private readonly Mock<IHeaderFindAndReplaceCreator> _headerFindAndReplaceCreator;
private readonly Mock<IDownstreamAddressesCreator> _downstreamAddressesCreator;
@ -53,7 +54,7 @@
_rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
_regionCreator = new Mock<IRegionCreator>();
_httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
_adminPath = new Mock<IAdministrationPath>();
_serviceProvider = new Mock<IServiceProvider>();
_headerFindAndReplaceCreator = new Mock<IHeaderFindAndReplaceCreator>();
_downstreamAddressesCreator = new Mock<IDownstreamAddressesCreator>();
@ -70,7 +71,7 @@
_rateLimitOptions.Object,
_regionCreator.Object,
_httpHandlerOptionsCreator.Object,
_adminPath.Object,
_serviceProvider.Object,
_headerFindAndReplaceCreator.Object,
_downstreamAddressesCreator.Object);
}
@ -821,6 +822,54 @@
.BDDfy();
}
[Fact]
public void should_set_up_dynamic_re_routes()
{
var reRouteOptions = new ReRouteOptionsBuilder()
.Build();
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithEnableRateLimiting(true)
.WithRateLimitOptions(new RateLimitOptionsBuilder().Build())
.Build();
var rateLimitOptions = new RateLimitOptionsBuilder()
.WithRateLimitRule(new RateLimitRule("1s", 1, 1))
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{
DynamicReRoutes = new List<FileDynamicReRoute>
{
new FileDynamicReRoute
{
ServiceName = "test",
RateLimitRule = new FileRateLimitRule
{
Period = "1s",
PeriodTimespan = 1,
Limit = 1
}
}
},
}))
.And(x => x.GivenTheConfigIsValid())
.And(x => GivenTheRateLimitCreatorReturns(rateLimitOptions))
.And(x => GivenTheDownstreamAddresses())
.And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheDynamicReRouteIsSetUp("test", rateLimitOptions.RateLimitRule))
.BDDfy();
}
private void GivenTheRateLimitCreatorReturns(RateLimitOptions rateLimitOptions)
{
_rateLimitOptions
.Setup(x => x.Create(It.IsAny<FileRateLimitRule>(), It.IsAny<FileGlobalConfiguration>()))
.Returns(rateLimitOptions);
}
private void GivenTheConfigIsInvalid(List<Error> errors)
{
_validator
@ -844,7 +893,7 @@
private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly()
{
_rateLimitOptions
.Verify(x => x.Create(It.IsAny<FileReRoute>(), It.IsAny<FileGlobalConfiguration>(), It.IsAny<bool>()), Times.Once);
.Verify(x => x.Create(It.IsAny<FileRateLimitRule>(), It.IsAny<FileGlobalConfiguration>()), Times.Once);
}
private void GivenTheConfigIsValid()
@ -864,6 +913,16 @@
_config = _internalConfigurationCreator.Create(_fileConfiguration).Result;
}
private void ThenTheDynamicReRouteIsSetUp(string serviceName, RateLimitRule rateLimitOptions)
{
var dynamic = _config.Data.ReRoutes[0].DownstreamReRoute[0];
dynamic.ServiceName.ShouldBe(serviceName);
dynamic.EnableEndpointEndpointRateLimiting.ShouldBeTrue();
dynamic.RateLimitOptions.RateLimitRule.Period.ShouldBe(rateLimitOptions.Period);
dynamic.RateLimitOptions.RateLimitRule.Limit.ShouldBe(rateLimitOptions.Limit);
dynamic.RateLimitOptions.RateLimitRule.PeriodTimespan.ShouldBe(rateLimitOptions.PeriodTimespan);
}
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes)
{
for (int i = 0; i < _config.Data.ReRoutes.Count; i++)

View File

@ -1,175 +1,182 @@
using System;
using Butterfly.Client.Tracing;
using Butterfly.OpenTracing;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class HttpHandlerOptionsCreatorTests
{
private IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
private FileReRoute _fileReRoute;
private HttpHandlerOptions _httpHandlerOptions;
private IServiceTracer _serviceTracer;
public HttpHandlerOptionsCreatorTests()
{
_serviceTracer = new FakeServiceTracer();
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceTracer);
}
[Fact]
public void should_not_use_tracing_if_fake_tracer_registered()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_use_tracing_if_real_tracer_registered()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
}
};
var expectedOptions = new HttpHandlerOptions(false, false, true, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.And(x => GivenARealTracer())
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{
var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_useCookie_and_allowAutoRedirect()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false,
UseCookieContainer = false,
UseTracing = false
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
using System;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Logging;
public class HttpHandlerOptionsCreatorTests
{
private IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
private FileReRoute _fileReRoute;
private HttpHandlerOptions _httpHandlerOptions;
private IServiceProvider _serviceProvider;
private IServiceCollection _serviceCollection;
public HttpHandlerOptionsCreatorTests()
{
_serviceCollection = new ServiceCollection();
_serviceProvider = _serviceCollection.BuildServiceProvider();
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider);
}
[Fact]
public void should_create_options_with_useproxy_true_as_default()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions()
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
[Fact]
public void should_not_use_tracing_if_fake_tracer_registered()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_useproxy()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseProxy = false
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, false);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
private void GivenTheFollowing(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
private void WhenICreateHttpHandlerOptions()
{
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute.HttpHandlerOptions);
}
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected)
{
_httpHandlerOptions.ShouldNotBeNull();
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
[Fact]
public void should_use_tracing_if_real_tracer_registered()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
}
};
var expectedOptions = new HttpHandlerOptions(false, false, true, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.And(x => GivenARealTracer())
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default()
{
var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_useCookie_and_allowAutoRedirect()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = false,
UseCookieContainer = false,
UseTracing = false
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_useproxy_true_as_default()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions()
};
var expectedOptions = new HttpHandlerOptions(false, false, false, true);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
[Fact]
public void should_create_options_with_specified_useproxy()
{
var fileReRoute = new FileReRoute
{
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseProxy = false
}
};
var expectedOptions = new HttpHandlerOptions(false, false, false, false);
this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions())
.Then(x => ThenTheFollowingOptionsReturned(expectedOptions))
.BDDfy();
}
private void GivenTheFollowing(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
private void WhenICreateHttpHandlerOptions()
{
_httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute.HttpHandlerOptions);
}
private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected)
{
_httpHandlerOptions.ShouldNotBeNull();
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing);
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
}
private void GivenARealTracer()
{
var tracer = new RealTracer();
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(tracer);
}
class RealTracer : IServiceTracer
{
public ITracer Tracer => throw new NotImplementedException();
public string ServiceName => throw new NotImplementedException();
public string Environment => throw new NotImplementedException();
public string Identity => throw new NotImplementedException();
public ISpan Start(ISpanBuilder spanBuilder)
{
throw new NotImplementedException();
}
}
}
}
_httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy);
}
private void GivenARealTracer()
{
var tracer = new FakeTracer();
_serviceCollection.AddSingleton<ITracer, FakeTracer>();
_serviceProvider = _serviceCollection.BuildServiceProvider();
_httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider);
}
class FakeTracer : ITracer
{
public void Event(HttpContext httpContext, string @event)
{
throw new NotImplementedException();
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, Action<string> addTraceIdToRepo,
Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> baseSendAsync)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -1,16 +0,0 @@
using Ocelot.Configuration.Creator;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class IdentityServerConfigurationCreatorTests
{
[Fact]
public void happy_path_only_exists_for_test_coverage_even_uncle_bob_probably_wouldnt_test_this()
{
var result = IdentityServerConfigurationCreator.GetIdentityServerConfiguration("secret");
result.ApiName.ShouldBe("admin");
}
}
}

View File

@ -1,108 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class RateLimitOptionsCreatorTests
{
private FileReRoute _fileReRoute;
private FileGlobalConfiguration _fileGlobalConfig;
private bool _enabled;
private RateLimitOptionsCreator _creator;
private RateLimitOptions _result;
public RateLimitOptionsCreatorTests()
{
_creator = new RateLimitOptionsCreator();
}
[Fact]
public void should_create_rate_limit_options()
{
var fileReRoute = new FileReRoute
{
RateLimitOptions = new FileRateLimitRule
{
ClientWhitelist = new List<string>(),
Period = "Period",
Limit = 1,
PeriodTimespan = 1,
EnableRateLimiting = true
}
};
var fileGlobalConfig = new FileGlobalConfiguration
{
RateLimitOptions = new FileRateLimitOptions
{
ClientIdHeader = "ClientIdHeader",
DisableRateLimitHeaders = true,
QuotaExceededMessage = "QuotaExceededMessage",
RateLimitCounterPrefix = "RateLimitCounterPrefix",
HttpStatusCode = 200
}
};
var expected = new RateLimitOptionsBuilder()
.WithClientIdHeader("ClientIdHeader")
.WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist)
.WithDisableRateLimitHeaders(true)
.WithEnableRateLimiting(true)
.WithHttpStatusCode(200)
.WithQuotaExceededMessage("QuotaExceededMessage")
.WithRateLimitCounterPrefix("RateLimitCounterPrefix")
.WithRateLimitRule(new RateLimitRule(fileReRoute.RateLimitOptions.Period,
fileReRoute.RateLimitOptions.PeriodTimespan,
fileReRoute.RateLimitOptions.Limit))
.Build();
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.And(x => x.GivenTheFollowingFileGlobalConfig(fileGlobalConfig))
.And(x => x.GivenRateLimitingIsEnabled())
.When(x => x.WhenICreate())
.Then(x => x.ThenTheFollowingIsReturned(expected))
.BDDfy();
}
private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
private void GivenTheFollowingFileGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
{
_fileGlobalConfig = fileGlobalConfig;
}
private void GivenRateLimitingIsEnabled()
{
_enabled = true;
}
private void WhenICreate()
{
_result = _creator.Create(_fileReRoute, _fileGlobalConfig, _enabled);
}
private void ThenTheFollowingIsReturned(RateLimitOptions expected)
{
_result.ClientIdHeader.ShouldBe(expected.ClientIdHeader);
_result.ClientWhitelist.ShouldBe(expected.ClientWhitelist);
_result.DisableRateLimitHeaders.ShouldBe(expected.DisableRateLimitHeaders);
_result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
_result.HttpStatusCode.ShouldBe(expected.HttpStatusCode);
_result.QuotaExceededMessage.ShouldBe(expected.QuotaExceededMessage);
_result.RateLimitCounterPrefix.ShouldBe(expected.RateLimitCounterPrefix);
_result.RateLimitRule.Limit.ShouldBe(expected.RateLimitRule.Limit);
_result.RateLimitRule.Period.ShouldBe(expected.RateLimitRule.Period);
TimeSpan.FromSeconds(_result.RateLimitRule.PeriodTimespan).Ticks.ShouldBe(TimeSpan.FromSeconds(expected.RateLimitRule.PeriodTimespan).Ticks);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class RateLimitOptionsCreatorTests
{
private FileReRoute _fileReRoute;
private FileGlobalConfiguration _fileGlobalConfig;
private bool _enabled;
private RateLimitOptionsCreator _creator;
private RateLimitOptions _result;
public RateLimitOptionsCreatorTests()
{
_creator = new RateLimitOptionsCreator();
}
[Fact]
public void should_create_rate_limit_options()
{
var fileReRoute = new FileReRoute
{
RateLimitOptions = new FileRateLimitRule
{
ClientWhitelist = new List<string>(),
Period = "Period",
Limit = 1,
PeriodTimespan = 1,
EnableRateLimiting = true
}
};
var fileGlobalConfig = new FileGlobalConfiguration
{
RateLimitOptions = new FileRateLimitOptions
{
ClientIdHeader = "ClientIdHeader",
DisableRateLimitHeaders = true,
QuotaExceededMessage = "QuotaExceededMessage",
RateLimitCounterPrefix = "RateLimitCounterPrefix",
HttpStatusCode = 200
}
};
var expected = new RateLimitOptionsBuilder()
.WithClientIdHeader("ClientIdHeader")
.WithClientWhiteList(fileReRoute.RateLimitOptions.ClientWhitelist)
.WithDisableRateLimitHeaders(true)
.WithEnableRateLimiting(true)
.WithHttpStatusCode(200)
.WithQuotaExceededMessage("QuotaExceededMessage")
.WithRateLimitCounterPrefix("RateLimitCounterPrefix")
.WithRateLimitRule(new RateLimitRule(fileReRoute.RateLimitOptions.Period,
fileReRoute.RateLimitOptions.PeriodTimespan,
fileReRoute.RateLimitOptions.Limit))
.Build();
this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute))
.And(x => x.GivenTheFollowingFileGlobalConfig(fileGlobalConfig))
.And(x => x.GivenRateLimitingIsEnabled())
.When(x => x.WhenICreate())
.Then(x => x.ThenTheFollowingIsReturned(expected))
.BDDfy();
}
private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute)
{
_fileReRoute = fileReRoute;
}
private void GivenTheFollowingFileGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
{
_fileGlobalConfig = fileGlobalConfig;
}
private void GivenRateLimitingIsEnabled()
{
_enabled = true;
}
private void WhenICreate()
{
_result = _creator.Create(_fileReRoute.RateLimitOptions, _fileGlobalConfig);
}
private void ThenTheFollowingIsReturned(RateLimitOptions expected)
{
_result.ClientIdHeader.ShouldBe(expected.ClientIdHeader);
_result.ClientWhitelist.ShouldBe(expected.ClientWhitelist);
_result.DisableRateLimitHeaders.ShouldBe(expected.DisableRateLimitHeaders);
_result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
_result.HttpStatusCode.ShouldBe(expected.HttpStatusCode);
_result.QuotaExceededMessage.ShouldBe(expected.QuotaExceededMessage);
_result.RateLimitCounterPrefix.ShouldBe(expected.RateLimitCounterPrefix);
_result.RateLimitRule.Limit.ShouldBe(expected.RateLimitRule.Limit);
_result.RateLimitRule.Period.ShouldBe(expected.RateLimitRule.Period);
TimeSpan.FromSeconds(_result.RateLimitRule.PeriodTimespan).Ticks.ShouldBe(TimeSpan.FromSeconds(expected.RateLimitRule.PeriodTimespan).Ticks);
}
}
}

View File

@ -8,14 +8,11 @@ using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
using Ocelot.Raft;
using Rafty.Concensus;
using Ocelot.Configuration;
namespace Ocelot.UnitTests.Controllers
{
using Ocelot.Configuration.Repository;
using Rafty.Concensus.Node;
public class FileConfigurationControllerTests
{
@ -25,7 +22,6 @@ namespace Ocelot.UnitTests.Controllers
private IActionResult _result;
private FileConfiguration _fileConfiguration;
private readonly Mock<IServiceProvider> _provider;
private Mock<INode> _node;
public FileConfigurationControllerTests()
{
@ -70,33 +66,6 @@ namespace Ocelot.UnitTests.Controllers
.BDDfy();
}
[Fact]
public void should_post_file_configuration_using_raft_node()
{
var expected = new FileConfiguration();
this.Given(x => GivenTheFileConfiguration(expected))
.And(x => GivenARaftNodeIsRegistered())
.And(x => GivenTheNodeReturnsOK())
.And(x => GivenTheConfigSetterReturns(new OkResponse()))
.When(x => WhenIPostTheFileConfiguration())
.Then(x => x.ThenTheNodeIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_return_error_when_cannot_set_config_using_raft_node()
{
var expected = new FileConfiguration();
this.Given(x => GivenTheFileConfiguration(expected))
.And(x => GivenARaftNodeIsRegistered())
.And(x => GivenTheNodeReturnsError())
.When(x => WhenIPostTheFileConfiguration())
.Then(x => ThenTheResponseIs<BadRequestObjectResult>())
.BDDfy();
}
[Fact]
public void should_return_error_when_cannot_set_config()
{
@ -110,33 +79,6 @@ namespace Ocelot.UnitTests.Controllers
.BDDfy();
}
private void ThenTheNodeIsCalledCorrectly()
{
_node.Verify(x => x.Accept(It.IsAny<UpdateFileConfiguration>()), Times.Once);
}
private void GivenARaftNodeIsRegistered()
{
_node = new Mock<INode>();
_provider
.Setup(x => x.GetService(typeof(INode)))
.Returns(_node.Object);
}
private void GivenTheNodeReturnsOK()
{
_node
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
.ReturnsAsync(new Rafty.Infrastructure.OkResponse<UpdateFileConfiguration>(new UpdateFileConfiguration(new FileConfiguration())));
}
private void GivenTheNodeReturnsError()
{
_node
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
.ReturnsAsync(new Rafty.Infrastructure.ErrorResponse<UpdateFileConfiguration>("error", new UpdateFileConfiguration(new FileConfiguration())));
}
private void GivenTheConfigSetterReturns(Response response)
{
_setter

View File

@ -46,7 +46,7 @@
[Fact]
public void should_merge_files()
{
this.Given(_ => GivenMultipleConfigurationFiles(false))
this.Given(_ => GivenMultipleConfigurationFiles("", false))
.When(_ => WhenIAddOcelotConfiguration(false))
.Then(_ => ThenTheConfigsAreMerged())
.BDDfy();
@ -55,15 +55,30 @@
[Fact]
public void should_merge_files_except_env()
{
this.Given(_ => GivenMultipleConfigurationFiles(true))
this.Given(_ => GivenMultipleConfigurationFiles("", true))
.When(_ => WhenIAddOcelotConfiguration(true))
.Then(_ => ThenTheConfigsAreMerged())
.And(_ => NotContainsEnvSpecificConfig())
.BDDfy();
}
private void GivenMultipleConfigurationFiles(bool addEnvSpecificConfig)
[Fact]
public void should_merge_files_in_specific_folder()
{
string configFolder = "ConfigFiles";
this.Given(_ => GivenMultipleConfigurationFiles(configFolder, false))
.When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder))
.Then(_ => ThenTheConfigsAreMerged())
.BDDfy();
}
private void GivenMultipleConfigurationFiles(string folder, bool addEnvSpecificConfig)
{
if (!string.IsNullOrEmpty(folder))
{
Directory.CreateDirectory(folder);
}
_globalConfig = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
@ -209,14 +224,20 @@
}
};
File.WriteAllText("ocelot.global.json", JsonConvert.SerializeObject(_globalConfig));
File.WriteAllText("ocelot.reRoutesA.json", JsonConvert.SerializeObject(_reRouteA));
File.WriteAllText("ocelot.reRoutesB.json", JsonConvert.SerializeObject(_reRouteB));
File.WriteAllText("ocelot.aggregates.json", JsonConvert.SerializeObject(_aggregate));
string globalFilename = Path.Combine(folder, "ocelot.global.json");
string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json");
string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json");
string aggregatesFilename = Path.Combine(folder, "ocelot.aggregates.json");
File.WriteAllText(globalFilename, JsonConvert.SerializeObject(_globalConfig));
File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA));
File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB));
File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate));
if (addEnvSpecificConfig)
{
File.WriteAllText("ocelot.Env.json", JsonConvert.SerializeObject(_envSpecific));
string envSpecificFilename = Path.Combine(folder, "ocelot.Env.json");
File.WriteAllText(envSpecificFilename, JsonConvert.SerializeObject(_envSpecific));
}
}
@ -232,6 +253,13 @@
_configRoot = builder.Build();
}
private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder)
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddOcelot(folder);
_configRoot = builder.Build();
}
private void ThenTheConfigsAreMerged()
{
var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration));

View File

@ -1,28 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Cache;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
using Ocelot.DependencyInjection;
using Ocelot.Requester;
using Ocelot.UnitTests.Requester;
using Shouldly;
using IdentityServer4.AccessTokenValidation;
using TestStack.BDDfy;
using Xunit;
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
using Ocelot.Middleware.Multiplexer;
namespace Ocelot.UnitTests.DependencyInjection
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.Setter;
using Ocelot.DependencyInjection;
using Ocelot.Requester;
using Ocelot.UnitTests.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests;
using Ocelot.Middleware.Multiplexer;
public class OcelotBuilderTests
{
private readonly IServiceCollection _services;
@ -79,57 +74,6 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy();
}
[Fact]
public void should_set_up_cache_manager()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpCacheManager())
.Then(x => ThenAnExceptionIsntThrown())
.And(x => OnlyOneVersionOfEachCacheIsRegistered())
.BDDfy();
}
[Fact]
public void should_set_up_consul()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpConsul())
.Then(x => ThenAnExceptionIsntThrown())
.BDDfy();
}
[Fact]
public void should_set_up_rafty()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpRafty())
.Then(x => ThenAnExceptionIsntThrown())
.Then(x => ThenTheCorrectAdminPathIsRegitered())
.BDDfy();
}
[Fact]
public void should_set_up_administration_with_identity_server_options()
{
Action<IdentityServerAuthenticationOptions> options = o => {};
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpAdministration(options))
.Then(x => ThenAnExceptionIsntThrown())
.Then(x => ThenTheCorrectAdminPathIsRegitered())
.BDDfy();
}
[Fact]
public void should_set_up_administration()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpAdministration())
.Then(x => ThenAnExceptionIsntThrown())
.Then(x => ThenTheCorrectAdminPathIsRegitered())
.BDDfy();
}
[Fact]
public void should_use_logger_factory()
{
@ -140,15 +84,6 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy();
}
[Fact]
public void should_set_up_tracing()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpOpentracing())
.When(x => WhenIAccessOcelotHttpTracingHandler())
.BDDfy();
}
[Fact]
public void should_set_up_without_passing_in_config()
{
@ -191,15 +126,6 @@ namespace Ocelot.UnitTests.DependencyInjection
_ocelotBuilder.AddTransientDefinedAggregator<T>();
}
private void ThenTheSpecificHandlersAreSingleton()
{
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var first = handlers[0];
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var second = handlers[0];
first.ShouldBe(second);
}
private void ThenTheSpecificHandlersAreTransient()
{
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
@ -218,16 +144,6 @@ namespace Ocelot.UnitTests.DependencyInjection
first.ShouldNotBe(second);
}
private void WhenISetUpAdministration()
{
_ocelotBuilder.AddAdministration("/administration", "secret");
}
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
{
_ocelotBuilder.AddAdministration("/administration", options);
}
private void AddTransientGlobalDelegatingHandler<T>()
where T : DelegatingHandler
{
@ -240,13 +156,6 @@ namespace Ocelot.UnitTests.DependencyInjection
_ocelotBuilder.AddDelegatingHandler<T>();
}
private void ThenTheCorrectAdminPathIsRegitered()
{
_serviceProvider = _services.BuildServiceProvider();
var path = _serviceProvider.GetService<IAdministrationPath>();
path.Path.ShouldBe("/administration");
}
private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>()
{
_serviceProvider = _services.BuildServiceProvider();
@ -289,60 +198,6 @@ namespace Ocelot.UnitTests.DependencyInjection
first.ShouldBe(second);
}
private void OnlyOneVersionOfEachCacheIsRegistered()
{
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>));
var instance = (ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance;
var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IInternalConfiguration>));
var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IInternalConfiguration>));
var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>));
var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>));
instance.Configuration.MaxRetries.ShouldBe(_maxRetries);
outputCache.ShouldNotBeNull();
ocelotConfigCache.ShouldNotBeNull();
ocelotConfigCacheManager.ShouldNotBeNull();
fileConfigCache.ShouldNotBeNull();
fileConfigCacheManager.ShouldNotBeNull();
}
private void WhenISetUpConsul()
{
try
{
_ocelotBuilder.AddStoreOcelotConfigurationInConsul();
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenISetUpRafty()
{
try
{
_ocelotBuilder.AddAdministration("/administration", "secret").AddRafty();
}
catch (Exception e)
{
_ex = e;
}
}
private void AddGlobalDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddDelegatingHandler<T>(true);
}
private void AddSpecificDelegatingHandler<T>()
where T : DelegatingHandler
{
_ocelotBuilder.AddDelegatingHandler<T>();
}
private void ThenAnOcelotBuilderIsReturned()
{
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
@ -372,39 +227,6 @@ namespace Ocelot.UnitTests.DependencyInjection
}
}
private void WhenISetUpCacheManager()
{
try
{
_ocelotBuilder.AddCacheManager(x => {
x.WithMaxRetries(_maxRetries);
x.WithDictionaryHandle();
});
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenISetUpOpentracing()
{
try
{
_ocelotBuilder.AddOpenTracing(
option =>
{
option.CollectorUrl = "http://localhost:9618";
option.Service = "Ocelot.ManualTest";
}
);
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenIAccessLoggerFactory()
{
try
@ -419,19 +241,6 @@ namespace Ocelot.UnitTests.DependencyInjection
}
}
private void WhenIAccessOcelotHttpTracingHandler()
{
try
{
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
tracingHandler.ShouldNotBeNull();
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenIValidateScopes()
{
try

View File

@ -1,9 +1,3 @@
using Ocelot.DownstreamRouteFinder.Finder;
using Xunit;
using Shouldly;
using Ocelot.Configuration;
using System.Net.Http;
namespace Ocelot.UnitTests.DownstreamRouteFinder
{
using System;
@ -14,6 +8,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
using Ocelot.LoadBalancer.LoadBalancers;
using Responses;
using TestStack.BDDfy;
using Ocelot.DownstreamRouteFinder.Finder;
using Xunit;
using Shouldly;
using Ocelot.Configuration;
using System.Net.Http;
using System.Collections.Generic;
public class DownstreamRouteCreatorTests
{
@ -53,6 +53,34 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy();
}
[Fact]
public void should_create_downstream_route_with_rate_limit_options()
{
var rateLimitOptions = new RateLimitOptionsBuilder()
.WithEnableRateLimiting(true)
.WithClientIdHeader("test")
.Build();
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithServiceName("auth")
.WithRateLimitOptions(rateLimitOptions)
.Build();
var reRoute = new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.Build();
var reRoutes = new List<ReRoute> { reRoute };
var configuration = new InternalConfiguration(reRoutes, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions);
this.Given(_ => GivenTheConfiguration(configuration))
.When(_ => WhenICreate())
.Then(_ => ThenTheDownstreamRouteIsCreated())
.And(_ => WithRateLimitOptions(rateLimitOptions))
.BDDfy();
}
[Fact]
public void should_cache_downstream_route()
{
@ -174,6 +202,13 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.Returns(options);
}
private void WithRateLimitOptions(RateLimitOptions expected)
{
_result.Data.ReRoute.DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue();
_result.Data.ReRoute.DownstreamReRoute[0].RateLimitOptions.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
_result.Data.ReRoute.DownstreamReRoute[0].RateLimitOptions.ClientIdHeader.ShouldBe(expected.ClientIdHeader);
}
private void ThenTheDownstreamRouteIsCreated()
{
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");

View File

@ -53,6 +53,21 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy();
}
[Fact]
public void should_return_downstream_route_finder_when_not_dynamic_re_route_and_service_discovery_on()
{
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).WithType("test").Build();
var reRoutes = new List<ReRoute>
{
new ReRouteBuilder().WithUpstreamPathTemplate("woot").Build()
};
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
.When(_ => WhenIGet())
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder>())
.BDDfy();
}
[Fact]
public void should_return_downstream_route_finder_as_no_service_discovery_given_no_host()
{
@ -101,6 +116,21 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy();
}
[Fact]
public void should_return_downstream_route_creator_with_dynamic_re_route()
{
var spConfig = new ServiceProviderConfigurationBuilder().WithHost("test").WithPort(50).WithType("test").Build();
var reRoutes = new List<ReRoute>
{
new ReRouteBuilder().Build()
};
this.Given(_ => GivenTheReRoutes(reRoutes, spConfig))
.When(_ => WhenIGet())
.Then(_ => ThenTheResultShouldBe<Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteCreator>())
.BDDfy();
}
private void ThenTheResultShouldBe<T>()
{
_result.ShouldBeOfType<T>();

View File

@ -289,6 +289,39 @@
.BDDfy();
}
[Fact]
public void issue_473_should_not_remove_additional_query_string()
{
var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamPathTemplate("/Authorized/{action}?server={server}")
.WithUpstreamHttpMethod(new List<string> { "Post", "Get" })
.WithDownstreamScheme("http")
.WithUpstreamPathTemplate("/uc/Authorized/{server}/{action}")
.Build();
var config = new ServiceProviderConfigurationBuilder()
.Build();
this.Given(x => x.GivenTheDownStreamRouteIs(
new DownstreamRoute(
new List<PlaceholderNameAndValue>
{
new PlaceholderNameAndValue("{action}", "1"),
new PlaceholderNameAndValue("{server}", "2")
},
new ReRouteBuilder()
.WithDownstreamReRoute(downstreamReRoute)
.WithUpstreamHttpMethod(new List<string> { "Post", "Get" })
.Build())))
.And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/uc/Authorized/2/1/refresh?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d"))
.And(x => GivenTheServiceProviderConfigIs(config))
.And(x => x.GivenTheUrlReplacerWillReturn("/Authorized/1?server=2"))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:5000/Authorized/1?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2"))
.And(x => ThenTheQueryStringIs("?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2"))
.BDDfy();
}
private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config)
{
var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null);

View File

@ -1,197 +1,197 @@
namespace Ocelot.UnitTests.Errors
{
using System;
using System.Net;
using System.Threading.Tasks;
using Ocelot.Errors.Middleware;
using Ocelot.Logging;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Middleware;
using Ocelot.Configuration.Repository;
public class ExceptionHandlerMiddlewareTests
{
bool _shouldThrowAnException;
private readonly Mock<IInternalConfigurationRepository> _configRepo;
private readonly Mock<IRequestScopedDataRepository> _repo;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private readonly ExceptionHandlerMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
public ExceptionHandlerMiddlewareTests()
{
_configRepo = new Mock<IInternalConfigurationRepository>();
_repo = new Mock<IRequestScopedDataRepository>();
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ExceptionHandlerMiddleware>()).Returns(_logger.Object);
_next = async context => {
await Task.CompletedTask;
if (_shouldThrowAnException)
{
throw new Exception("BOOM");
}
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
};
_middleware = new ExceptionHandlerMiddleware(_next, _loggerFactory.Object, _configRepo.Object, _repo.Object);
}
[Fact]
public void NoDownstreamException()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheAspDotnetRequestIdIsSet())
.BDDfy();
}
[Fact]
public void DownstreamException()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsError())
.BDDfy();
}
[Fact]
public void ShouldSetRequestId()
{
var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheRequestIdIsSet("RequestId", "1234"))
.BDDfy();
}
[Fact]
public void ShouldSetAspDotNetRequestId()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheAspDotnetRequestIdIsSet())
.BDDfy();
}
[Fact]
public void should_throw_exception_if_config_provider_returns_error()
{
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigReturnsError())
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenAnExceptionIsThrown())
.BDDfy();
}
[Fact]
public void should_throw_exception_if_config_provider_throws()
{
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigThrows())
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenAnExceptionIsThrown())
.BDDfy();
}
private void WhenICallTheMiddlewareWithTheRequestIdKey(string key, string value)
{
_downstreamContext.HttpContext.Request.Headers.Add(key, value);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void WhenICallTheMiddleware()
{
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void GivenTheConfigThrows()
{
var ex = new Exception("outer", new Exception("inner"));
_configRepo
.Setup(x => x.Get()).Throws(ex);
}
private void ThenAnExceptionIsThrown()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500);
}
private void GivenTheConfigReturnsError()
{
var response = new Responses.ErrorResponse<IInternalConfiguration>(new FakeError());
_configRepo
.Setup(x => x.Get()).Returns(response);
}
private void TheRequestIdIsSet(string key, string value)
{
_repo.Verify(x => x.Add(key, value), Times.Once);
}
private void GivenTheConfigurationIs(IInternalConfiguration config)
{
var response = new Responses.OkResponse<IInternalConfiguration>(config);
_configRepo
.Setup(x => x.Get()).Returns(response);
}
private void GivenAnExceptionWillNotBeThrownDownstream()
{
_shouldThrowAnException = false;
}
private void GivenAnExceptionWillBeThrownDownstream()
{
_shouldThrowAnException = true;
}
private void ThenTheResponseIsOk()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(200);
}
private void ThenTheResponseIsError()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500);
}
private void TheAspDotnetRequestIdIsSet()
{
_repo.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
class FakeError : Error
{
internal FakeError()
: base("meh", OcelotErrorCode.CannotAddDataError)
{
}
}
}
}
namespace Ocelot.UnitTests.Errors
{
using System;
using System.Net;
using System.Threading.Tasks;
using Ocelot.Errors.Middleware;
using Ocelot.Logging;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Middleware;
using Ocelot.Configuration.Repository;
public class ExceptionHandlerMiddlewareTests
{
bool _shouldThrowAnException;
private readonly Mock<IInternalConfigurationRepository> _configRepo;
private readonly Mock<IRequestScopedDataRepository> _repo;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private readonly ExceptionHandlerMiddleware _middleware;
private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next;
public ExceptionHandlerMiddlewareTests()
{
_configRepo = new Mock<IInternalConfigurationRepository>();
_repo = new Mock<IRequestScopedDataRepository>();
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ExceptionHandlerMiddleware>()).Returns(_logger.Object);
_next = async context => {
await Task.CompletedTask;
if (_shouldThrowAnException)
{
throw new Exception("BOOM");
}
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
};
_middleware = new ExceptionHandlerMiddleware(_next, _loggerFactory.Object, _configRepo.Object, _repo.Object);
}
[Fact]
public void NoDownstreamException()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheAspDotnetRequestIdIsSet())
.BDDfy();
}
[Fact]
public void DownstreamException()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsError())
.BDDfy();
}
[Fact]
public void ShouldSetRequestId()
{
var config = new InternalConfiguration(null, null, null, "requestidkey", null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheRequestIdIsSet("RequestId", "1234"))
.BDDfy();
}
[Fact]
public void ShouldSetAspDotNetRequestId()
{
var config = new InternalConfiguration(null, null, null, null, null, null, null, null);
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigurationIs(config))
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenTheResponseIsOk())
.And(_ => TheAspDotnetRequestIdIsSet())
.BDDfy();
}
[Fact]
public void should_throw_exception_if_config_provider_returns_error()
{
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigReturnsError())
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenAnExceptionIsThrown())
.BDDfy();
}
[Fact]
public void should_throw_exception_if_config_provider_throws()
{
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.And(_ => GivenTheConfigThrows())
.When(_ => WhenICallTheMiddlewareWithTheRequestIdKey("requestidkey", "1234"))
.Then(_ => ThenAnExceptionIsThrown())
.BDDfy();
}
private void WhenICallTheMiddlewareWithTheRequestIdKey(string key, string value)
{
_downstreamContext.HttpContext.Request.Headers.Add(key, value);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void WhenICallTheMiddleware()
{
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
}
private void GivenTheConfigThrows()
{
var ex = new Exception("outer", new Exception("inner"));
_configRepo
.Setup(x => x.Get()).Throws(ex);
}
private void ThenAnExceptionIsThrown()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500);
}
private void GivenTheConfigReturnsError()
{
var response = new Responses.ErrorResponse<IInternalConfiguration>(new FakeError());
_configRepo
.Setup(x => x.Get()).Returns(response);
}
private void TheRequestIdIsSet(string key, string value)
{
_repo.Verify(x => x.Add(key, value), Times.Once);
}
private void GivenTheConfigurationIs(IInternalConfiguration config)
{
var response = new Responses.OkResponse<IInternalConfiguration>(config);
_configRepo
.Setup(x => x.Get()).Returns(response);
}
private void GivenAnExceptionWillNotBeThrownDownstream()
{
_shouldThrowAnException = false;
}
private void GivenAnExceptionWillBeThrownDownstream()
{
_shouldThrowAnException = true;
}
private void ThenTheResponseIsOk()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(200);
}
private void ThenTheResponseIsError()
{
_downstreamContext.HttpContext.Response.StatusCode.ShouldBe(500);
}
private void TheAspDotnetRequestIdIsSet()
{
_repo.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
}
class FakeError : Error
{
internal FakeError()
: base("meh", OcelotErrorCode.CannotAddDataError)
{
}
}
}
}

View File

@ -22,7 +22,7 @@ namespace Ocelot.UnitTests.Infrastructure
called = true;
});
_bus.Publish(new object(), 1);
await Task.Delay(10);
await Task.Delay(100);
called.ShouldBeTrue();
}

View File

@ -7,6 +7,7 @@ using Xunit;
using Ocelot.Middleware;
using Microsoft.AspNetCore.Http;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Ocelot.UnitTests.Logging
{
@ -15,7 +16,8 @@ namespace Ocelot.UnitTests.Logging
private readonly OcelotDiagnosticListener _listener;
private Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IOcelotLogger> _logger;
private IServiceTracer _tracer;
private IServiceCollection _serviceCollection;
private IServiceProvider _serviceProvider;
private DownstreamContext _downstreamContext;
private string _name;
private Exception _exception;
@ -24,9 +26,10 @@ namespace Ocelot.UnitTests.Logging
{
_factory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_tracer = new FakeServiceTracer();
_serviceCollection = new ServiceCollection();
_serviceProvider = _serviceCollection.BuildServiceProvider();
_factory.Setup(x => x.CreateLogger<OcelotDiagnosticListener>()).Returns(_logger.Object);
_listener = new OcelotDiagnosticListener(_factory.Object, _tracer);
_listener = new OcelotDiagnosticListener(_factory.Object, _serviceProvider);
}
[Fact]

View File

@ -3,12 +3,14 @@ namespace Ocelot.UnitTests.Middleware
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Middleware;
using Ocelot.Middleware.Pipeline;
using Pivotal.Discovery.Client;
using Ocelot.Request.Middleware;
using Ocelot.WebSockets.Middleware;
using Shouldly;
using Steeltoe.Common.Discovery;
using Steeltoe.Discovery.Eureka;
using TestStack.BDDfy;
using Xunit;
@ -26,6 +28,15 @@ namespace Ocelot.UnitTests.Middleware
.BDDfy();
}
[Fact]
public void should_expand_pipeline()
{
this.Given(_ => GivenTheDepedenciesAreSetUp())
.When(_ => WhenIExpandBuild())
.Then(_ => ThenThePipelineIsBuilt())
.BDDfy();
}
private void ThenThePipelineIsBuilt()
{
_handlers.ShouldNotBeNull();
@ -36,21 +47,28 @@ namespace Ocelot.UnitTests.Middleware
_handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration());
}
private void WhenIExpandBuild()
{
OcelotPipelineConfiguration configuration = new OcelotPipelineConfiguration();
configuration.MapWhenOcelotPipeline.Add((app) =>
{
app.UseDownstreamRouteFinderMiddleware();
app.UseDownstreamRequestInitialiser();
app.UseLoadBalancingMiddleware();
app.UseDownstreamUrlCreatorMiddleware();
app.UseWebSocketsProxyMiddleware();
return context => context.HttpContext.WebSockets.IsWebSocketRequest;
});
_handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration());
}
private void GivenTheDepedenciesAreSetUp()
{
IConfigurationBuilder test = new ConfigurationBuilder();
var root = test.Build();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(root);
services.AddDiscoveryClient(new DiscoveryOptions
{
ClientType = DiscoveryClientType.EUREKA,
ClientOptions = new EurekaClientOptions()
{
ShouldFetchRegistry = false,
ShouldRegisterWithEureka = false
}
});
services.AddOcelot();
var provider = services.BuildServiceProvider();
_builder = new OcelotPipelineBuilder(provider);

View File

@ -79,6 +79,8 @@ namespace Ocelot.UnitTests.Middleware
del.Invoke(_downstreamContext);
}
private void ThenTheFuncIsInThePipeline()
{
_counter.ShouldBe(1);

View File

@ -57,7 +57,6 @@
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
<PackageReference Include="Rafty" Version="0.4.4" />
</ItemGroup>
<ItemGroup>

View File

@ -1,45 +0,0 @@
using Moq;
using Ocelot.Configuration.Setter;
using Ocelot.Raft;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Raft
{
public class OcelotFiniteStateMachineTests
{
private UpdateFileConfiguration _command;
private OcelotFiniteStateMachine _fsm;
private Mock<IFileConfigurationSetter> _setter;
public OcelotFiniteStateMachineTests()
{
_setter = new Mock<IFileConfigurationSetter>();
_fsm = new OcelotFiniteStateMachine(_setter.Object);
}
[Fact]
public void should_handle_update_file_configuration_command()
{
this.Given(x => GivenACommand(new UpdateFileConfiguration(new Ocelot.Configuration.File.FileConfiguration())))
.When(x => WhenTheCommandIsHandled())
.Then(x => ThenTheStateIsUpdated())
.BDDfy();
}
private void GivenACommand(UpdateFileConfiguration command)
{
_command = command;
}
private void WhenTheCommandIsHandled()
{
_fsm.Handle(new Rafty.Log.LogEntry(_command, _command.GetType(), 0));
}
private void ThenTheStateIsUpdated()
{
_setter.Verify(x => x.Set(_command.Configuration), Times.Once);
}
}
}

View File

@ -1,407 +1,442 @@
namespace Ocelot.UnitTests.Request.Mapper
{
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Primitives;
using Ocelot.Request.Mapper;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public class RequestMapperTests
{
readonly HttpRequest _inputRequest;
readonly RequestMapper _requestMapper;
Response<HttpRequestMessage> _mappedRequest;
List<KeyValuePair<string, StringValues>> _inputHeaders = null;
public RequestMapperTests()
{
_inputRequest = new DefaultHttpRequest(new DefaultHttpContext());
_requestMapper = new RequestMapper();
}
[Theory]
[InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")]
[InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request
[InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")]
[InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")]
[InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")]
public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri)
{
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasUri(expectedUri))
.BDDfy();
}
[Theory]
[InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")]
public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString)
{
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped())
.Then(_ => ThenAnErrorIsReturned())
.And(_ => ThenTheMappedRequestIsNull())
.BDDfy();
}
[Theory]
[InlineData("GET")]
[InlineData("POST")]
[InlineData("WHATEVER")]
public void Should_map_method(string method)
{
this.Given(_ => GivenTheInputRequestHasMethod(method))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasMethod(method))
.BDDfy();
}
[Fact]
public void Should_map_all_headers()
{
this.Given(_ => GivenTheInputRequestHasHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasEachHeader())
.BDDfy();
}
[Fact]
public void Should_handle_no_headers()
{
this.Given(_ => GivenTheInputRequestHasNoHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoHeaders())
.BDDfy();
}
[Fact]
public void Should_map_content()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContent("This is my content"))
.BDDfy();
}
[Fact]
public void Should_handle_no_content()
{
this.Given(_ => GivenTheInputRequestHasNoContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
[Fact]
public void Should_map_content_headers()
{
byte[] md5bytes = new byte[0];
using (var md5 = MD5.Create())
{
md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5"));
}
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheContentEncodingIs("gzip, compress"))
.And(_ => GivenTheContentLanguageIs("english"))
.And(_ => GivenTheContentLocationIs("/my-receipts/38"))
.And(_ => GivenTheContentRangeIs("bytes 1-2/*"))
.And(_ => GivenTheContentDispositionIs("inline"))
.And(_ => GivenTheContentMD5Is(md5bytes))
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress"))
.And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
.And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38"))
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes))
.And(_ => ThenTheMappedRequestHasContentRangeHeader())
.And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
.BDDfy();
}
[Fact]
public void should_not_add_content_headers()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheInputRequestHasMethod("POST"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheOtherContentTypeHeadersAreNotMapped())
.BDDfy();
}
private void ThenTheContentHeadersAreNotAddedToNonContentHeaders()
{
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type");
}
private void ThenTheOtherContentTypeHeadersAreNotMapped()
{
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull();
}
private void ThenTheMappedRequestHasContentDispositionHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected);
}
private void GivenTheContentDispositionIs(string input)
{
_inputRequest.Headers.Add("Content-Disposition", input);
}
private void ThenTheMappedRequestHasContentMD5Header(byte[] expected)
{
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected);
}
private void GivenTheContentMD5Is(byte[] input)
{
var base64 = Convert.ToBase64String(input);
_inputRequest.Headers.Add("Content-MD5", base64);
}
private void ThenTheMappedRequestHasContentRangeHeader()
{
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
_mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2);
}
private void GivenTheContentRangeIs(string input)
{
_inputRequest.Headers.Add("Content-Range", input);
}
private void ThenTheMappedRequestHasContentLocationHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected);
}
private void GivenTheContentLocationIs(string input)
{
_inputRequest.Headers.Add("Content-Location", input);
}
private void ThenTheMappedRequestHasContentLanguageHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected);
}
private void GivenTheContentLanguageIs(string input)
{
_inputRequest.Headers.Add("Content-Language", input);
}
private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo)
{
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo);
}
private void GivenTheContentEncodingIs(string input)
{
_inputRequest.Headers.Add("Content-Encoding", input);
}
private void GivenTheContentTypeIs(string contentType)
{
_inputRequest.ContentType = contentType;
}
private void ThenTheMappedRequestHasContentTypeHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
}
private void ThenTheMappedRequestHasContentSize(long expected)
{
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
}
private void GivenTheInputRequestHasMethod(string method)
{
_inputRequest.Method = method;
}
private void GivenTheInputRequestHasScheme(string scheme)
{
_inputRequest.Scheme = scheme;
}
private void GivenTheInputRequestHasHost(string host)
{
_inputRequest.Host = new HostString(host);
}
private void GivenTheInputRequestHasPath(string path)
{
if (path != null)
{
_inputRequest.Path = path;
}
}
private void GivenTheInputRequestHasQueryString(string querystring)
{
if (querystring != null)
{
_inputRequest.QueryString = new QueryString(querystring);
}
}
private void GivenTheInputRequestHasAValidUri()
{
GivenTheInputRequestHasScheme("http");
GivenTheInputRequestHasHost("www.google.com");
}
private void GivenTheInputRequestHasHeaders()
{
_inputHeaders = new List<KeyValuePair<string, StringValues>>()
{
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })),
new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })),
};
foreach (var inputHeader in _inputHeaders)
{
_inputRequest.Headers.Add(inputHeader);
}
}
private void GivenTheInputRequestHasNoHeaders()
{
_inputRequest.Headers.Clear();
}
private void GivenTheInputRequestHasContent(string content)
{
_inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
}
private void GivenTheInputRequestHasNoContent()
{
_inputRequest.Body = null;
}
private void WhenMapped()
{
_mappedRequest = _requestMapper.Map(_inputRequest).GetAwaiter().GetResult();
}
private void ThenNoErrorIsReturned()
{
_mappedRequest.IsError.ShouldBeFalse();
}
private void ThenAnErrorIsReturned()
{
_mappedRequest.IsError.ShouldBeTrue();
}
private void ThenTheMappedRequestHasUri(string expectedUri)
{
_mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri);
}
private void ThenTheMappedRequestHasMethod(string expectedMethod)
{
_mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod);
}
private void ThenTheMappedRequestHasEachHeader()
{
_mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count);
foreach(var header in _mappedRequest.Data.Headers)
{
var inputHeader = _inputHeaders.First(h => h.Key == header.Key);
inputHeader.ShouldNotBeNull();
inputHeader.Value.Count().ShouldBe(header.Value.Count());
foreach(var inputHeaderValue in inputHeader.Value)
{
header.Value.Any(v => v == inputHeaderValue);
}
}
}
private void ThenTheMappedRequestHasNoHeaders()
{
_mappedRequest.Data.Headers.Count().ShouldBe(0);
}
private void ThenTheMappedRequestHasContent(string expectedContent)
{
_mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent);
}
private void ThenTheMappedRequestHasNoContent()
{
_mappedRequest.Data.Content.ShouldBeNull();
}
private void ThenTheMappedRequestIsNull()
{
_mappedRequest.Data.ShouldBeNull();
}
}
}
namespace Ocelot.UnitTests.Request.Mapper
{
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Primitives;
using Ocelot.Request.Mapper;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Threading.Tasks;
public class RequestMapperTests
{
readonly HttpRequest _inputRequest;
readonly RequestMapper _requestMapper;
Response<HttpRequestMessage> _mappedRequest;
List<KeyValuePair<string, StringValues>> _inputHeaders = null;
public RequestMapperTests()
{
_inputRequest = new DefaultHttpRequest(new DefaultHttpContext());
_requestMapper = new RequestMapper();
}
[Theory]
[InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")]
[InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request
[InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")]
[InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")]
[InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")]
public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri)
{
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasUri(expectedUri))
.BDDfy();
}
[Theory]
[InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")]
public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString)
{
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped())
.Then(_ => ThenAnErrorIsReturned())
.And(_ => ThenTheMappedRequestIsNull())
.BDDfy();
}
[Theory]
[InlineData("GET")]
[InlineData("POST")]
[InlineData("WHATEVER")]
public void Should_map_method(string method)
{
this.Given(_ => GivenTheInputRequestHasMethod(method))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasMethod(method))
.BDDfy();
}
[Fact]
public void Should_map_all_headers()
{
this.Given(_ => GivenTheInputRequestHasHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasEachHeader())
.BDDfy();
}
[Fact]
public void Should_handle_no_headers()
{
this.Given(_ => GivenTheInputRequestHasNoHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoHeaders())
.BDDfy();
}
[Fact]
public void Should_map_content()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContent("This is my content"))
.BDDfy();
}
[Fact]
public void Should_handle_no_content()
{
this.Given(_ => GivenTheInputRequestHasNullContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
[Fact]
public void Should_handle_no_content_type()
{
this.Given(_ => GivenTheInputRequestHasNoContentType())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
[Fact]
public void Should_handle_no_content_length()
{
this.Given(_ => GivenTheInputRequestHasNoContentLength())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
private void GivenTheInputRequestHasNoContentLength()
{
_inputRequest.ContentLength = null;
}
private void GivenTheInputRequestHasNoContentType()
{
_inputRequest.ContentType = null;
}
[Fact]
public void Should_map_content_headers()
{
byte[] md5bytes = new byte[0];
using (var md5 = MD5.Create())
{
md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5"));
}
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheContentEncodingIs("gzip, compress"))
.And(_ => GivenTheContentLanguageIs("english"))
.And(_ => GivenTheContentLocationIs("/my-receipts/38"))
.And(_ => GivenTheContentRangeIs("bytes 1-2/*"))
.And(_ => GivenTheContentDispositionIs("inline"))
.And(_ => GivenTheContentMD5Is(md5bytes))
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress"))
.And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
.And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38"))
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes))
.And(_ => ThenTheMappedRequestHasContentRangeHeader())
.And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
.BDDfy();
}
[Fact]
public void should_not_add_content_headers()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheInputRequestHasMethod("POST"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheOtherContentTypeHeadersAreNotMapped())
.BDDfy();
}
private void ThenTheContentHeadersAreNotAddedToNonContentHeaders()
{
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type");
}
private void ThenTheOtherContentTypeHeadersAreNotMapped()
{
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull();
}
private void ThenTheMappedRequestHasContentDispositionHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected);
}
private void GivenTheContentDispositionIs(string input)
{
_inputRequest.Headers.Add("Content-Disposition", input);
}
private void ThenTheMappedRequestHasContentMD5Header(byte[] expected)
{
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected);
}
private void GivenTheContentMD5Is(byte[] input)
{
var base64 = Convert.ToBase64String(input);
_inputRequest.Headers.Add("Content-MD5", base64);
}
private void ThenTheMappedRequestHasContentRangeHeader()
{
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
_mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2);
}
private void GivenTheContentRangeIs(string input)
{
_inputRequest.Headers.Add("Content-Range", input);
}
private void ThenTheMappedRequestHasContentLocationHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected);
}
private void GivenTheContentLocationIs(string input)
{
_inputRequest.Headers.Add("Content-Location", input);
}
private void ThenTheMappedRequestHasContentLanguageHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected);
}
private void GivenTheContentLanguageIs(string input)
{
_inputRequest.Headers.Add("Content-Language", input);
}
private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo)
{
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo);
}
private void GivenTheContentEncodingIs(string input)
{
_inputRequest.Headers.Add("Content-Encoding", input);
}
private void GivenTheContentTypeIs(string contentType)
{
_inputRequest.ContentType = contentType;
}
private void ThenTheMappedRequestHasContentTypeHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
}
private void ThenTheMappedRequestHasContentSize(long expected)
{
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
}
private void GivenTheInputRequestHasMethod(string method)
{
_inputRequest.Method = method;
}
private void GivenTheInputRequestHasScheme(string scheme)
{
_inputRequest.Scheme = scheme;
}
private void GivenTheInputRequestHasHost(string host)
{
_inputRequest.Host = new HostString(host);
}
private void GivenTheInputRequestHasPath(string path)
{
if (path != null)
{
_inputRequest.Path = path;
}
}
private void GivenTheInputRequestHasQueryString(string querystring)
{
if (querystring != null)
{
_inputRequest.QueryString = new QueryString(querystring);
}
}
private void GivenTheInputRequestHasAValidUri()
{
GivenTheInputRequestHasScheme("http");
GivenTheInputRequestHasHost("www.google.com");
}
private void GivenTheInputRequestHasHeaders()
{
_inputHeaders = new List<KeyValuePair<string, StringValues>>()
{
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })),
new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })),
};
foreach (var inputHeader in _inputHeaders)
{
_inputRequest.Headers.Add(inputHeader);
}
}
private void GivenTheInputRequestHasNoHeaders()
{
_inputRequest.Headers.Clear();
}
private void GivenTheInputRequestHasContent(string content)
{
_inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
}
private void GivenTheInputRequestHasNullContent()
{
_inputRequest.Body = null;
}
private async Task WhenMapped()
{
_mappedRequest = await _requestMapper.Map(_inputRequest);
}
private void ThenNoErrorIsReturned()
{
_mappedRequest.IsError.ShouldBeFalse();
}
private void ThenAnErrorIsReturned()
{
_mappedRequest.IsError.ShouldBeTrue();
}
private void ThenTheMappedRequestHasUri(string expectedUri)
{
_mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri);
}
private void ThenTheMappedRequestHasMethod(string expectedMethod)
{
_mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod);
}
private void ThenTheMappedRequestHasEachHeader()
{
_mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count);
foreach(var header in _mappedRequest.Data.Headers)
{
var inputHeader = _inputHeaders.First(h => h.Key == header.Key);
inputHeader.ShouldNotBeNull();
inputHeader.Value.Count().ShouldBe(header.Value.Count());
foreach(var inputHeaderValue in inputHeader.Value)
{
header.Value.Any(v => v == inputHeaderValue);
}
}
}
private void ThenTheMappedRequestHasNoHeaders()
{
_mappedRequest.Data.Headers.Count().ShouldBe(0);
}
private void ThenTheMappedRequestHasContent(string expectedContent)
{
_mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent);
}
private void ThenTheMappedRequestHasNoContent()
{
_mappedRequest.Data.Content.ShouldBeNull();
}
private void ThenTheMappedRequestIsNull()
{
_mappedRequest.Data.ShouldBeNull();
}
}
}

View File

@ -1,23 +1,30 @@
using Butterfly.Client.Tracing;
using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Requester;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
using System;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Requester;
using Shouldly;
using Xunit;
using Ocelot.Logging;
public class TracingHandlerFactoryTests
{
private TracingHandlerFactory _factory;
private Mock<IServiceTracer> _tracer;
private readonly TracingHandlerFactory _factory;
private Mock<ITracer> _tracer;
private IServiceCollection _serviceCollection;
private IServiceProvider _serviceProvider;
private Mock<IRequestScopedDataRepository> _repo;
public TracingHandlerFactoryTests()
{
_tracer = new Mock<IServiceTracer>();
_tracer = new Mock<ITracer>();
_serviceCollection = new ServiceCollection();
_serviceCollection.AddSingleton<ITracer>(_tracer.Object);
_serviceProvider = _serviceCollection.BuildServiceProvider();
_repo = new Mock<IRequestScopedDataRepository>();
_factory = new TracingHandlerFactory(_tracer.Object, _repo.Object);
_factory = new TracingHandlerFactory(_serviceProvider, _repo.Object);
}
[Fact]
@ -27,4 +34,4 @@ namespace Ocelot.UnitTests.Requester
handler.ShouldBeOfType<OcelotHttpTracingHandler>();
}
}
}
}

View File

@ -72,7 +72,6 @@ namespace Ocelot.UnitTests.Responder
[InlineData(OcelotErrorCode.UnableToFindLoadBalancerError)]
[InlineData(OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)]
[InlineData(OcelotErrorCode.UnableToFindQoSProviderError)]
[InlineData(OcelotErrorCode.UnableToSetConfigInConsulError)]
[InlineData(OcelotErrorCode.UnknownError)]
[InlineData(OcelotErrorCode.UnmappableRequestError)]
[InlineData(OcelotErrorCode.UnsupportedAuthenticationProviderError)]
@ -126,7 +125,7 @@ namespace Ocelot.UnitTests.Responder
// If this test fails then it's because the number of error codes has changed.
// You should make the appropriate changes to the test cases here to ensure
// they cover all the error codes, and then modify this assertion.
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(35, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(34, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
}
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)

View File

@ -1,249 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Infrastructure.Consul;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery.Configuration;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
namespace Ocelot.UnitTests.ServiceDiscovery
{
public class ConsulServiceDiscoveryProviderTests : IDisposable
{
private IWebHost _fakeConsulBuilder;
private readonly List<ServiceEntry> _serviceEntries;
private ConsulServiceDiscoveryProvider _provider;
private readonly string _serviceName;
private readonly int _port;
private readonly string _consulHost;
private readonly string _fakeConsulServiceDiscoveryUrl;
private List<Service> _services;
private readonly Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IOcelotLogger> _logger;
private string _receivedToken;
private IConsulClientFactory _clientFactory;
public ConsulServiceDiscoveryProviderTests()
{
_serviceName = "test";
_port = 8500;
_consulHost = "localhost";
_fakeConsulServiceDiscoveryUrl = $"http://{_consulHost}:{_port}";
_serviceEntries = new List<ServiceEntry>();
_factory = new Mock<IOcelotLoggerFactory>();
_clientFactory = new ConsulClientFactory();
_logger = new Mock<IOcelotLogger>();
_factory.Setup(x => x.CreateLogger<ConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName, null);
_provider = new ConsulServiceDiscoveryProvider(config, _factory.Object, _clientFactory);
}
[Fact]
public void should_return_service_from_consul()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x =>GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(1))
.BDDfy();
}
[Fact]
public void should_use_token()
{
var token = "test token";
var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName, token);
_provider = new ConsulServiceDiscoveryProvider(config, _factory.Object, _clientFactory);
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.When(_ => WhenIGetTheServices())
.Then(_ => ThenTheCountIs(1))
.And(_ => _receivedToken.ShouldBe(token))
.BDDfy();
}
[Fact]
public void should_not_return_services_with_invalid_address()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "http://localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "http://localhost",
Port = 50888,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(0))
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress())
.BDDfy();
}
[Fact]
public void should_not_return_services_with_invalid_port()
{
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = -1,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = _serviceName,
Address = "localhost",
Port = 0,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.When(x => WhenIGetTheServices())
.Then(x => ThenTheCountIs(0))
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts())
.BDDfy();
}
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()
{
_logger.Verify(
x => x.LogWarning(
"Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
_logger.Verify(
x => x.LogWarning(
"Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
}
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()
{
_logger.Verify(
x => x.LogWarning(
"Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
_logger.Verify(
x => x.LogWarning(
"Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
Times.Once);
}
private void ThenTheCountIs(int count)
{
_services.Count.ShouldBe(count);
}
private void WhenIGetTheServices()
{
_services = _provider.Get().GetAwaiter().GetResult();
}
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
{
foreach (var serviceEntry in serviceEntries)
{
_serviceEntries.Add(serviceEntry);
}
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
{
_fakeConsulBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
{
if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values))
{
_receivedToken = values.First();
}
await context.Response.WriteJsonAsync(_serviceEntries);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
public void Dispose()
{
_fakeConsulBuilder?.Dispose();
}
}
}

View File

@ -1,118 +0,0 @@
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using Ocelot.ServiceDiscovery.Providers;
using Pivotal.Discovery.Client;
using Shouldly;
using Steeltoe.Common.Discovery;
using TestStack.BDDfy;
using Values;
using Xunit;
public class EurekaServiceDiscoveryProviderTests
{
private readonly EurekaServiceDiscoveryProvider _provider;
private readonly Mock<IDiscoveryClient> _client;
private readonly string _serviceId;
private List<IServiceInstance> _instances;
private List<Service> _result;
public EurekaServiceDiscoveryProviderTests()
{
_serviceId = "Laura";
_client = new Mock<IDiscoveryClient>();
_provider = new EurekaServiceDiscoveryProvider(_serviceId, _client.Object);
}
[Fact]
public void should_return_empty_services()
{
this.When(_ => WhenIGet())
.Then(_ => ThenTheCountIs(0))
.BDDfy();
}
[Fact]
public void should_return_service_from_client()
{
var instances = new List<IServiceInstance>
{
new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary<string, string>())
};
this.Given(_ => GivenThe(instances))
.When(_ => WhenIGet())
.Then(_ => ThenTheCountIs(1))
.And(_ => ThenTheClientIsCalledCorrectly())
.And(_ => ThenTheServiceIsMapped())
.BDDfy();
}
[Fact]
public void should_return_services_from_client()
{
var instances = new List<IServiceInstance>
{
new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary<string, string>()),
new EurekaService(_serviceId, "somehost", 801, false, new Uri("http://somehost:801"), new Dictionary<string, string>())
};
this.Given(_ => GivenThe(instances))
.When(_ => WhenIGet())
.Then(_ => ThenTheCountIs(2))
.And(_ => ThenTheClientIsCalledCorrectly())
.BDDfy();
}
private void ThenTheServiceIsMapped()
{
_result[0].HostAndPort.DownstreamHost.ShouldBe("somehost");
_result[0].HostAndPort.DownstreamPort.ShouldBe(801);
_result[0].Name.ShouldBe(_serviceId);
}
private void ThenTheCountIs(int expected)
{
_result.Count.ShouldBe(expected);
}
private void ThenTheClientIsCalledCorrectly()
{
_client.Verify(x => x.GetInstances(_serviceId), Times.Once);
}
private async Task WhenIGet()
{
_result = await _provider.Get();
}
private void GivenThe(List<IServiceInstance> instances)
{
_instances = instances;
_client.Setup(x => x.GetInstances(It.IsAny<string>())).Returns(instances);
}
}
public class EurekaService : IServiceInstance
{
public EurekaService(string serviceId, string host, int port, bool isSecure, Uri uri, IDictionary<string, string> metadata)
{
ServiceId = serviceId;
Host = host;
Port = port;
IsSecure = isSecure;
Uri = uri;
Metadata = metadata;
}
public string ServiceId { get; }
public string Host { get; }
public int Port { get; }
public bool IsSecure { get; }
public Uri Uri { get; }
public IDictionary<string, string> Metadata { get; }
}
}

View File

@ -1,80 +0,0 @@
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
using static Ocelot.Infrastructure.Wait;
public class PollingConsulServiceDiscoveryProviderTests
{
private readonly int _delay;
private PollingConsulServiceDiscoveryProvider _provider;
private readonly List<Service> _services;
private readonly Mock<IOcelotLoggerFactory> _factory;
private readonly Mock<IOcelotLogger> _logger;
private readonly Mock<IServiceDiscoveryProvider> _consulServiceDiscoveryProvider;
private List<Service> _result;
public PollingConsulServiceDiscoveryProviderTests()
{
_services = new List<Service>();
_delay = 1;
_factory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_factory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
_consulServiceDiscoveryProvider = new Mock<IServiceDiscoveryProvider>();
}
[Fact]
public void should_return_service_from_consul()
{
var service = new Service("", new ServiceHostAndPort("", 0), "", "", new List<string>());
this.Given(x => GivenConsulReturns(service))
.When(x => WhenIGetTheServices(1))
.Then(x => ThenTheCountIs(1))
.BDDfy();
}
private void GivenConsulReturns(Service service)
{
_services.Add(service);
_consulServiceDiscoveryProvider.Setup(x => x.Get()).ReturnsAsync(_services);
}
private void ThenTheCountIs(int count)
{
_result.Count.ShouldBe(count);
}
private void WhenIGetTheServices(int expected)
{
_provider = new PollingConsulServiceDiscoveryProvider(_delay, "", _factory.Object, _consulServiceDiscoveryProvider.Object);
var result = WaitFor(3000).Until(() => {
try
{
_result = _provider.Get().GetAwaiter().GetResult();
if(_result.Count == expected)
{
return true;
}
return false;
}
catch(Exception)
{
return false;
}
});
result.ShouldBeTrue();
}
}
}

View File

@ -1,23 +1,12 @@
using Ocelot.ServiceDiscovery.Configuration;
using Ocelot.ServiceDiscovery.Providers;
namespace Ocelot.UnitTests.ServiceDiscovery
namespace Ocelot.UnitTests.ServiceDiscovery
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.Values;
using Xunit;
using TestStack.BDDfy;
using Shouldly;
using Ocelot.ServiceDiscovery.Configuration;
using Ocelot.ServiceDiscovery.Providers;
public class ServiceFabricServiceDiscoveryProviderTests
{

View File

@ -1,39 +1,38 @@
using System;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Infrastructure.Consul;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Providers;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.ServiceDiscovery
{
using Pivotal.Discovery.Client;
using Steeltoe.Common.Discovery;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Values;
using System.Collections.Generic;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Logging;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Providers;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ServiceProviderFactoryTests
{
private ServiceProviderConfiguration _serviceConfig;
private IServiceDiscoveryProvider _result;
private readonly ServiceDiscoveryProviderFactory _factory;
private ServiceDiscoveryProviderFactory _factory;
private DownstreamReRoute _reRoute;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IDiscoveryClient> _discoveryClient;
private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
private IServiceProvider _provider;
private readonly IServiceCollection _collection;
public ServiceProviderFactoryTests()
{
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<PollingConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
_discoveryClient = new Mock<IDiscoveryClient>();
var consulClient = new Mock<IConsulClientFactory>();
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, consulClient.Object, _discoveryClient.Object);
_collection = new ServiceCollection();
_provider = _collection.BuildServiceProvider();
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider);
}
[Fact]
@ -72,7 +71,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
}
[Fact]
public void should_return_consul_service_provider()
public void should_call_delegate()
{
var reRoute = new DownstreamReRouteBuilder()
.WithServiceName("product")
@ -83,27 +82,9 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.And(x => GivenAFakeDelegate())
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<ConsulServiceDiscoveryProvider>())
.BDDfy();
}
[Fact]
public void should_return_polling_consul_service_provider()
{
var reRoute = new DownstreamReRouteBuilder()
.WithServiceName("product")
.WithUseServiceDiscovery(true)
.Build();
var serviceConfig = new ServiceProviderConfigurationBuilder()
.WithType("PollConsul")
.WithPollingInterval(100000)
.Build();
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<PollingConsulServiceDiscoveryProvider>())
.Then(x => x.ThenTheDelegateIsCalled())
.BDDfy();
}
@ -125,22 +106,25 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.BDDfy();
}
[Fact]
public void should_return_eureka_provider()
private void GivenAFakeDelegate()
{
var reRoute = new DownstreamReRouteBuilder()
.WithServiceName("product")
.WithUseServiceDiscovery(true)
.Build();
ServiceDiscoveryFinderDelegate fake = (provider, config, name) => new Fake();
_collection.AddSingleton(fake);
_provider = _collection.BuildServiceProvider();
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider);
}
var serviceConfig = new ServiceProviderConfigurationBuilder()
.WithType("Eureka")
.Build();
class Fake : IServiceDiscoveryProvider
{
public Task<List<Service>> Get()
{
return null;
}
}
this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute))
.When(x => x.WhenIGetTheServiceProvider())
.Then(x => x.ThenTheServiceProviderIs<EurekaServiceDiscoveryProvider>())
.BDDfy();
private void ThenTheDelegateIsCalled()
{
_result.GetType().Name.ShouldBe("Fake");
}
private void ThenTheFollowingServicesAreReturned(List<DownstreamHostAndPort> downstreamAddresses)