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

@ -20,14 +20,14 @@ namespace Ocelot.AcceptanceTests
{
public class AggregateTests : IDisposable
{
private IWebHost _serviceOneBuilder;
private IWebHost _serviceTwoBuilder;
private readonly Steps _steps;
private string _downstreamPathOne;
private string _downstreamPathTwo;
private readonly ServiceHandler _serviceHandler;
public AggregateTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -116,7 +116,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51885,
Port = 51875,
}
},
UpstreamPathTemplate = "/laura",
@ -157,7 +157,7 @@ namespace Ocelot.AcceptanceTests
var expected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51885", "/", 200, "{Hello from Laura}"))
this.Given(x => x.GivenServiceOneIsRunning("http://localhost:51875", "/", 200, "{Hello from Laura}"))
.Given(x => x.GivenServiceTwoIsRunning("http://localhost:51886", "/", 200, "{Hello from Tom}"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
@ -370,64 +370,40 @@ namespace Ocelot.AcceptanceTests
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
{
_serviceOneBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPathOne != basePath)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathOne != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_serviceOneBuilder.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
{
_serviceTwoBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPathTwo != basePath)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathTwo != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_serviceTwoBuilder.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
@ -438,8 +414,7 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_serviceOneBuilder?.Dispose();
_serviceTwoBuilder?.Dispose();
_serviceHandler.Dispose();
_steps.Dispose();
}
}

View File

@ -1,37 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using IdentityServer4.Test;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class AuthenticationTests : IDisposable
{
private IWebHost _servicebuilder;
private readonly Steps _steps;
private IWebHost _identityServerBuilder;
private string _identityServerRootUrl = "http://localhost:51888";
private string _downstreamServicePath = "/";
private string _downstreamServiceHost = "localhost";
private int _downstreamServicePort = 51876;
private string _downstreamServiceScheme = "http";
private string _downstreamServiceUrl = "http://localhost:51876";
private string _downstreamServiceUrl = "http://localhost:";
private readonly Action<IdentityServerAuthenticationOptions> _options;
private readonly ServiceHandler _serviceHandler;
public AuthenticationTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
_options = o =>
{
@ -46,6 +45,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_401_using_identity_server_access_token()
{
int port = 54329;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -58,7 +59,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
Port = port,
}
},
DownstreamScheme = _downstreamServiceScheme,
@ -73,7 +74,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
.And(x => _steps.GivenThePostHasContent("postContent"))
@ -85,7 +86,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_using_identity_server()
{
var configuration = new FileConfiguration
int port = 54099;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -97,7 +100,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
Port = port,
}
},
DownstreamScheme = _downstreamServiceScheme,
@ -112,7 +115,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -126,7 +129,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
{
var configuration = new FileConfiguration
int port = 54196;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -138,7 +143,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
Port = port,
}
},
DownstreamScheme = _downstreamServiceScheme,
@ -153,7 +158,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -166,7 +171,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_201_using_identity_server_access_token()
{
var configuration = new FileConfiguration
int port = 52226;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -178,7 +185,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
Port = port,
}
},
DownstreamScheme = _downstreamServiceScheme,
@ -193,7 +200,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -207,7 +214,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_201_using_identity_server_reference_token()
{
var configuration = new FileConfiguration
int port = 52222;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -219,7 +228,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host =_downstreamServiceHost,
Port = _downstreamServicePort,
Port = port,
}
},
DownstreamScheme = _downstreamServiceScheme,
@ -234,7 +243,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => x.GivenThereIsAServiceRunningOn($"{_downstreamServiceUrl}{port}", 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -247,23 +256,11 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
{
_servicebuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_servicebuilder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType)
@ -371,7 +368,7 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_servicebuilder?.Dispose();
_serviceHandler.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}

View File

@ -1,33 +1,32 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using IdentityServer4;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
using IdentityServer4.Test;
public class AuthorisationTests : IDisposable
{
private IWebHost _servicebuilder;
private IWebHost _identityServerBuilder;
private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options;
private readonly Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl = "http://localhost:51888";
private readonly ServiceHandler _serviceHandler;
public AuthorisationTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
_options = o =>
{
@ -42,8 +41,10 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_authorising_route()
{
var configuration = new FileConfiguration
{
int port = 52875;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
@ -54,7 +55,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -83,10 +84,10 @@ namespace Ocelot.AcceptanceTests
}
}
}
};
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -100,7 +101,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_403_authorising_route()
{
var configuration = new FileConfiguration
int port = 59471;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -112,7 +115,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -143,7 +146,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -156,7 +159,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_using_identity_server_with_allowed_scope()
{
var configuration = new FileConfiguration
int port = 63471;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -168,7 +173,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -184,7 +189,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -197,7 +202,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_403_using_identity_server_with_scope_not_allowed()
{
var configuration = new FileConfiguration
int port = 60571;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -209,7 +216,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -225,7 +232,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -238,7 +245,9 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_fix_issue_240()
{
var configuration = new FileConfiguration
int port = 61071;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
@ -250,7 +259,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -284,7 +293,7 @@ namespace Ocelot.AcceptanceTests
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt, users))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning(_options, "Test"))
@ -297,23 +306,11 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)
{
_servicebuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_servicebuilder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType)
@ -465,7 +462,7 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_servicebuilder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}

View File

@ -1,284 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Butterfly.Client.AspNetCore;
using static Rafty.Infrastructure.Wait;
namespace Ocelot.AcceptanceTests
{
using Xunit.Abstractions;
public class ButterflyTracingTests : IDisposable
{
private IWebHost _serviceOneBuilder;
private IWebHost _serviceTwoBuilder;
private IWebHost _fakeButterfly;
private readonly Steps _steps;
private string _downstreamPathOne;
private string _downstreamPathTwo;
private int _butterflyCalled;
private readonly ITestOutputHelper _output;
public ButterflyTracingTests(ITestOutputHelper output)
{
_output = output;
_steps = new Steps();
}
[Fact]
public void should_forward_tracing_information_from_ocelot_and_downstream_services()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51887,
}
},
UpstreamPathTemplate = "/api001/values",
UpstreamHttpMethod = new List<string> { "Get" },
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
},
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 3,
DurationOfBreak = 10,
TimeoutValue = 5000
}
},
new FileReRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51388,
}
},
UpstreamPathTemplate = "/api002/values",
UpstreamHttpMethod = new List<string> { "Get" },
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
},
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 3,
DurationOfBreak = 10,
TimeoutValue = 5000
}
}
}
};
var butterflyUrl = "http://localhost:9618";
this.Given(x => GivenFakeButterfly(butterflyUrl))
.And(x => GivenServiceOneIsRunning("http://localhost:51887", "/api/values", 200, "Hello from Laura", butterflyUrl))
.And(x => GivenServiceTwoIsRunning("http://localhost:51388", "/api/values", 200, "Hello from Tom", butterflyUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api002/values"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.BDDfy();
var commandOnAllStateMachines = WaitFor(10000).Until(() => _butterflyCalled >= 4);
_output.WriteLine($"_butterflyCalled is {_butterflyCalled}");
commandOnAllStateMachines.ShouldBeTrue();
}
[Fact]
public void should_return_tracing_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/values",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51387,
}
},
UpstreamPathTemplate = "/api001/values",
UpstreamHttpMethod = new List<string> { "Get" },
HttpHandlerOptions = new FileHttpHandlerOptions
{
UseTracing = true
},
QoSOptions = new FileQoSOptions
{
ExceptionsAllowedBeforeBreaking = 3,
DurationOfBreak = 10,
TimeoutValue = 5000
},
DownstreamHeaderTransform = new Dictionary<string, string>()
{
{"Trace-Id", "{TraceId}"},
{"Tom", "Laura"}
}
}
}
};
var butterflyUrl = "http://localhost:9618";
this.Given(x => GivenFakeButterfly(butterflyUrl))
.And(x => GivenServiceOneIsRunning("http://localhost:51387", "/api/values", 200, "Hello from Laura", butterflyUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.ThenTheTraceHeaderIsSet("Trace-Id"))
.And(x => _steps.ThenTheResponseHeaderIs("Tom", "Laura"))
.BDDfy();
}
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
{
_serviceOneBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.ConfigureServices(services => {
services.AddButterfly(option =>
{
option.CollectorUrl = butterflyUrl;
option.Service = "Service One";
option.IgnoredRoutesRegexPatterns = new string[0];
});
})
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathOne != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_serviceOneBuilder.Start();
}
private void GivenFakeButterfly(string baseUrl)
{
_fakeButterfly = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.Run(async context =>
{
_butterflyCalled++;
await context.Response.WriteAsync("OK...");
});
})
.Build();
_fakeButterfly.Start();
}
private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
{
_serviceTwoBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.ConfigureServices(services => {
services.AddButterfly(option =>
{
option.CollectorUrl = butterflyUrl;
option.Service = "Service Two";
option.IgnoredRoutesRegexPatterns = new string[0];
});
})
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathTwo != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_serviceTwoBuilder.Start();
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPathOne, string expectedDownstreamPath)
{
_downstreamPathOne.ShouldBe(expectedDownstreamPathOne);
_downstreamPathTwo.ShouldBe(expectedDownstreamPath);
}
public void Dispose()
{
_serviceOneBuilder?.Dispose();
_serviceTwoBuilder?.Dispose();
_fakeButterfly?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,137 +0,0 @@
using CacheManager.Core;
using CacheManager.Core.Internal;
using CacheManager.Core.Logging;
using System;
using System.Collections.Concurrent;
using System.Linq;
using static CacheManager.Core.Utility.Guard;
namespace Ocelot.AcceptanceTests.Caching
{
public class InMemoryJsonHandle<TCacheValue> : BaseCacheHandle<TCacheValue>
{
private readonly ICacheSerializer _serializer;
private readonly ConcurrentDictionary<string, Tuple<Type, byte[]>> _cache;
public InMemoryJsonHandle(
ICacheManagerConfiguration managerConfiguration,
CacheHandleConfiguration configuration,
ICacheSerializer serializer,
ILoggerFactory loggerFactory) : base(managerConfiguration, configuration)
{
_cache = new ConcurrentDictionary<string, Tuple<Type, byte[]>>();
_serializer = serializer;
Logger = loggerFactory.CreateLogger(this);
}
public override int Count => _cache.Count;
protected override ILogger Logger { get; }
public override void Clear() => _cache.Clear();
public override void ClearRegion(string region)
{
NotNullOrWhiteSpace(region, nameof(region));
var key = string.Concat(region, ":");
foreach (var item in _cache.Where(p => p.Key.StartsWith(key, StringComparison.OrdinalIgnoreCase)))
{
_cache.TryRemove(item.Key, out Tuple<Type, byte[]> val);
}
}
public override bool Exists(string key)
{
NotNullOrWhiteSpace(key, nameof(key));
return _cache.ContainsKey(key);
}
public override bool Exists(string key, string region)
{
NotNullOrWhiteSpace(region, nameof(region));
var fullKey = GetKey(key, region);
return _cache.ContainsKey(fullKey);
}
protected override bool AddInternalPrepared(CacheItem<TCacheValue> item)
{
NotNull(item, nameof(item));
var key = GetKey(item.Key, item.Region);
var serializedItem = _serializer.SerializeCacheItem(item);
return _cache.TryAdd(key, new Tuple<Type, byte[]>(item.Value.GetType(), serializedItem));
}
protected override CacheItem<TCacheValue> GetCacheItemInternal(string key) => GetCacheItemInternal(key, null);
protected override CacheItem<TCacheValue> GetCacheItemInternal(string key, string region)
{
var fullKey = GetKey(key, region);
CacheItem<TCacheValue> deserializedResult = null;
if (_cache.TryGetValue(fullKey, out Tuple<Type, byte[]> result))
{
deserializedResult = _serializer.DeserializeCacheItem<TCacheValue>(result.Item2, result.Item1);
if (deserializedResult.ExpirationMode != ExpirationMode.None && IsExpired(deserializedResult, DateTime.UtcNow))
{
_cache.TryRemove(fullKey, out Tuple<Type, byte[]> removeResult);
TriggerCacheSpecificRemove(key, region, CacheItemRemovedReason.Expired, deserializedResult.Value);
return null;
}
}
return deserializedResult;
}
protected override void PutInternalPrepared(CacheItem<TCacheValue> item)
{
NotNull(item, nameof(item));
var serializedItem = _serializer.SerializeCacheItem<TCacheValue>(item);
_cache[GetKey(item.Key, item.Region)] = new Tuple<Type, byte[]>(item.Value.GetType(), serializedItem);
}
protected override bool RemoveInternal(string key) => RemoveInternal(key, null);
protected override bool RemoveInternal(string key, string region)
{
var fullKey = GetKey(key, region);
return _cache.TryRemove(fullKey, out Tuple<Type, byte[]> val);
}
private static string GetKey(string key, string region)
{
NotNullOrWhiteSpace(key, nameof(key));
if (string.IsNullOrWhiteSpace(region))
{
return key;
}
return string.Concat(region, ":", key);
}
private static bool IsExpired(CacheItem<TCacheValue> item, DateTime now)
{
if (item.ExpirationMode == ExpirationMode.Absolute
&& item.CreatedUtc.Add(item.ExpirationTimeout) < now)
{
return true;
}
else if (item.ExpirationMode == ExpirationMode.Sliding
&& item.LastAccessedUtc.Add(item.ExpirationTimeout) < now)
{
return true;
}
return false;
}
}
}

View File

@ -1,239 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class CachingTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
public CachingTests()
{
_steps = new Steps();
}
[Fact]
public void should_return_cached_response()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 100
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.ThenTheContentLengthIs(16))
.BDDfy();
}
[Fact]
public void should_return_cached_response_with_expires_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52839,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 100
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52839", 200, "Hello from Laura", "Expires", "-1"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:52839", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => _steps.ThenTheContentLengthIs(16))
.And(x => _steps.ThenTheResponseBodyHeaderIs("Expires", "-1"))
.BDDfy();
}
[Fact]
public void should_return_cached_response_when_using_jsonserialized_cache()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 100
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingJsonSerializedCache())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_not_return_cached_response_as_ttl_expires()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51899,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 1
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", 200, "Hello from Laura", null, null))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.Given(x => x.GivenTheServiceNowReturns("http://localhost:51899", 200, "Hello from Tom"))
.And(x => x.GivenTheCacheExpires())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom"))
.BDDfy();
}
private void GivenTheCacheExpires()
{
Thread.Sleep(1000);
}
private void GivenTheServiceNowReturns(string url, int statusCode, string responseBody)
{
_builder.Dispose();
GivenThereIsAServiceRunningOn(url, statusCode, responseBody, null, null);
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, string key, string value)
{
_builder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if(!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(key))
{
context.Response.Headers.Add(key, value);
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,23 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class CaseSensitiveRoutingTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public CaseSensitiveRoutingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -226,29 +224,16 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(baseUrl)
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_builder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,35 +1,35 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.AcceptanceTests
{
using IdentityServer4;
using IdentityServer4.Test;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Claims;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
public class ClaimsToHeadersForwardingTests : IDisposable
{
private IWebHost _servicebuilder;
private IWebHost _identityServerBuilder;
private readonly Steps _steps;
private Action<IdentityServerAuthenticationOptions> _options;
private string _identityServerRootUrl = "http://localhost:52888";
private readonly ServiceHandler _serviceHandler;
public ClaimsToHeadersForwardingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
_options = o =>
{
@ -107,29 +107,17 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url, int statusCode)
{
_servicebuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
var customerId = context.Request.Headers.First(x => x.Key == "CustomerId").Value.First();
var locationId = context.Request.Headers.First(x => x.Key == "LocationId").Value.First();
var userType = context.Request.Headers.First(x => x.Key == "UserType").Value.First();
var userId = context.Request.Headers.First(x => x.Key == "UserId").Value.First();
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_servicebuilder.Start();
var responseBody = $"CustomerId: {customerId} LocationId: {locationId} UserType: {userType} UserId: {userId}";
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user)
@ -203,7 +191,7 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_servicebuilder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
_identityServerBuilder?.Dispose();
}

View File

@ -1,36 +1,25 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestStack.BDDfy;
using Xunit;
public class ClientRateLimitTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private int _counterOne;
private int _counterOne;
private readonly ServiceHandler _serviceHandler;
public ClientRateLimitTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
[Fact]
public void should_call_withratelimiting()
{
@ -53,7 +42,6 @@ namespace Ocelot.AcceptanceTests
UpstreamPathTemplate = "/api/ClientRateLimit",
UpstreamHttpMethod = new List<string> { "Get" },
RequestIdKey = _steps.RequestIdKey,
RateLimitOptions = new FileRateLimitRule()
{
EnableRateLimiting = true,
@ -158,6 +146,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_call_middleware_withWhitelistClient()
{
int port = 61876;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -170,7 +160,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51876,
Port = port,
}
},
DownstreamScheme = "http",
@ -201,7 +191,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", "/api/ClientRateLimit"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4))
@ -211,26 +201,18 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(baseUrl)
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(context =>
{
_counterOne++;
context.Response.StatusCode = 200;
context.Response.WriteAsync(_counterOne.ToString());
return Task.CompletedTask;
});
})
.Build();
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
{
_counterOne++;
context.Response.StatusCode = 200;
context.Response.WriteAsync(_counterOne.ToString());
return Task.CompletedTask;
});
}
_builder.Start();
public void Dispose()
{
_steps.Dispose();
}
}
}

View File

@ -1,390 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using static Ocelot.Infrastructure.Wait;
namespace Ocelot.AcceptanceTests
{
public class ConfigurationInConsulTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private IWebHost _fakeConsulBuilder;
private FileConfiguration _config;
public ConfigurationInConsulTests()
{
_steps = new Steps();
}
[Fact]
public void should_return_response_200_with_simple_url()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = 9500
}
}
};
var fakeConsulServiceDiscoveryUrl = "http://localhost:9500";
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = 9502
}
}
};
var fakeConsulServiceDiscoveryUrl = "http://localhost:9502";
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_load_configuration_out_of_consul()
{
var consulPort = 8500;
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var consulConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/status",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51779,
}
},
UpstreamPathTemplate = "/cs/status",
UpstreamHttpMethod = new List<string> {"Get"}
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51779", "/status", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_load_configuration_out_of_consul_if_it_is_changed()
{
var consulPort = 8506;
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var consulConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/status",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51780,
}
},
UpstreamPathTemplate = "/cs/status",
UpstreamHttpMethod = new List<string> {"Get"}
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
var secondConsulConfig = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/status",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51780,
}
},
UpstreamPathTemplate = "/cs/status/awesome",
UpstreamHttpMethod = new List<string> {"Get"}
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => GivenTheConsulConfigurationIs(consulConfig))
.And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51780", "/status", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig())
.And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status"))
.And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.When(x => GivenTheConsulConfigurationIs(secondConsulConfig))
.Then(x => ThenTheConfigIsUpdatedInOcelot())
.BDDfy();
}
private void ThenTheConfigIsUpdatedInOcelot()
{
var result = WaitFor(20000).Until(() => {
try
{
_steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome");
_steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK);
_steps.ThenTheResponseBodyShouldBe("Hello from Laura");
return true;
}
catch (Exception)
{
return false;
}
});
result.ShouldBeTrue();
}
private void GivenTheConsulConfigurationIs(FileConfiguration config)
{
_config = config;
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
{
_fakeConsulBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
{
var json = JsonConvert.SerializeObject(_config);
var bytes = Encoding.UTF8.GetBytes(json);
var base64 = Convert.ToBase64String(bytes);
var kvp = new FakeConsulGetResponse(base64);
await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp });
}
else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration")
{
try
{
var reader = new StreamReader(context.Request.Body);
var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<FileConfiguration>(json);
var response = JsonConvert.SerializeObject(true);
await context.Response.WriteAsync(response);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
public class FakeConsulGetResponse
{
public FakeConsulGetResponse(string value)
{
Value = value;
}
public int CreateIndex => 100;
public int ModifyIndex => 200;
public int LockIndex => 200;
public string Key => "InternalConfiguration";
public int Flags => 0;
public string Value { get; private set; }
public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e";
}
private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -0,0 +1,68 @@
using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter;
using Ocelot.Middleware;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Text;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class ConfigurationReloadTests : IDisposable
{
private FileConfiguration _initialConfig;
private FileConfiguration _anotherConfig;
private Steps _steps;
public ConfigurationReloadTests()
{
_steps = new Steps();
_initialConfig = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
RequestIdKey = "initialKey"
}
};
_anotherConfig = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
RequestIdKey = "someOtherKey"
}
};
}
[Fact]
public void should_reload_config_on_change()
{
this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig))
.And(x => _steps.GivenOcelotIsRunningReloadingConfig(true))
.And(x => _steps.GivenThereIsAConfiguration(_anotherConfig))
.And(x => _steps.GivenIWait(2500))
.And(x => _steps.ThenConfigShouldBe(_anotherConfig))
.BDDfy();
}
[Fact]
public void should_not_reload_config_on_change()
{
this.Given(x => _steps.GivenThereIsAConfiguration(_initialConfig))
.And(x => _steps.GivenOcelotIsRunningReloadingConfig(false))
.And(x => _steps.GivenThereIsAConfiguration(_anotherConfig))
.And(x => _steps.GivenIWait(2500))
.And(x => _steps.ThenConfigShouldBe(_initialConfig))
.BDDfy();
}
public void Dispose()
{
_steps.Dispose();
}
}
}

View File

@ -0,0 +1,177 @@
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ContentTests : IDisposable
{
private readonly Steps _steps;
private string _contentType;
private long? _contentLength;
private bool _contentTypeHeaderExists;
private readonly ServiceHandler _serviceHandler;
public ContentTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_not_add_content_type_or_content_length_headers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51339,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheContentTypeShouldBeEmpty())
.And(x => ThenTheContentLengthShouldBeEmpty())
.BDDfy();
}
[Fact]
public void should_add_content_type_and_content_length_headers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51349,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
var contentType = "application/json";
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51349", "/", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.And(x => _steps.GivenThePostHasContentType(contentType))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.And(x => ThenTheContentLengthIs(11))
.And(x => ThenTheContentTypeIsIs(contentType))
.BDDfy();
}
[Fact]
public void should_add_default_content_type_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51359,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51359", "/", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.And(x => ThenTheContentLengthIs(11))
.And(x => ThenTheContentTypeIsIs("text/plain; charset=utf-8"))
.BDDfy();
}
private void ThenTheContentTypeIsIs(string expected)
{
_contentType.ShouldBe(expected);
}
private void ThenTheContentLengthShouldBeEmpty()
{
_contentLength.ShouldBeNull();
}
private void ThenTheContentLengthIs(int expected)
{
_contentLength.ShouldBe(expected);
}
private void ThenTheContentTypeShouldBeEmpty()
{
_contentType.ShouldBeNullOrEmpty();
_contentTypeHeaderExists.ShouldBe(false);
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_contentType = context.Request.ContentType;
_contentLength = context.Request.ContentLength;
_contentTypeHeaderExists = context.Request.Headers.TryGetValue("Content-Type", out var value);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,29 +1,27 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Ocelot.Middleware;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Ocelot.Middleware;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class CustomMiddlewareTests : IDisposable
{
private readonly string _configurationPath;
private IWebHost _builder;
private readonly Steps _steps;
private int _counter;
private readonly ServiceHandler _serviceHandler;
public CustomMiddlewareTests()
{
_serviceHandler = new ServiceHandler();
_counter = 0;
_steps = new Steps();
_configurationPath = "ocelot.json";
@ -340,37 +338,24 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string basePath)
{
_builder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, context =>
{
if (string.IsNullOrEmpty(basePath))
{
app.UsePathBase(basePath);
app.Run(context =>
{
if(string.IsNullOrEmpty(basePath))
{
context.Response.StatusCode = statusCode;
}
else if(context.Request.Path.Value != basePath)
{
context.Response.StatusCode = 404;
}
context.Response.StatusCode = statusCode;
}
else if (context.Request.Path.Value != basePath)
{
context.Response.StatusCode = 404;
}
return Task.CompletedTask;
});
})
.Build();
_builder.Start();
return Task.CompletedTask;
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}

View File

@ -1,29 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class GzipTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public GzipTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -66,51 +62,40 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expected)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
if (context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding))
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding))
{
contentEncoding.First().ShouldBe("gzip");
string text = null;
using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress))
{
using (var sr = new StreamReader(decompress)) {
text = sr.ReadToEnd();
}
}
contentEncoding.First().ShouldBe("gzip");
if(text != expected)
{
throw new Exception("not gzipped");
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
string text = null;
using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress))
{
using (var sr = new StreamReader(decompress))
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
text = sr.ReadToEnd();
}
});
})
.Build();
}
_builder.Start();
if (text != expected)
{
throw new Exception("not gzipped");
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,26 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class HeaderTests : IDisposable
{
private IWebHost _builder;
private int _count;
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public HeaderTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -313,99 +311,135 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
[Fact]
public void issue_474_should_not_put_spaces_in_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Accept", "text/html,application/xhtml+xml,application/xml;"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("text/html,application/xhtml+xml,application/xml;"))
.BDDfy();
}
[Fact]
public void issue_474_should_put_spaces_in_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Accept"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIAddAHeader("Accept", "text/html"))
.And(x => _steps.GivenIAddAHeader("Accept", "application/xhtml+xml"))
.And(x => _steps.GivenIAddAHeader("Accept", "application/xml"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("text/html, application/xhtml+xml, application/xml"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
{
if (_count == 0)
{
app.UsePathBase(basePath);
app.Run(context =>
context.Response.Cookies.Append("test", "0");
_count++;
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
}
if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
{
if (cookieValue == "0" || headerValue == "test=1; path=/")
{
if (_count == 0)
{
context.Response.Cookies.Append("test", "0");
_count++;
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
}
if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue))
{
if (cookieValue == "0" || headerValue == "test=1; path=/")
{
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
}
}
context.Response.StatusCode = 500;
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
});
})
.Build();
}
}
_builder.Start();
context.Response.StatusCode = 500;
return Task.CompletedTask;
});
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
if (context.Request.Headers.TryGetValue(headerKey, out var values))
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if (context.Request.Headers.TryGetValue(headerKey, out var values))
{
var result = values.First();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result);
}
});
})
.Build();
_builder.Start();
var result = values.First();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(result);
}
});
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string headerKey, string headerValue)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context =>
{
context.Response.OnStarting(() =>
{
app.UsePathBase(basePath);
app.Run(context =>
{
context.Response.OnStarting(() =>
{
context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
});
context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode;
return Task.CompletedTask;
});
return Task.CompletedTask;
});
})
.Build();
_builder.Start();
return Task.CompletedTask;
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,28 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
public class HttpDelegatingHandlersTests
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class HttpDelegatingHandlersTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public HttpDelegatingHandlersTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -249,6 +247,7 @@ namespace Ocelot.AcceptanceTests
return base.SendAsync(request, cancellationToken);
}
}
// ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandlerAgain : DelegatingHandler
{
@ -263,33 +262,27 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
if (_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
public void Dispose()
{
_steps?.Dispose();
_serviceHandler?.Dispose();
}
}
}

View File

@ -1,36 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Ocelot.LoadBalancer.LoadBalancers;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Ocelot.LoadBalancer.LoadBalancers;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class LoadBalancerTests : IDisposable
{
private IWebHost _builderOne;
private IWebHost _builderTwo;
private readonly Steps _steps;
private int _counterOne;
private int _counterTwo;
private static readonly object _syncLock = new object();
private readonly ServiceHandler _serviceHandler;
public LoadBalancerTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_load_balance_request_with_least_connection()
{
var downstreamServiceOneUrl = "http://localhost:50881";
var downstreamServiceTwoUrl = "http://localhost:50892";
int portOne = 50591;
int portTwo = 51482;
var downstreamServiceOneUrl = $"http://localhost:{portOne}";
var downstreamServiceTwoUrl = $"http://localhost:{portTwo}";
var configuration = new FileConfiguration
{
@ -48,12 +48,12 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 50881
Port = portOne
},
new FileHostAndPort
{
Host = "localhost",
Port = 50892
Port = portTwo
}
}
}
@ -76,8 +76,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_load_balance_request_with_round_robin()
{
var downstreamPortOne = 51881;
var downstreamPortTwo = 51892;
var downstreamPortOne = 51701;
var downstreamPortTwo = 53802;
var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
@ -136,77 +136,53 @@ namespace Ocelot.AcceptanceTests
private void GivenProductServiceOneIsRunning(string url, int statusCode)
{
_builderOne = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
try
{
app.Run(async context =>
var response = string.Empty;
lock (_syncLock)
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterOne++;
response = _counterOne.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_counterOne++;
response = _counterOne.ToString();
}
_builderOne.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
}
private void GivenProductServiceTwoIsRunning(string url, int statusCode)
{
_builderTwo = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
try
{
app.Run(async context =>
var response = string.Empty;
lock (_syncLock)
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterTwo++;
response = _counterTwo.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (System.Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_counterTwo++;
response = _counterTwo.ToString();
}
_builderTwo.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
}
public void Dispose()
{
_builderOne?.Dispose();
_builderTwo?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -32,7 +32,6 @@
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CacheManager.Serialization.Json" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
@ -51,9 +50,9 @@
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="Shouldly" Version="3.0.0" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Consul" Version="0.7.2.5" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
<PackageReference Include="Rafty" Version="0.4.4" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
<PackageReference Include="IdentityServer4" Version="2.2.0" />
</ItemGroup>
</Project>

View File

@ -1,27 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class QoSTests : IDisposable
{
private IWebHost _brokenService;
private readonly Steps _steps;
private int _requestCount;
private IWebHost _workingService;
private readonly ServiceHandler _serviceHandler;
public QoSTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -227,74 +224,48 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAPossiblyBrokenServiceRunningOn(string url, string responseBody)
{
_brokenService = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
//circuit starts closed
if (_requestCount == 0)
{
app.Run(async context =>
{
//circuit starts closed
if (_requestCount == 0)
{
_requestCount++;
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
return;
}
_requestCount++;
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
return;
}
//request one times out and polly throws exception, circuit opens
if (_requestCount == 1)
{
_requestCount++;
await Task.Delay(1000);
context.Response.StatusCode = 200;
return;
}
//request one times out and polly throws exception, circuit opens
if (_requestCount == 1)
{
_requestCount++;
await Task.Delay(1000);
context.Response.StatusCode = 200;
return;
}
//after break closes we return 200 OK
if (_requestCount == 2)
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
return;
}
});
})
.Build();
_brokenService.Start();
//after break closes we return 200 OK
if (_requestCount == 2)
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync(responseBody);
}
});
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody, int timeout)
{
_workingService = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
Thread.Sleep(timeout);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_workingService.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
Thread.Sleep(timeout);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
}
public void Dispose()
{
_workingService?.Dispose();
_brokenService?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,26 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class RequestIdTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public RequestIdTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -171,30 +166,17 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url)
{
_builder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(context =>
{
StringValues requestId;
context.Request.Headers.TryGetValue(_steps.RequestIdKey, out requestId);
context.Response.Headers.Add(_steps.RequestIdKey, requestId.First());
return Task.CompletedTask;
});
})
.Build();
_builder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(url, context =>
{
context.Request.Headers.TryGetValue(_steps.RequestIdKey, out var requestId);
context.Response.Headers.Add(_steps.RequestIdKey, requestId.First());
return Task.CompletedTask;
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,25 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class ResponseCodeTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public ResponseCodeTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -58,26 +53,15 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
});
})
.Build();
_builder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
context.Response.StatusCode = statusCode;
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class ReturnsErrorTests : IDisposable
{
private IWebHost _servicebuilder;
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
public ReturnsErrorTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -55,27 +53,12 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string url)
{
_servicebuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(context =>
{
throw new Exception("BLAMMMM");
});
})
.Build();
_servicebuilder.Start();
_serviceHandler.GivenThereIsAServiceRunningOn(url, context => throw new Exception("BLAMMMM"));
}
public void Dispose()
{
_servicebuilder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,25 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class RoutingTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public RoutingTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -973,33 +971,21 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
_builder.Start();
if (_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
@ -1009,7 +995,7 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_builder?.Dispose();
_serviceHandler.Dispose();
_steps.Dispose();
}
}

View File

@ -1,25 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class RoutingWithQueryStringTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public RoutingWithQueryStringTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -208,41 +204,24 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
if (context.Request.PathBase.Value != basePath || context.Request.QueryString.Value != queryString)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(context.Request.PathBase.Value != basePath || context.Request.QueryString.Value != queryString)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
context.Response.StatusCode = 500;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,923 +0,0 @@
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Newtonsoft.Json;
using Steeltoe.Common.Discovery;
public class ServiceDiscoveryTests : IDisposable
{
private IWebHost _builderOne;
private IWebHost _builderTwo;
private IWebHost _fakeConsulBuilder;
private readonly Steps _steps;
private readonly List<ServiceEntry> _consulServices;
private readonly List<IServiceInstance> _eurekaInstances;
private int _counterOne;
private int _counterTwo;
private static readonly object SyncLock = new object();
private IWebHost _builder;
private string _downstreamPath;
private string _receivedToken;
public ServiceDiscoveryTests()
{
_steps = new Steps();
_consulServices = new List<ServiceEntry>();
_eurekaInstances = new List<IServiceInstance>();
}
[Fact]
public void should_use_eureka_service_discovery_and_make_request()
{
var eurekaPort = 8761;
var serviceName = "product";
var downstreamServicePort = 50371;
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeEurekaServiceDiscoveryUrl = $"http://localhost:{eurekaPort}";
var instanceOne = new FakeEurekaService(serviceName, "localhost", downstreamServicePort, false,
new Uri($"http://localhost:{downstreamServicePort}"), new Dictionary<string, string>());
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Type = "Eureka"
}
}
};
this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenThereIsAFakeEurekaServiceDiscoveryProvider(fakeEurekaServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithEureka(instanceOne))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(_ => _steps.ThenTheResponseBodyShouldBe(nameof(ServiceDiscoveryTests)))
.BDDfy();
}
[Fact]
public void should_use_consul_service_discovery_and_load_balance_request()
{
var consulPort = 8502;
var serviceName = "product";
var downstreamServiceOneUrl = "http://localhost:50881";
var downstreamServiceTwoUrl = "http://localhost:50882";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 50881,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 50882,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50))
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
.BDDfy();
}
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request()
{
const int consulPort = 8505;
const string serviceName = "web";
const string downstreamServiceOneUrl = "http://localhost:8080";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 8080,
ID = "web_90_0_2_224_8080",
Tags = new[] {"version-v1"}
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/home",
DownstreamScheme = "http",
UpstreamPathTemplate = "/home",
UpstreamHttpMethod = new List<string> { "Get", "Options" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/home"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes()
{
const int consulPort = 8513;
const string serviceName = "web";
const int downstreamServicePort = 8087;
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = downstreamServicePort,
ID = "web_90_0_2_224_8080",
Tags = new[] {"version-v1"}
},
};
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "localhost",
Port = consulPort
},
DownstreamScheme = "http",
HttpHandlerOptions = new FileHttpHandlerOptions
{
AllowAutoRedirect = true,
UseCookieContainer = true,
UseTracing = false
},
QoSOptions = new FileQoSOptions
{
TimeoutValue = 100,
DurationOfBreak = 1000,
ExceptionsAllowedBeforeBreaking = 1
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/web/something"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes()
{
var consulPort = 8510;
var serviceName = "product";
var serviceOnePort = 50888;
var serviceTwoPort = 50889;
var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}";
var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = serviceOnePort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = serviceTwoPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
},
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
DownstreamScheme = "http"
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50))
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26))
.BDDfy();
}
[Fact]
public void should_use_token_to_make_request_to_consul()
{
var token = "abctoken";
var consulPort = 8515;
var serviceName = "web";
var downstreamServiceOneUrl = "http://localhost:8081";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 8081,
ID = "web_90_0_2_224_8080",
Tags = new[] { "version-v1" }
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/home",
DownstreamScheme = "http",
UpstreamPathTemplate = "/home",
UpstreamHttpMethod = new List<string> { "Get", "Options" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort,
Token = token
}
}
};
this.Given(_ => GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura"))
.And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.And(_ => _steps.GivenThereIsAConfiguration(configuration))
.And(_ => _steps.GivenOcelotIsRunning())
.When(_ => _steps.WhenIGetUrlOnTheApiGateway("/home"))
.Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(_ => _receivedToken.ShouldBe(token))
.BDDfy();
}
[Fact]
public void should_send_request_to_service_after_it_becomes_available_in_consul()
{
var consulPort = 8501;
var serviceName = "product";
var downstreamServiceOneUrl = "http://localhost:50879";
var downstreamServiceTwoUrl = "http://localhost:50880";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 50879,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = 50880,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
.And(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6))
.And(x => WhenIRemoveAService(serviceEntryTwo))
.And(x => GivenIResetCounters())
.And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
.And(x => ThenOnlyOneServiceHasBeenCalled())
.And(x => WhenIAddAServiceBackIn(serviceEntryTwo))
.And(x => GivenIResetCounters())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10))
.Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10))
.And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6))
.BDDfy();
}
[Fact]
public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request()
{
const int consulPort = 8518;
const string serviceName = "web";
const int downstreamServicePort = 8082;
var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = "localhost",
Port = downstreamServicePort,
ID = $"web_90_0_2_224_{downstreamServicePort}",
Tags = new[] {"version-v1"}
},
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/home",
DownstreamScheme = "http",
UpstreamPathTemplate = "/home",
UpstreamHttpMethod = new List<string> { "Get", "Options" },
ServiceName = serviceName,
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" },
UseServiceDiscovery = true,
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort,
Type = "PollConsul",
PollingInterval = 0
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura"))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo)
{
_consulServices.Add(serviceEntryTwo);
}
private void ThenOnlyOneServiceHasBeenCalled()
{
_counterOne.ShouldBe(10);
_counterTwo.ShouldBe(0);
}
private void WhenIRemoveAService(ServiceEntry serviceEntryTwo)
{
_consulServices.Remove(serviceEntryTwo);
}
private void GivenIResetCounters()
{
_counterOne = 0;
_counterTwo = 0;
}
private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
{
_counterOne.ShouldBeInRange(bottom, top);
_counterOne.ShouldBeInRange(bottom, top);
}
private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected)
{
var total = _counterOne + _counterTwo;
total.ShouldBe(expected);
}
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
{
foreach(var serviceEntry in serviceEntries)
{
_consulServices.Add(serviceEntry);
}
}
private void GivenTheServicesAreRegisteredWithEureka(params IServiceInstance[] serviceInstances)
{
foreach (var instance in serviceInstances)
{
_eurekaInstances.Add(instance);
}
}
private void GivenThereIsAFakeEurekaServiceDiscoveryProvider(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 == "/eureka/apps/")
{
var apps = new List<Application>();
foreach (var serviceInstance in _eurekaInstances)
{
var a = new Application
{
name = serviceName,
instance = new List<Instance>
{
new Instance
{
instanceId = $"{serviceInstance.Host}:{serviceInstance}",
hostName = serviceInstance.Host,
app = serviceName,
ipAddr = "127.0.0.1",
status = "UP",
overriddenstatus = "UNKNOWN",
port = new Port {value = serviceInstance.Port, enabled = "true"},
securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"},
countryId = 1,
dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"},
leaseInfo = new LeaseInfo
{
renewalIntervalInSecs = 30,
durationInSecs = 90,
registrationTimestamp = 1457714988223,
lastRenewalTimestamp= 1457716158319,
evictionTimestamp = 0,
serviceUpTimestamp = 1457714988223
},
metadata = new Metadata
{
value = "java.util.Collections$EmptyMap"
},
homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}",
vipAddress = serviceName,
isCoordinatingDiscoveryServer = "false",
lastUpdatedTimestamp = "1457714988223",
lastDirtyTimestamp = "1457714988172",
actionType = "ADDED"
}
}
};
apps.Add(a);
}
var applications = new EurekaApplications
{
applications = new Applications
{
application = apps,
apps__hashcode = "UP_1_",
versions__delta = "1"
}
};
await context.Response.WriteJsonAsync(applications);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
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(_consulServices);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
private void GivenProductServiceOneIsRunning(string url, int statusCode)
{
_builderOne = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
try
{
string response;
lock (SyncLock)
{
_counterOne++;
response = _counterOne.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderOne.Start();
}
private void GivenProductServiceTwoIsRunning(string url, int statusCode)
{
_builderTwo = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
try
{
string response;
lock (SyncLock)
{
_counterTwo++;
response = _counterTwo.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderTwo.Start();
}
private void GivenEurekaProductServiceOneIsRunning(string url, int statusCode)
{
_builderOne = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
try
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync(nameof(ServiceDiscoveryTests));
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderOne.Start();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builderOne?.Dispose();
_builderTwo?.Dispose();
_steps.Dispose();
}
}
public class FakeEurekaService : IServiceInstance
{
public FakeEurekaService(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; }
}
public class Port
{
[JsonProperty("$")]
public int value { get; set; }
[JsonProperty("@enabled")]
public string enabled { get; set; }
}
public class SecurePort
{
[JsonProperty("$")]
public int value { get; set; }
[JsonProperty("@enabled")]
public string enabled { get; set; }
}
public class DataCenterInfo
{
[JsonProperty("@class")]
public string value { get; set; }
public string name { get; set; }
}
public class LeaseInfo
{
public int renewalIntervalInSecs { get; set; }
public int durationInSecs { get; set; }
public long registrationTimestamp { get; set; }
public long lastRenewalTimestamp { get; set; }
public int evictionTimestamp { get; set; }
public long serviceUpTimestamp { get; set; }
}
public class Metadata
{
[JsonProperty("@class")]
public string value { get; set; }
}
public class Instance
{
public string instanceId { get; set; }
public string hostName { get; set; }
public string app { get; set; }
public string ipAddr { get; set; }
public string status { get; set; }
public string overriddenstatus { get; set; }
public Port port { get; set; }
public SecurePort securePort { get; set; }
public int countryId { get; set; }
public DataCenterInfo dataCenterInfo { get; set; }
public LeaseInfo leaseInfo { get; set; }
public Metadata metadata { get; set; }
public string homePageUrl { get; set; }
public string statusPageUrl { get; set; }
public string healthCheckUrl { get; set; }
public string vipAddress { get; set; }
public string isCoordinatingDiscoveryServer { get; set; }
public string lastUpdatedTimestamp { get; set; }
public string lastDirtyTimestamp { get; set; }
public string actionType { get; set; }
}
public class Application
{
public string name { get; set; }
public List<Instance> instance { get; set; }
}
public class Applications
{
public string versions__delta { get; set; }
public string apps__hashcode { get; set; }
public List<Application> application { get; set; }
}
public class EurekaApplications
{
public Applications applications { get; set; }
}
}

View File

@ -1,28 +1,22 @@
using System.Linq;
using Microsoft.Extensions.Primitives;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class ServiceFabricTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public ServiceFabricTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -102,46 +96,34 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expectedQueryString)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
if (context.Request.QueryString.Value.Contains(expectedQueryString))
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
}
});
})
.Build();
_builder.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
if (context.Request.QueryString.Value.Contains(expectedQueryString))
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
}
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -0,0 +1,108 @@
namespace Ocelot.AcceptanceTests
{
using System;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class ServiceHandler : IDisposable
{
private IWebHost _builder;
public void GivenThereIsAServiceRunningOn(string baseUrl, RequestDelegate del)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.Run(del);
})
.Build();
_builder.Start();
}
public void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, RequestDelegate del)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(del);
})
.Build();
_builder.Start();
}
public void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string fileName, string password, int port, RequestDelegate del)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, port, listenOptions =>
{
listenOptions.UseHttps(fileName, password);
});
})
.UseContentRoot(Directory.GetCurrentDirectory())
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(del);
})
.Build();
_builder.Start();
}
public async Task StartFakeDownstreamService(string url, string path, Func<HttpContext, Func<Task>, Task> middleware)
{
_builder = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(middleware);
})
.UseIISIntegration()
.Build();
await _builder.StartAsync();
}
public void Dispose()
{
_builder?.Dispose();
}
}
}

View File

@ -1,25 +1,22 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class SslTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public SslTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
@ -98,48 +95,26 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, int port)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel(options =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, "idsrv3test.pfx", "idsrv3test", port, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
options.Listen(IPAddress.Loopback, port, listenOptions =>
{
listenOptions.UseHttps("idsrv3test.pfx", "idsrv3test");
});
})
.UseContentRoot(Directory.GetCurrentDirectory())
.Configure(app =>
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -0,0 +1,100 @@
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Configuration.Repository;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Responses;
using TestStack.BDDfy;
using Xunit;
public class StartupTests : IDisposable
{
private readonly Steps _steps;
private readonly ServiceHandler _serviceHandler;
private string _downstreamPath;
public StartupTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 52179,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var fakeRepo = new FakeFileConfigurationRepository();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52179", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithBlowingUpDiskRepo(fakeRepo))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
}
class FakeFileConfigurationRepository : IFileConfigurationRepository
{
public Task<Response<FileConfiguration>> Get()
{
throw new NotImplementedException();
}
public Task<Response> Set(FileConfiguration fileConfiguration)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -1,37 +1,35 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using Ocelot.AcceptanceTests.Caching;
using System.IO.Compression;
using System.Text;
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
using Ocelot.Requester;
using Ocelot.Middleware.Multiplexer;
using static Ocelot.Infrastructure.Wait;
namespace Ocelot.AcceptanceTests
namespace Ocelot.AcceptanceTests
{
using Microsoft.Net.Http.Headers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using System.IO.Compression;
using System.Text;
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
using Ocelot.Middleware.Multiplexer;
using static Ocelot.Infrastructure.Wait;
using Configuration.Repository;
using Ocelot.Configuration.Creator;
using CookieHeaderValue = System.Net.Http.Headers.CookieHeaderValue;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
public class Steps : IDisposable
@ -41,7 +39,6 @@ namespace Ocelot.AcceptanceTests
private HttpResponseMessage _response;
private HttpContent _postContent;
private BearerToken _token;
public HttpClient OcelotClient => _ocelotClient;
public string RequestIdKey = "OcRequestId";
private readonly Random _random;
private IWebHostBuilder _webHostBuilder;
@ -53,6 +50,18 @@ namespace Ocelot.AcceptanceTests
_random = new Random();
}
public async Task ThenConfigShouldBe(FileConfiguration fileConfig)
{
var internalConfigCreator = _ocelotServer.Host.Services.GetService<IInternalConfigurationCreator>();
var internalConfigRepo = _ocelotServer.Host.Services.GetService<IInternalConfigurationRepository>();
var internalConfig = internalConfigRepo.Get();
var config = await internalConfigCreator.Create(fileConfig);
internalConfig.Data.RequestId.ShouldBe(config.Data.RequestId);
}
public async Task StartFakeOcelotWithWebSockets()
{
_ocelotBuilder = new WebHostBuilder();
@ -68,9 +77,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
@ -114,6 +123,34 @@ namespace Ocelot.AcceptanceTests
File.WriteAllText(configurationPath, jsonConfiguration);
}
public void GivenOcelotIsRunningReloadingConfig(bool shouldReload)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: shouldReload);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
@ -126,9 +163,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -145,44 +182,6 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient();
}
internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot()
.AddOpenTracing(option =>
{
//this is the url that the butterfly collector server is running on...
option.CollectorUrl = butterflyUrl;
option.Service = "Ocelot";
});
})
.Configure(app =>
{
app.Use(async (context, next) =>
{
await next.Invoke();
});
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
internal void GivenIWait(int wait)
{
Thread.Sleep(wait);
@ -197,9 +196,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -228,9 +227,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -261,9 +260,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -294,9 +293,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -326,9 +325,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -357,9 +356,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -396,9 +395,9 @@ namespace Ocelot.AcceptanceTests
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
@ -423,146 +422,6 @@ namespace Ocelot.AcceptanceTests
header.First().ShouldBe(value);
}
public void ThenTheResponseBodyHeaderIs(string key, string value)
{
var header = _response.Content.Headers.GetValues(key);
header.First().ShouldBe(value);
}
public void ThenTheTraceHeaderIsSet(string key)
{
var header = _response.Headers.GetValues(key);
header.First().ShouldNotBeNullOrEmpty();
}
public void GivenOcelotIsRunningUsingJsonSerializedCache()
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot()
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
});
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfig()
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot().AddStoreOcelotConfigurationInConsul();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddOcelot()
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
})
.AddStoreOcelotConfigurationInConsul();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
internal void ThenTheResponseShouldBe(FileConfiguration expecteds)
{
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++)
{
for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = response.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod);
}
}
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
@ -570,8 +429,8 @@ namespace Ocelot.AcceptanceTests
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("ocelot.json")
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables();
var configuration = builder.Build();
@ -585,15 +444,6 @@ namespace Ocelot.AcceptanceTests
.UseConfiguration(configuration)
.ConfigureServices(s =>
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
s.AddOcelot(configuration);
})
.ConfigureLogging(l =>
@ -683,26 +533,6 @@ namespace Ocelot.AcceptanceTests
}
}
public void GivenIHaveAnOcelotToken(string adminPath)
{
var tokenUrl = $"{adminPath}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "admin"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "admin"),
new KeyValuePair<string, string>("username", "admin"),
new KeyValuePair<string, string>("password", "admin"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
var response = _ocelotClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
public void VerifyIdentiryServerStarted(string url)
{
using (var httpClient = new HttpClient())
@ -718,24 +548,6 @@ namespace Ocelot.AcceptanceTests
_response = _ocelotClient.GetAsync(url).Result;
}
public void WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk(string url)
{
var result = WaitFor(2000).Until(() => {
try
{
_response = _ocelotClient.GetAsync(url).Result;
_response.EnsureSuccessStatusCode();
return true;
}
catch(Exception)
{
return false;
}
});
result.ShouldBeTrue();
}
public void WhenIGetUrlOnTheApiGateway(string url, string cookie, string value)
{
var request = _ocelotServer.CreateRequest(url);
@ -746,7 +558,7 @@ namespace Ocelot.AcceptanceTests
public void GivenIAddAHeader(string key, string value)
{
_ocelotClient.DefaultRequestHeaders.Add(key, value);
_ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(key, value);
}
public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times)
@ -823,6 +635,11 @@ namespace Ocelot.AcceptanceTests
_postContent = new StringContent(postcontent);
}
public void GivenThePostHasContentType(string postcontent)
{
_postContent.Headers.ContentType = new MediaTypeHeaderValue(postcontent);
}
public void GivenThePostHasGzipContent(object input)
{
var json = JsonConvert.SerializeObject(input);
@ -873,11 +690,6 @@ namespace Ocelot.AcceptanceTests
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
}
public void ThenTheContentLengthIs(int expected)
{
_response.Content.Headers.ContentLength.ShouldBe(expected);
}
public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway()
{
int numberOfRequests = 100;
@ -923,5 +735,35 @@ namespace Ocelot.AcceptanceTests
var content = await response.Content.ReadAsStringAsync();
content.ShouldBe(expectedBody);
}
public void GivenOcelotIsRunningWithBlowingUpDiskRepo(IFileConfigurationRepository fake)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddSingleton<IFileConfigurationRepository>(fake);
s.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
}
}

View File

@ -1,34 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class StickySessionsTests : IDisposable
{
private IWebHost _builderOne;
private IWebHost _builderTwo;
private readonly Steps _steps;
private int _counterOne;
private int _counterTwo;
private static readonly object _syncLock = new object();
private static readonly object SyncLock = new object();
private readonly ServiceHandler _serviceHandler;
public StickySessionsTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_use_same_downstream_host()
{
var downstreamPortOne = 51881;
var downstreamPortOne = 51375;
var downstreamPortTwo = 51892;
var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}";
var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}";
@ -244,77 +241,52 @@ namespace Ocelot.AcceptanceTests
private void GivenProductServiceOneIsRunning(string url, int statusCode)
{
_builderOne = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
try
{
app.Run(async context =>
var response = string.Empty;
lock (SyncLock)
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterOne++;
response = _counterOne.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_builderOne.Start();
_counterOne++;
response = _counterOne.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
}
private void GivenProductServiceTwoIsRunning(string url, int statusCode)
{
_builderTwo = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(url, async context =>
{
try
{
app.Run(async context =>
var response = string.Empty;
lock (SyncLock)
{
try
{
var response = string.Empty;
lock (_syncLock)
{
_counterTwo++;
response = _counterTwo.ToString();
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (System.Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
})
.Build();
_counterTwo++;
response = _counterTwo.ToString();
}
_builderTwo.Start();
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(response);
}
catch (Exception exception)
{
await context.Response.WriteAsync(exception.StackTrace);
}
});
}
public void Dispose()
{
_builderOne?.Dispose();
_builderTwo?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,191 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class TwoDownstreamServicesTests : IDisposable
{
private IWebHost _builderOne;
private IWebHost _builderTwo;
private IWebHost _fakeConsulBuilder;
private readonly Steps _steps;
private readonly List<ServiceEntry> _serviceEntries;
private string _downstreamPathOne;
private string _downstreamPathTwo;
public TwoDownstreamServicesTests()
{
_steps = new Steps();
_serviceEntries = new List<ServiceEntry>();
}
[Fact]
public void should_fix_issue_194()
{
var consulPort = 8503;
var downstreamServiceOneUrl = "http://localhost:8362";
var downstreamServiceTwoUrl = "http://localhost:8330";
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/user/{user}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 8362,
}
},
UpstreamPathTemplate = "/api/user/{user}",
UpstreamHttpMethod = new List<string> { "Get" },
},
new FileReRoute
{
DownstreamPathTemplate = "/api/product/{product}",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 8330,
}
},
UpstreamPathTemplate = "/api/product/{product}",
UpstreamHttpMethod = new List<string> { "Get" },
}
},
GlobalConfiguration = new FileGlobalConfiguration()
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider()
{
Host = "localhost",
Port = consulPort
}
}
};
this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, "/api/user/info", 200, "user"))
.And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, "/api/product/info", 200, "product"))
.And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/user/info?id=1"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("user"))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/product/info?id=1"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("product"))
.BDDfy();
}
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url)
{
_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/product")
{
await context.Response.WriteJsonAsync(_serviceEntries);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
private void GivenProductServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builderOne = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathOne != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builderOne.Start();
}
private void GivenProductServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builderTwo = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPathTwo != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builderTwo.Start();
}
public void Dispose()
{
_builderOne?.Dispose();
_builderTwo?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -1,31 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using TestStack.BDDfy;
using Xunit;
public class UpstreamHostTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _downstreamPath;
private readonly ServiceHandler _serviceHandler;
public UpstreamHostTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
}
[Fact]
public void should_return_response_200_with_simple_url_and_hosts_match()
{
int port = 64905;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -39,7 +38,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51875,
Port = port,
}
},
UpstreamPathTemplate = "/",
@ -49,7 +48,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -61,6 +60,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes()
{
int port = 64904;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -74,7 +75,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51875,
Port = port,
}
},
UpstreamPathTemplate = "/",
@ -100,7 +101,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -112,6 +113,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed()
{
int port = 64903;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -141,7 +144,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51875,
Port = port,
}
},
UpstreamPathTemplate = "/",
@ -151,7 +154,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -163,6 +166,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_with_simple_url_and_hosts_match_multiple_re_routes_reversed_with_no_host_first()
{
int port = 64902;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -191,7 +196,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51875,
Port = port,
}
},
UpstreamPathTemplate = "/",
@ -201,7 +206,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -213,6 +218,8 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_404_with_simple_url_and_hosts_dont_match()
{
int port = 64901;
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
@ -226,7 +233,7 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort
{
Host = "localhost",
Port = 51875,
Port = port,
}
},
UpstreamPathTemplate = "/",
@ -236,7 +243,7 @@ namespace Ocelot.AcceptanceTests
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51875", "/", 200, "Hello from Laura"))
this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
@ -246,43 +253,26 @@ namespace Ocelot.AcceptanceTests
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if (_downstreamPath != basePath)
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
if(_downstreamPath != basePath)
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
})
.Build();
_builder.Start();
}
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
});
}
public void Dispose()
{
_builder?.Dispose();
_serviceHandler?.Dispose();
_steps.Dispose();
}
}

View File

@ -1,39 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class WebSocketTests : IDisposable
{
private IWebHost _firstDownstreamHost;
private IWebHost _secondDownstreamHost;
private readonly List<string> _secondRecieved;
private readonly List<string> _firstRecieved;
private readonly List<ServiceEntry> _serviceEntries;
private readonly Steps _steps;
private IWebHost _fakeConsulBuilder;
private readonly ServiceHandler _serviceHandler;
public WebSocketTests()
{
_serviceHandler = new ServiceHandler();
_steps = new Steps();
_firstRecieved = new List<string>();
_secondRecieved = new List<string>();
_serviceEntries = new List<ServiceEntry>();
}
[Fact]
@ -115,77 +105,6 @@ namespace Ocelot.AcceptanceTests
.BDDfy();
}
[Fact]
public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer()
{
var downstreamPort = 5007;
var downstreamHost = "localhost";
var secondDownstreamPort = 5008;
var secondDownstreamHost = "localhost";
var serviceName = "websockets";
var consulPort = 8509;
var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
var serviceEntryOne = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = downstreamHost,
Port = downstreamPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var serviceEntryTwo = new ServiceEntry()
{
Service = new AgentService()
{
Service = serviceName,
Address = secondDownstreamHost,
Port = secondDownstreamPort,
ID = Guid.NewGuid().ToString(),
Tags = new string[0]
},
};
var config = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/",
DownstreamPathTemplate = "/ws",
DownstreamScheme = "ws",
LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" },
ServiceName = serviceName,
UseServiceDiscovery = true
}
},
GlobalConfiguration = new FileGlobalConfiguration
{
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "localhost",
Port = consulPort,
Type = "consul"
}
}
};
this.Given(_ => _steps.GivenThereIsAConfiguration(config))
.And(_ => _steps.StartFakeOcelotWithWebSockets())
.And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
.And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
.And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
.And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws"))
.When(_ => WhenIStartTheClients())
.Then(_ => ThenBothDownstreamServicesAreCalled())
.BDDfy();
}
private void ThenBothDownstreamServicesAreCalled()
{
_firstRecieved.Count.ShouldBe(10);
@ -201,37 +120,6 @@ namespace Ocelot.AcceptanceTests
});
}
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}")
{
await context.Response.WriteJsonAsync(_serviceEntries);
}
});
})
.Build();
_fakeConsulBuilder.Start();
}
private async Task WhenIStartTheClients()
{
var firstClient = StartClient("ws://localhost:5000/");
@ -333,94 +221,48 @@ namespace Ocelot.AcceptanceTests
private async Task StartFakeDownstreamService(string url, string path)
{
_firstDownstreamHost = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
await _serviceHandler.StartFakeDownstreamService(url, path, async(context, next) =>
{
if (context.Request.Path == path)
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(async (context, next) =>
if (context.WebSockets.IsWebSocketRequest)
{
if (context.Request.Path == path)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
})
.UseIISIntegration().Build();
await _firstDownstreamHost.StartAsync();
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
}
private async Task StartSecondFakeDownstreamService(string url, string path)
{
_secondDownstreamHost = new WebHostBuilder()
.ConfigureServices(s => { }).UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) =>
{
if (context.Request.Path == path)
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.Configure(app =>
{
app.UseWebSockets();
app.Use(async (context, next) =>
if (context.WebSockets.IsWebSocketRequest)
{
if (context.Request.Path == path)
{
if (context.WebSockets.IsWebSocketRequest)
{
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Message(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
})
.UseIISIntegration().Build();
await _secondDownstreamHost.StartAsync();
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Message(webSocket);
}
else
{
context.Response.StatusCode = 400;
}
}
else
{
await next();
}
});
}
private async Task Echo(WebSocket webSocket)
@ -473,10 +315,8 @@ namespace Ocelot.AcceptanceTests
public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
_firstDownstreamHost?.Dispose();
_secondDownstreamHost?.Dispose();
_fakeConsulBuilder?.Dispose();
}
}
}

View File

@ -1,157 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Middleware;
using Ocelot.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Validators;
namespace Ocelot.Benchmarks
{
[Config(typeof(AllTheThingsBenchmarks))]
public class AllTheThingsBenchmarks : ManualConfig
{
private IWebHost _service;
private IWebHost _ocelot;
private HttpClient _httpClient;
public AllTheThingsBenchmarks()
{
Add(StatisticColumn.AllStatistics);
Add(MemoryDiagnoser.Default);
Add(BaselineValidator.FailOnError);
}
[GlobalSetup]
public void SetUp()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty);
GivenThereIsAConfiguration(configuration);
GivenOcelotIsRunning("http://localhost:5000");
_httpClient = new HttpClient();
}
[Benchmark(Baseline = true)]
public async Task Baseline()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
/* * Summary*
BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0]
Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores
.NET Core SDK = 2.1.4
[Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated |
--------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:|
Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/
private void GivenOcelotIsRunning(string url)
{
_ocelot = new WebHostBuilder()
.UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build();
_ocelot.Start();
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_service = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_service.Start();
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Middleware;
using Ocelot.DependencyInjection;
using System.Net.Http;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Validators;
namespace Ocelot.Benchmarks
{
[Config(typeof(AllTheThingsBenchmarks))]
public class AllTheThingsBenchmarks : ManualConfig
{
private IWebHost _service;
private IWebHost _ocelot;
private HttpClient _httpClient;
public AllTheThingsBenchmarks()
{
Add(StatisticColumn.AllStatistics);
Add(MemoryDiagnoser.Default);
Add(BaselineValidator.FailOnError);
}
[GlobalSetup]
public void SetUp()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty);
GivenThereIsAConfiguration(configuration);
GivenOcelotIsRunning("http://localhost:5000");
_httpClient = new HttpClient();
}
[Benchmark(Baseline = true)]
public async Task Baseline()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
}
/* * Summary*
BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0]
Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores
.NET Core SDK = 2.1.4
[Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated |
--------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:|
Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/
private void GivenOcelotIsRunning(string url)
{
_ocelot = new WebHostBuilder()
.UseKestrel()
.UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build();
_ocelot.Start();
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_service = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_service.Start();
}
}
}

View File

@ -1,852 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using IdentityServer4.Test;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Cache;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Ocelot.IntegrationTests
{
public class AdministrationTests : IDisposable
{
private HttpClient _httpClient;
private readonly HttpClient _httpClientTwo;
private HttpResponseMessage _response;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private string _ocelotBaseUrl;
private BearerToken _token;
private IWebHostBuilder _webHostBuilderTwo;
private IWebHost _builderTwo;
private IWebHost _identityServerBuilder;
private IWebHost _fooServiceBuilder;
private IWebHost _barServiceBuilder;
public AdministrationTests()
{
_httpClient = new HttpClient();
_httpClientTwo = new HttpClient();
_ocelotBaseUrl = "http://localhost:5000";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_return_response_401_with_call_re_routes_controller()
{
var configuration = new FileConfiguration();
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
}
[Fact]
public void should_return_response_200_with_call_re_routes_controller()
{
var configuration = new FileConfiguration();
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config()
{
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5011";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
BaseUrl = _ocelotBaseUrl
}
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl))
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b()
{
var configuration = new FileConfiguration();
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet())
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenAnotherOcelotIsRunning("http://localhost:5007"))
.When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_return_file_configuration()
{
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
RequestIdKey = "RequestId",
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
{
Host = "127.0.0.1",
}
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Geoff"
}
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Dave"
}
}
}
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(configuration))
.BDDfy();
}
[Fact]
public void should_get_file_configuration_edit_and_post_updated_version()
{
var initialConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/"
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test"
}
}
};
var updatedConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/"
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" },
UpstreamPathTemplate = "/test"
}
}
};
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.BDDfy();
}
[Fact]
public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute()
{
var fooPort = 47689;
var barPort = 47690;
var initialConfiguration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = fooPort,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/foo",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/foo"
}
}
};
var updatedConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = barPort,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/bar",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/foo"
}
}
};
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
.And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}"))
.And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}"))
.And(x => GivenOcelotIsRunning())
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("foo"))
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("bar"))
.When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => ThenTheResponseShouldBe(initialConfiguration))
.And(x => WhenIGetUrlOnTheApiGateway("/foo"))
.Then(x => ThenTheResponseBodyShouldBe("foo"))
.BDDfy();
}
[Fact]
public void should_clear_region()
{
var initialConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10
}
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 80,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10
}
}
}
};
var regionToClear = "gettest";
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent))
.BDDfy();
}
[Fact]
public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area()
{
var configuration = new FileConfiguration();
var identityServerRootUrl = "http://localhost:5123";
Action<IdentityServerAuthenticationOptions> options = o => {
o.Authority = identityServerRootUrl;
o.ApiName = "api";
o.RequireHttpsMetadata = false;
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api"))
.And(x => GivenOcelotIsRunningWithIdentityServerSettings(options))
.And(x => GivenIHaveAToken(identityServerRootUrl))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
private void GivenIHaveAToken(string url)
{
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "api"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "api"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
using (var httpClient = new HttpClient())
{
var response = httpClient.PostAsync($"{url}/connect/token", content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
}
private void GivenThereIsAnIdentityServerOn(string url, string apiName)
{
_identityServerBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureServices(services =>
{
services.AddLogging();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(new List<ApiResource>
{
new ApiResource
{
Name = apiName,
Description = apiName,
Enabled = true,
DisplayName = apiName,
Scopes = new List<Scope>()
{
new Scope(apiName)
}
}
})
.AddInMemoryClients(new List<Client>
{
new Client
{
ClientId = apiName,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> {new Secret("secret".Sha256())},
AllowedScopes = new List<string> { apiName },
AccessTokenType = AccessTokenType.Jwt,
Enabled = true
}
})
.AddTestUsers(new List<TestUser>
{
new TestUser
{
Username = "test",
Password = "test",
SubjectId = "1231231"
}
});
})
.Configure(app =>
{
app.UseIdentityServer();
})
.Build();
_identityServerBuilder.Start();
using (var httpClient = new HttpClient())
{
var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
response.EnsureSuccessStatusCode();
}
}
private void GivenAnotherOcelotIsRunning(string baseUrl)
{
_httpClientTwo.BaseAddress = new Uri(baseUrl);
_webHostBuilderTwo = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
Action<ConfigurationBuilderCachePart> settings = (s) =>
{
s.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
x.AddOcelot()
.AddCacheManager(settings)
.AddAdministration("/administration", "secret");
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builderTwo = _webHostBuilderTwo.Build();
_builderTwo.Start();
}
private void GivenIdentityServerSigningEnvironmentalVariablesAreSet()
{
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx");
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test");
}
private void WhenIGetUrlOnTheSecondOcelot(string url)
{
_httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
_response = _httpClientTwo.GetAsync(url).Result;
}
private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
{
var json = JsonConvert.SerializeObject(updatedConfiguration);
var content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
_response = _httpClient.PostAsync(url, content).Result;
}
private void ThenTheResponseShouldBe(List<string> expected)
{
var content = _response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<Regions>(content);
result.Value.ShouldBe(expected);
}
private void ThenTheResponseBodyShouldBe(string expected)
{
var content = _response.Content.ReadAsStringAsync().Result;
content.ShouldBe(expected);
}
private void ThenTheResponseShouldBe(FileConfiguration expecteds)
{
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++)
{
for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = response.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod);
}
}
private void GivenIHaveAddedATokenToMyRequest()
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
}
private void GivenIHaveAnOcelotToken(string adminPath)
{
var tokenUrl = $"{adminPath}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "admin"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "admin"),
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
var content = new FormUrlEncodedContent(formData);
var response = _httpClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
var configPath = $"{adminPath}/.well-known/openid-configuration";
response = _httpClient.GetAsync(configPath).Result;
response.EnsureSuccessStatusCode();
}
private void GivenOcelotIsRunningWithIdentityServerSettings(Action<IdentityServerAuthenticationOptions> configOptions)
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(x => {
x.AddSingleton(_webHostBuilder);
x.AddOcelot()
.AddCacheManager(c =>
{
c.WithDictionaryHandle();
})
.AddAdministration("/administration", configOptions);
})
.Configure(app => {
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
Action<ConfigurationBuilderCachePart> settings = (s) =>
{
s.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
x.AddOcelot()
.AddCacheManager(settings)
.AddAdministration("/administration", "secret");
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl)
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(x => {
x.AddSingleton(_webHostBuilder);
x.AddOcelot()
.AddCacheManager(c =>
{
c.WithDictionaryHandle();
})
.AddAdministration("/administration", "secret");
})
.Configure(app => {
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void WhenIGetUrlOnTheApiGateway(string url)
{
_response = _httpClient.GetAsync(url).Result;
}
private void WhenIDeleteOnTheApiGateway(string url)
{
_response = _httpClient.DeleteAsync(url).Result;
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
{
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
}
public void Dispose()
{
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "");
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "");
_builder?.Dispose();
_httpClient?.Dispose();
_identityServerBuilder?.Dispose();
}
private void GivenThereIsAFooServiceRunningOn(string baseUrl)
{
_fooServiceBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase("/foo");
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("foo");
});
})
.Build();
_fooServiceBuilder.Start();
}
private void GivenThereIsABarServiceRunningOn(string baseUrl)
{
_barServiceBuilder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase("/bar");
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("bar");
});
})
.Build();
_barServiceBuilder.Start();
}
}
}

View File

@ -1,16 +0,0 @@
using Newtonsoft.Json;
namespace Ocelot.IntegrationTests
{
class BearerToken
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
}
}

View File

@ -39,11 +39,8 @@
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="IdentityServer4" Version="2.2.0" />
<PackageReference Include="Shouldly" Version="3.0.0" />
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
<PackageReference Include="Consul" Version="0.7.2.5" />
<PackageReference Include="Rafty" Version="0.4.4" />
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
</ItemGroup>
</Project>

View File

@ -1,515 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.Raft;
using Rafty.Concensus;
using Rafty.Infrastructure;
using Shouldly;
using Xunit;
using static Rafty.Infrastructure.Wait;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace Ocelot.IntegrationTests
{
using System.Threading.Tasks;
using Xunit.Abstractions;
public class RaftTests : IDisposable
{
private readonly List<IWebHost> _builders;
private readonly List<IWebHostBuilder> _webHostBuilders;
private readonly List<Thread> _threads;
private FilePeers _peers;
private HttpClient _httpClient;
private readonly HttpClient _httpClientForAssertions;
private BearerToken _token;
private HttpResponseMessage _response;
private static readonly object _lock = new object();
private ITestOutputHelper _output;
public RaftTests(ITestOutputHelper output)
{
_output = output;
_httpClientForAssertions = new HttpClient();
_webHostBuilders = new List<IWebHostBuilder>();
_builders = new List<IWebHost>();
_threads = new List<Thread>();
}
[Fact(Skip = "Still not stable, more work required in rafty..")]
public async Task should_persist_command_to_five_servers()
{
var peers = new List<FilePeer>
{
new FilePeer {HostAndPort = "http://localhost:5000"},
new FilePeer {HostAndPort = "http://localhost:5001"},
new FilePeer {HostAndPort = "http://localhost:5002"},
new FilePeer {HostAndPort = "http://localhost:5003"},
new FilePeer {HostAndPort = "http://localhost:5004"}
};
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
}
};
var updatedConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
Port = 80,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/"
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" },
UpstreamPathTemplate = "/test"
}
}
};
var command = new UpdateFileConfiguration(updatedConfiguration);
GivenThePeersAre(peers);
GivenThereIsAConfiguration(configuration);
GivenFiveServersAreRunning();
await GivenIHaveAnOcelotToken("/administration");
await WhenISendACommandIntoTheCluster(command);
Thread.Sleep(5000);
await ThenTheCommandIsReplicatedToAllStateMachines(command);
}
[Fact(Skip = "Still not stable, more work required in rafty..")]
public async Task should_persist_command_to_five_servers_when_using_administration_api()
{
var peers = new List<FilePeer>
{
new FilePeer {HostAndPort = "http://localhost:5005"},
new FilePeer {HostAndPort = "http://localhost:5006"},
new FilePeer {HostAndPort = "http://localhost:5007"},
new FilePeer {HostAndPort = "http://localhost:5008"},
new FilePeer {HostAndPort = "http://localhost:5009"}
};
var configuration = new FileConfiguration
{
};
var updatedConfiguration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "127.0.0.1",
Port = 80,
}
},
DownstreamScheme = "http",
DownstreamPathTemplate = "/geoffrey",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/"
},
new FileReRoute()
{
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "123.123.123",
Port = 443,
}
},
DownstreamScheme = "https",
DownstreamPathTemplate = "/blooper/{productId}",
UpstreamHttpMethod = new List<string> { "post" },
UpstreamPathTemplate = "/test"
}
}
};
var command = new UpdateFileConfiguration(updatedConfiguration);
GivenThePeersAre(peers);
GivenThereIsAConfiguration(configuration);
GivenFiveServersAreRunning();
await GivenIHaveAnOcelotToken("/administration");
GivenIHaveAddedATokenToMyRequest();
await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration);
await ThenTheCommandIsReplicatedToAllStateMachines(command);
}
private void GivenThePeersAre(List<FilePeer> peers)
{
FilePeers filePeers = new FilePeers();
filePeers.Peers.AddRange(peers);
var json = JsonConvert.SerializeObject(filePeers);
File.WriteAllText("peers.json", json);
_httpClient = new HttpClient();
var ocelotBaseUrl = peers[0].HostAndPort;
_httpClient.BaseAddress = new Uri(ocelotBaseUrl);
}
private async Task WhenISendACommandIntoTheCluster(UpdateFileConfiguration command)
{
async Task<bool> SendCommand()
{
try
{
var p = _peers.Peers.First();
var json = JsonConvert.SerializeObject(command, new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
});
var httpContent = new StringContent(json);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var errorResult = JsonConvert.DeserializeObject<ErrorResponse<UpdateFileConfiguration>>(content);
if (!string.IsNullOrEmpty(errorResult.Error))
{
return false;
}
var okResult = JsonConvert.DeserializeObject<OkResponse<UpdateFileConfiguration>>(content);
if (okResult.Command.Configuration.ReRoutes.Count == 2)
{
return true;
}
}
return false;
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
}
var commandSent = await WaitFor(40000).Until(async () =>
{
var result = await SendCommand();
Thread.Sleep(1000);
return result;
});
commandSent.ShouldBeTrue();
}
private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds)
{
async Task<bool> CommandCalledOnAllStateMachines()
{
try
{
var passed = 0;
foreach (var peer in _peers.Peers)
{
var path = $"{peer.HostAndPort.Replace("/","").Replace(":","")}.db";
using(var connection = new SqliteConnection($"Data Source={path};"))
{
connection.Open();
var sql = @"select count(id) from logs";
using(var command = new SqliteCommand(sql, connection))
{
var index = Convert.ToInt32(command.ExecuteScalar());
index.ShouldBe(1);
}
}
_httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
var result = await _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration");
var json = await result.Content.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<FileConfiguration>(json, new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All});
response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++)
{
for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var res = response.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.Configuration.ReRoutes[i].DownstreamHostAndPorts[j];
res.Host.ShouldBe(expected.Host);
res.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamHttpMethod);
}
passed++;
}
return passed == 5;
}
catch(Exception e)
{
//_output.WriteLine($"{e.Message}, {e.StackTrace}");
Console.WriteLine(e);
return false;
}
}
var commandOnAllStateMachines = await WaitFor(40000).Until(async () =>
{
var result = await CommandCalledOnAllStateMachines();
Thread.Sleep(1000);
return result;
});
commandOnAllStateMachines.ShouldBeTrue();
}
private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
{
async Task<bool> SendCommand()
{
var json = JsonConvert.SerializeObject(updatedConfiguration);
var content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
_response = await _httpClient.PostAsync(url, content);
var responseContent = await _response.Content.ReadAsStringAsync();
if(responseContent == "There was a problem. This error message sucks raise an issue in GitHub.")
{
return false;
}
if(string.IsNullOrEmpty(responseContent))
{
return false;
}
return _response.IsSuccessStatusCode;
}
var commandSent = await WaitFor(40000).Until(async () =>
{
var result = await SendCommand();
Thread.Sleep(1000);
return result;
});
commandSent.ShouldBeTrue();
}
private void GivenIHaveAddedATokenToMyRequest()
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
}
private async Task GivenIHaveAnOcelotToken(string adminPath)
{
async Task<bool> AddToken()
{
try
{
var tokenUrl = $"{adminPath}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "admin"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "admin"),
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
var content = new FormUrlEncodedContent(formData);
var response = await _httpClient.PostAsync(tokenUrl, content);
var responseContent = await response.Content.ReadAsStringAsync();
if(!response.IsSuccessStatusCode)
{
return false;
}
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
var configPath = $"{adminPath}/.well-known/openid-configuration";
response = await _httpClient.GetAsync(configPath);
return response.IsSuccessStatusCode;
}
catch(Exception)
{
return false;
}
}
var addToken = await WaitFor(40000).Until(async () =>
{
var result = await AddToken();
Thread.Sleep(1000);
return result;
});
addToken.ShouldBeTrue();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void GivenAServerIsRunning(string url)
{
lock(_lock)
{
IWebHostBuilder webHostBuilder = new WebHostBuilder();
webHostBuilder.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddJsonFile("peers.json", optional: true, reloadOnChange: true);
#pragma warning disable CS0618
config.AddOcelotBaseUrl(url);
#pragma warning restore CS0618
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddSingleton(new NodeId(url));
x
.AddOcelot()
.AddAdministration("/administration", "secret")
.AddRafty();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
var builder = webHostBuilder.Build();
builder.Start();
_webHostBuilders.Add(webHostBuilder);
_builders.Add(builder);
}
}
private void GivenFiveServersAreRunning()
{
var bytes = File.ReadAllText("peers.json");
_peers = JsonConvert.DeserializeObject<FilePeers>(bytes);
foreach (var peer in _peers.Peers)
{
File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", ""));
File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db");
var thread = new Thread(() => GivenAServerIsRunning(peer.HostAndPort));
thread.Start();
_threads.Add(thread);
}
}
public void Dispose()
{
foreach (var builder in _builders)
{
builder?.Dispose();
}
foreach (var peer in _peers.Peers)
{
try
{
File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", ""));
File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}

View File

@ -1,218 +1,205 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using CacheManager.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace Ocelot.IntegrationTests
{
public class ThreadSafeHeadersTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
public ThreadSafeHeadersTests()
{
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
_random = new Random();
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5001";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
var header = context.Request.Headers["ThreadSafeHeadersTest"];
context.Response.StatusCode = 200;
await context.Response.WriteAsync(header[0]);
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("ocelot.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
Action<ConfigurationBuilderCachePart> settings = (s) =>
{
s.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
x.AddOcelot()
.AddCacheManager(settings)
.AddAdministration("/administration", "secret");
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{
var tasks = new Task[times];
for (int i = 0; i < times; i++)
{
var urlCopy = url;
var random = _random.Next(0, 50);
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
}
Task.WaitAll(tasks);
}
private async Task GetForThreadSafeHeadersTest(string url, int random)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
int result = int.Parse(content);
var tshtr = new ThreadSafeHeadersTestResult(result, random);
_results.Add(tshtr);
}
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
{
foreach(var result in _results)
{
result.Result.ShouldBe(result.Random);
}
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
class ThreadSafeHeadersTestResult
{
public ThreadSafeHeadersTestResult(int result, int random)
{
Result = result;
Random = random;
}
public int Result { get; private set; }
public int Random { get; private set; }
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace Ocelot.IntegrationTests
{
public class ThreadSafeHeadersTests : IDisposable
{
private readonly HttpClient _httpClient;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder;
private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
public ThreadSafeHeadersTests()
{
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
_random = new Random();
_httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5001";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
[Fact]
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
.And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url)
{
_downstreamBuilder = new WebHostBuilder()
.UseUrls(url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(url)
.Configure(app =>
{
app.Run(async context =>
{
var header = context.Request.Headers["ThreadSafeHeadersTest"];
context.Response.StatusCode = 200;
await context.Response.WriteAsync(header[0]);
});
})
.Build();
_downstreamBuilder.Start();
}
private void GivenOcelotIsRunning()
{
_webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(x =>
{
x.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_builder = _webHostBuilder.Build();
_builder.Start();
}
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath);
}
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{
var tasks = new Task[times];
for (int i = 0; i < times; i++)
{
var urlCopy = url;
var random = _random.Next(0, 50);
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
}
Task.WaitAll(tasks);
}
private async Task GetForThreadSafeHeadersTest(string url, int random)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
int result = int.Parse(content);
var tshtr = new ThreadSafeHeadersTestResult(result, random);
_results.Add(tshtr);
}
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
{
foreach(var result in _results)
{
result.Result.ShouldBe(result.Random);
}
}
public void Dispose()
{
_builder?.Dispose();
_httpClient?.Dispose();
_downstreamBuilder?.Dispose();
}
class ThreadSafeHeadersTestResult
{
public ThreadSafeHeadersTestResult(int result, int random)
{
Result = result;
Random = random;
}
public int Result { get; private set; }
public int Random { get; private set; }
}
}
}

View File

@ -36,7 +36,6 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.1.1" />
<PackageReference Include="Consul" Version="0.7.2.5" />
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>

View File

@ -8,7 +8,6 @@
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using System;
using IdentityServer4.AccessTokenValidation;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
@ -26,7 +25,7 @@
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
.AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
@ -38,17 +37,17 @@
});
s.AddOcelot()
.AddDelegatingHandler<FakeHandler>(true)
.AddDelegatingHandler<FakeHandler>(true);
// .AddCacheManager(x =>
// {
// x.WithDictionaryHandle();
// })
/*.AddOpenTracing(option =>
{
option.CollectorUrl = "http://localhost:9618";
option.Service = "Ocelot.ManualTest";
})*/
.AddAdministration("/administration", "secret");
// .AddOpenTracing(option =>
// {
// option.CollectorUrl = "http://localhost:9618";
// option.Service = "Ocelot.ManualTest";
// })
// .AddAdministration("/administration", "secret");
})
.ConfigureLogging((hostingContext, logging) =>
{

View File

@ -8,7 +8,7 @@
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 3000
"Port": 5001
}
],
"QoSOptions": {

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)