mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 09:08:14 +08:00
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:
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
68
test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs
Normal file
68
test/Ocelot.AcceptanceTests/ConfigurationReloadTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
177
test/Ocelot.AcceptanceTests/ContentTests.cs
Normal file
177
test/Ocelot.AcceptanceTests/ContentTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
108
test/Ocelot.AcceptanceTests/ServiceHandler.cs
Normal file
108
test/Ocelot.AcceptanceTests/ServiceHandler.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
100
test/Ocelot.AcceptanceTests/StartupTests.cs
Normal file
100
test/Ocelot.AcceptanceTests/StartupTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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) =>
|
||||
{
|
||||
|
@ -8,7 +8,7 @@
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 3000
|
||||
"Port": 5001
|
||||
}
|
||||
],
|
||||
"QoSOptions": {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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++)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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>();
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
@ -79,6 +79,8 @@ namespace Ocelot.UnitTests.Middleware
|
||||
del.Invoke(_downstreamContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ThenTheFuncIsInThePipeline()
|
||||
{
|
||||
_counter.ShouldBe(1);
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user