mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 07:08:14 +08:00
Raft round 2 (#182)
* brought in rafty * moved raft classes into Ocelot and deleted from int project * started to set up rafty in Ocelot * RAFTY INSIDE OCELOT...WOOT * more work adding rafty...just need to get auth working now * rudimentary authenticated raft requests working * asyn await stuff * hacked rafty into the fileconfigurationcontroller...everything seems to be working roughly but I have a lot of refactoring to do * updated to latest rafty that doesnt need an id * hacky but all tests passing * changed admin area set up to use builder not configuration.json, changed admin area auth to use client credentials * missing code coverage * ignore raft sectionf for code coverage * ignore raft sectionf for code coverage * back to normal filters * try exclude attr * missed these * moved client secret to builder for authentication and updated docs * lock to try and fix error accessing identity server created temprsa file on build server * updated postman scripts and changed Ocelot to not always use type handling as this looked crap when manually accessing the configuration endpoint * added rafty docs * changes I missed * added serialisation code we need for rafty to process commands when they proxy to leader * moved controllers into their feature slices
This commit is contained in:
@ -12,9 +12,9 @@ using Ocelot.AcceptanceTests.Caching;
|
||||
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
public class Startup
|
||||
public class AcceptanceTestsStartup
|
||||
{
|
||||
public Startup(IHostingEnvironment env)
|
||||
public AcceptanceTestsStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
@ -41,7 +41,7 @@ namespace Ocelot.AcceptanceTests
|
||||
}
|
||||
}
|
||||
|
||||
public class Startup_WithCustomCacheHandle : Startup
|
||||
public class Startup_WithCustomCacheHandle : AcceptanceTestsStartup
|
||||
{
|
||||
public Startup_WithCustomCacheHandle(IHostingEnvironment env) : base(env) { }
|
||||
|
||||
@ -60,7 +60,7 @@ namespace Ocelot.AcceptanceTests
|
||||
}
|
||||
}
|
||||
|
||||
public class Startup_WithConsul_And_CustomCacheHandle : Startup
|
||||
public class Startup_WithConsul_And_CustomCacheHandle : AcceptanceTestsStartup
|
||||
{
|
||||
public Startup_WithConsul_And_CustomCacheHandle(IHostingEnvironment env) : base(env) { }
|
||||
|
@ -83,7 +83,7 @@ namespace Ocelot.AcceptanceTests
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder
|
||||
.UseStartup<Startup>());
|
||||
.UseStartup<AcceptanceTestsStartup>());
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
@ -103,7 +103,7 @@ namespace Ocelot.AcceptanceTests
|
||||
});
|
||||
|
||||
_ocelotServer = new TestServer(_webHostBuilder
|
||||
.UseStartup<Startup>());
|
||||
.UseStartup<AcceptanceTestsStartup>());
|
||||
|
||||
_ocelotClient = _ocelotServer.CreateClient();
|
||||
}
|
||||
@ -157,7 +157,6 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||
|
||||
response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
|
@ -39,13 +39,7 @@ namespace Ocelot.IntegrationTests
|
||||
[Fact]
|
||||
public void should_return_response_401_with_call_re_routes_controller()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
}
|
||||
};
|
||||
var configuration = new FileConfiguration();
|
||||
|
||||
this.Given(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenOcelotIsRunning())
|
||||
@ -57,13 +51,7 @@ namespace Ocelot.IntegrationTests
|
||||
[Fact]
|
||||
public void should_return_response_200_with_call_re_routes_controller()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
}
|
||||
};
|
||||
var configuration = new FileConfiguration();
|
||||
|
||||
this.Given(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenOcelotIsRunning())
|
||||
@ -77,13 +65,7 @@ namespace Ocelot.IntegrationTests
|
||||
[Fact]
|
||||
public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
}
|
||||
};
|
||||
var configuration = new FileConfiguration();
|
||||
|
||||
this.Given(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet())
|
||||
@ -102,7 +84,6 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration",
|
||||
RequestIdKey = "RequestId",
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
@ -160,7 +141,6 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
},
|
||||
ReRoutes = new List<FileReRoute>()
|
||||
{
|
||||
@ -189,7 +169,6 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
},
|
||||
ReRoutes = new List<FileReRoute>()
|
||||
{
|
||||
@ -234,7 +213,6 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "/administration"
|
||||
},
|
||||
ReRoutes = new List<FileReRoute>()
|
||||
{
|
||||
@ -289,7 +267,7 @@ namespace Ocelot.IntegrationTests
|
||||
.ConfigureServices(x => {
|
||||
x.AddSingleton(_webHostBuilderTwo);
|
||||
})
|
||||
.UseStartup<Startup>();
|
||||
.UseStartup<IntegrationTestsStartup>();
|
||||
|
||||
_builderTwo = _webHostBuilderTwo.Build();
|
||||
|
||||
@ -327,7 +305,6 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||
|
||||
response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
@ -356,9 +333,7 @@ namespace Ocelot.IntegrationTests
|
||||
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", "secret"),
|
||||
new KeyValuePair<string, string>("grant_type", "password")
|
||||
new KeyValuePair<string, string>("grant_type", "client_credentials")
|
||||
};
|
||||
var content = new FormUrlEncodedContent(formData);
|
||||
|
||||
@ -380,7 +355,7 @@ namespace Ocelot.IntegrationTests
|
||||
.ConfigureServices(x => {
|
||||
x.AddSingleton(_webHostBuilder);
|
||||
})
|
||||
.UseStartup<Startup>();
|
||||
.UseStartup<IntegrationTestsStartup>();
|
||||
|
||||
_builder = _webHostBuilder.Build();
|
||||
|
||||
|
@ -11,9 +11,9 @@ using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBui
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class Startup
|
||||
public class IntegrationTestsStartup
|
||||
{
|
||||
public Startup(IHostingEnvironment env)
|
||||
public IntegrationTestsStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
@ -38,7 +38,9 @@ namespace Ocelot.IntegrationTests
|
||||
.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
services.AddOcelot(Configuration);
|
||||
services.AddOcelot(Configuration)
|
||||
.AddCacheManager(settings)
|
||||
.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
@ -1,5 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
@ -13,39 +12,35 @@
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="configuration.json;appsettings.json;idsrv3test.pfx">
|
||||
<None Update="peers.json;configuration.json;appsettings.json;idsrv3test.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
|
||||
<ProjectReference Include="..\Ocelot.ManualTest\Ocelot.ManualTest.csproj" />
|
||||
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.2" />
|
||||
<PackageReference Include="Shouldly" Version="3.0.0-beta0003" />
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20171031-01"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.DotNet.InternalAbstractions" Version="1.0.500-preview2-1-003177"/>
|
||||
<PackageReference Include="xunit" Version="2.3.1"/>
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.2"/>
|
||||
<PackageReference Include="Shouldly" Version="3.0.0-beta0003"/>
|
||||
<PackageReference Include="TestStack.BDDfy" Version="4.3.2"/>
|
||||
<PackageReference Include="Consul" Version="0.7.2.3"/>
|
||||
<PackageReference Include="Rafty" Version="0.4.2"/>
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
55
test/Ocelot.IntegrationTests/RaftStartup.cs
Normal file
55
test/Ocelot.IntegrationTests/RaftStartup.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
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.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class RaftStartup
|
||||
{
|
||||
public RaftStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||
.AddJsonFile("peers.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile("configuration.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddOcelot(Configuration)
|
||||
.AddAdministration("/administration", "secret")
|
||||
.AddRafty();
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
|
||||
//this is from Ocelot...so we need to move stuff below into it...
|
||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
app.UseOcelot().Wait();
|
||||
}
|
||||
}
|
||||
}
|
431
test/Ocelot.IntegrationTests/RaftTests.cs
Normal file
431
test/Ocelot.IntegrationTests/RaftTests.cs
Normal file
@ -0,0 +1,431 @@
|
||||
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.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using static Rafty.Infrastructure.Wait;
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class RaftTests : IDisposable
|
||||
{
|
||||
private List<IWebHost> _builders;
|
||||
private List<IWebHostBuilder> _webHostBuilders;
|
||||
private List<Thread> _threads;
|
||||
private FilePeers _peers;
|
||||
private HttpClient _httpClient;
|
||||
private HttpClient _httpClientForAssertions;
|
||||
private string _ocelotBaseUrl;
|
||||
private BearerToken _token;
|
||||
private HttpResponseMessage _response;
|
||||
private static object _lock = new object();
|
||||
|
||||
public RaftTests()
|
||||
{
|
||||
_httpClientForAssertions = new HttpClient();
|
||||
_httpClient = new HttpClient();
|
||||
_ocelotBaseUrl = "http://localhost:5000";
|
||||
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
|
||||
_webHostBuilders = new List<IWebHostBuilder>();
|
||||
_builders = new List<IWebHost>();
|
||||
_threads = new List<Thread>();
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var builder in _builders)
|
||||
{
|
||||
builder?.Dispose();
|
||||
}
|
||||
|
||||
foreach (var peer in _peers.Peers)
|
||||
{
|
||||
File.Delete(peer.HostAndPort.Replace("/","").Replace(":",""));
|
||||
File.Delete($"{peer.HostAndPort.Replace("/","").Replace(":","")}.db");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_persist_command_to_five_servers()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
var updatedConfiguration = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
},
|
||||
ReRoutes = new List<FileReRoute>()
|
||||
{
|
||||
new FileReRoute()
|
||||
{
|
||||
DownstreamHost = "127.0.0.1",
|
||||
DownstreamPort = 80,
|
||||
DownstreamScheme = "http",
|
||||
DownstreamPathTemplate = "/geoffrey",
|
||||
UpstreamHttpMethod = new List<string> { "get" },
|
||||
UpstreamPathTemplate = "/"
|
||||
},
|
||||
new FileReRoute()
|
||||
{
|
||||
DownstreamHost = "123.123.123",
|
||||
DownstreamPort = 443,
|
||||
DownstreamScheme = "https",
|
||||
DownstreamPathTemplate = "/blooper/{productId}",
|
||||
UpstreamHttpMethod = new List<string> { "post" },
|
||||
UpstreamPathTemplate = "/test"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var command = new UpdateFileConfiguration(updatedConfiguration);
|
||||
GivenThereIsAConfiguration(configuration);
|
||||
GivenFiveServersAreRunning();
|
||||
GivenALeaderIsElected();
|
||||
GivenIHaveAnOcelotToken("/administration");
|
||||
WhenISendACommandIntoTheCluster(command);
|
||||
ThenTheCommandIsReplicatedToAllStateMachines(command);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_persist_command_to_five_servers_when_using_administration_api()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
};
|
||||
|
||||
var updatedConfiguration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>()
|
||||
{
|
||||
new FileReRoute()
|
||||
{
|
||||
DownstreamHost = "127.0.0.1",
|
||||
DownstreamPort = 80,
|
||||
DownstreamScheme = "http",
|
||||
DownstreamPathTemplate = "/geoffrey",
|
||||
UpstreamHttpMethod = new List<string> { "get" },
|
||||
UpstreamPathTemplate = "/"
|
||||
},
|
||||
new FileReRoute()
|
||||
{
|
||||
DownstreamHost = "123.123.123",
|
||||
DownstreamPort = 443,
|
||||
DownstreamScheme = "https",
|
||||
DownstreamPathTemplate = "/blooper/{productId}",
|
||||
UpstreamHttpMethod = new List<string> { "post" },
|
||||
UpstreamPathTemplate = "/test"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var command = new UpdateFileConfiguration(updatedConfiguration);
|
||||
GivenThereIsAConfiguration(configuration);
|
||||
GivenFiveServersAreRunning();
|
||||
GivenALeaderIsElected();
|
||||
GivenIHaveAnOcelotToken("/administration");
|
||||
GivenIHaveAddedATokenToMyRequest();
|
||||
WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration);
|
||||
ThenTheCommandIsReplicatedToAllStateMachines(command);
|
||||
}
|
||||
|
||||
private void WhenISendACommandIntoTheCluster(UpdateFileConfiguration command)
|
||||
{
|
||||
var p = _peers.Peers.First();
|
||||
var json = JsonConvert.SerializeObject(command,new JsonSerializerSettings() {
|
||||
TypeNameHandling = TypeNameHandling.All
|
||||
});
|
||||
var httpContent = new StringContent(json);
|
||||
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
||||
using(var httpClient = new HttpClient())
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||
var response = httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent).GetAwaiter().GetResult();
|
||||
response.EnsureSuccessStatusCode();
|
||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
var result = JsonConvert.DeserializeObject<OkResponse<UpdateFileConfiguration>>(content);
|
||||
result.Command.Configuration.ReRoutes.Count.ShouldBe(2);
|
||||
}
|
||||
|
||||
//dirty sleep to make sure command replicated...
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
while(stopwatch.ElapsedMilliseconds < 10000)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expected)
|
||||
{
|
||||
//dirty sleep to give a chance to replicate...
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
while(stopwatch.ElapsedMilliseconds < 2000)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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 = _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration").Result;
|
||||
var json = result.Content.ReadAsStringAsync().Result;
|
||||
var response = JsonConvert.DeserializeObject<FileConfiguration>(json, new JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All});
|
||||
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.Configuration.GlobalConfiguration.RequestIdKey);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
|
||||
for (var i = 0; i < response.ReRoutes.Count; i++)
|
||||
{
|
||||
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamHost);
|
||||
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamPathTemplate);
|
||||
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamPort);
|
||||
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.Configuration.ReRoutes[i].DownstreamScheme);
|
||||
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.Configuration.ReRoutes[i].UpstreamPathTemplate);
|
||||
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.Configuration.ReRoutes[i].UpstreamHttpMethod);
|
||||
}
|
||||
passed++;
|
||||
}
|
||||
|
||||
return passed == 5;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var commandOnAllStateMachines = WaitFor(20000).Until(() => CommandCalledOnAllStateMachines());
|
||||
commandOnAllStateMachines.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void ThenTheResponseShouldBe(FileConfiguration expected)
|
||||
{
|
||||
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||
|
||||
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
|
||||
for (var i = 0; i < response.ReRoutes.Count; i++)
|
||||
{
|
||||
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate);
|
||||
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenIGetUrlOnTheApiGateway(string url)
|
||||
{
|
||||
_response = _httpClient.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 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 GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = $"{Directory.GetCurrentDirectory()}/configuration.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}/configuration.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())
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
x.AddSingleton(webHostBuilder);
|
||||
x.AddSingleton(new NodeId(url));
|
||||
})
|
||||
.UseStartup<RaftStartup>();
|
||||
|
||||
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)
|
||||
{
|
||||
var thread = new Thread(() => GivenAServerIsRunning(peer.HostAndPort));
|
||||
thread.Start();
|
||||
_threads.Add(thread);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenALeaderIsElected()
|
||||
{
|
||||
//dirty sleep to make sure we have a leader
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
while(stopwatch.ElapsedMilliseconds < 20000)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISendACommandIntoTheCluster(FakeCommand command)
|
||||
{
|
||||
var p = _peers.Peers.First();
|
||||
var json = JsonConvert.SerializeObject(command,new JsonSerializerSettings() {
|
||||
TypeNameHandling = TypeNameHandling.All
|
||||
});
|
||||
var httpContent = new StringContent(json);
|
||||
httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
|
||||
using(var httpClient = new HttpClient())
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||
var response = httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent).GetAwaiter().GetResult();
|
||||
response.EnsureSuccessStatusCode();
|
||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
var result = JsonConvert.DeserializeObject<OkResponse<FakeCommand>>(content);
|
||||
result.Command.Value.ShouldBe(command.Value);
|
||||
}
|
||||
|
||||
//dirty sleep to make sure command replicated...
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
while(stopwatch.ElapsedMilliseconds < 10000)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenTheCommandIsReplicatedToAllStateMachines(FakeCommand command)
|
||||
{
|
||||
//dirty sleep to give a chance to replicate...
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
while(stopwatch.ElapsedMilliseconds < 2000)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CommandCalledOnAllStateMachines()
|
||||
{
|
||||
try
|
||||
{
|
||||
var passed = 0;
|
||||
foreach (var peer in _peers.Peers)
|
||||
{
|
||||
string fsmData;
|
||||
fsmData = File.ReadAllText(peer.HostAndPort.Replace("/","").Replace(":",""));
|
||||
fsmData.ShouldNotBeNullOrEmpty();
|
||||
var fakeCommand = JsonConvert.DeserializeObject<FakeCommand>(fsmData);
|
||||
fakeCommand.Value.ShouldBe(command.Value);
|
||||
passed++;
|
||||
}
|
||||
|
||||
return passed == 5;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var commandOnAllStateMachines = WaitFor(20000).Until(() => CommandCalledOnAllStateMachines());
|
||||
commandOnAllStateMachines.ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -95,7 +95,7 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
x.AddSingleton(_webHostBuilder);
|
||||
})
|
||||
.UseStartup<Startup>();
|
||||
.UseStartup<IntegrationTestsStartup>();
|
||||
|
||||
_builder = _webHostBuilder.Build();
|
||||
|
||||
|
18
test/Ocelot.IntegrationTests/peers.json
Normal file
18
test/Ocelot.IntegrationTests/peers.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"Peers": [{
|
||||
"HostAndPort": "http://localhost:5000"
|
||||
},
|
||||
{
|
||||
"HostAndPort": "http://localhost:5002"
|
||||
},
|
||||
{
|
||||
"HostAndPort": "http://localhost:5003"
|
||||
},
|
||||
{
|
||||
"HostAndPort": "http://localhost:5004"
|
||||
},
|
||||
{
|
||||
"HostAndPort": "http://localhost:5001"
|
||||
}
|
||||
]
|
||||
}
|
@ -11,9 +11,9 @@ using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBui
|
||||
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
public class Startup
|
||||
public class ManualTestStartup
|
||||
{
|
||||
public Startup(IHostingEnvironment env)
|
||||
public ManualTestStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
@ -45,7 +45,8 @@ namespace Ocelot.ManualTest
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot(Configuration);
|
||||
services.AddOcelot(Configuration)
|
||||
.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
@ -15,7 +15,7 @@ namespace Ocelot.ManualTest
|
||||
builder.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>();
|
||||
.UseStartup<ManualTestStartup>();
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
}
|
||||
|
@ -300,12 +300,11 @@
|
||||
"DownstreamHost": "www.bbc.co.uk",
|
||||
"DownstreamPort": 80,
|
||||
"UpstreamPathTemplate": "/bbc/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
],
|
||||
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": "OcRequestId",
|
||||
"AdministrationPath": "/administration"
|
||||
"RequestIdKey": "OcRequestId"
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.UnitTests.TestData;
|
||||
|
||||
@ -36,6 +37,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
private Mock<IRateLimitOptionsCreator> _rateLimitOptions;
|
||||
private Mock<IRegionCreator> _regionCreator;
|
||||
private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
|
||||
private Mock<IAdministrationPath> _adminPath;
|
||||
|
||||
public FileConfigurationCreatorTests()
|
||||
{
|
||||
@ -52,13 +54,23 @@ namespace Ocelot.UnitTests.Configuration
|
||||
_rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
|
||||
_regionCreator = new Mock<IRegionCreator>();
|
||||
_httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
|
||||
_adminPath = new Mock<IAdministrationPath>();
|
||||
|
||||
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
|
||||
_fileConfig.Object, _validator.Object, _logger.Object,
|
||||
_fileConfig.Object,
|
||||
_validator.Object,
|
||||
_logger.Object,
|
||||
_claimsToThingCreator.Object,
|
||||
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
|
||||
_serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
|
||||
_rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.Object);
|
||||
_authOptionsCreator.Object,
|
||||
_upstreamTemplatePatternCreator.Object,
|
||||
_requestIdKeyCreator.Object,
|
||||
_serviceProviderConfigCreator.Object,
|
||||
_qosOptionsCreator.Object,
|
||||
_fileReRouteOptionsCreator.Object,
|
||||
_rateLimitOptions.Object,
|
||||
_regionCreator.Object,
|
||||
_httpHandlerOptionsCreator.Object,
|
||||
_adminPath.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -91,7 +91,6 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
|
||||
{
|
||||
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
@ -126,7 +125,6 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void ThenTheFollowingIsReturned(FileConfiguration expected)
|
||||
{
|
||||
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||
@ -155,7 +153,6 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
var globalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "asdas",
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Port = 198,
|
||||
@ -185,7 +182,6 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
var globalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
AdministrationPath = "testy",
|
||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||
{
|
||||
Port = 198,
|
||||
|
@ -9,7 +9,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
[Fact]
|
||||
public void happy_path_only_exists_for_test_coverage_even_uncle_bob_probably_wouldnt_test_this()
|
||||
{
|
||||
var result = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
|
||||
var result = IdentityServerConfigurationCreator.GetIdentityServerConfiguration("secret");
|
||||
result.ApiName.ShouldBe("admin");
|
||||
}
|
||||
}
|
||||
|
@ -1,117 +0,0 @@
|
||||
using Ocelot.Configuration.Authentication;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Moq;
|
||||
using IdentityServer4.Validation;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
public class OcelotResourceOwnerPasswordValidatorTests
|
||||
{
|
||||
private OcelotResourceOwnerPasswordValidator _validator;
|
||||
private Mock<IHashMatcher> _matcher;
|
||||
private string _userName;
|
||||
private string _password;
|
||||
private ResourceOwnerPasswordValidationContext _context;
|
||||
private Mock<IIdentityServerConfiguration> _config;
|
||||
private User _user;
|
||||
|
||||
public OcelotResourceOwnerPasswordValidatorTests()
|
||||
{
|
||||
_matcher = new Mock<IHashMatcher>();
|
||||
_config = new Mock<IIdentityServerConfiguration>();
|
||||
_validator = new OcelotResourceOwnerPasswordValidator(_matcher.Object, _config.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_success()
|
||||
{
|
||||
this.Given(x => GivenTheUserName("tom"))
|
||||
.And(x => GivenThePassword("password"))
|
||||
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||
.And(x => GivenTheMatcherReturns(true))
|
||||
.When(x => WhenIValidate())
|
||||
.Then(x => ThenTheUserIsValidated())
|
||||
.And(x => ThenTheMatcherIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_fail_when_no_user()
|
||||
{
|
||||
this.Given(x => GivenTheUserName("bob"))
|
||||
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||
.And(x => GivenTheMatcherReturns(true))
|
||||
.When(x => WhenIValidate())
|
||||
.Then(x => ThenTheUserIsNotValidated())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_fail_when_password_doesnt_match()
|
||||
{
|
||||
this.Given(x => GivenTheUserName("tom"))
|
||||
.And(x => GivenThePassword("password"))
|
||||
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||
.And(x => GivenTheMatcherReturns(false))
|
||||
.When(x => WhenIValidate())
|
||||
.Then(x => ThenTheUserIsNotValidated())
|
||||
.And(x => ThenTheMatcherIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheMatcherIsCalledCorrectly()
|
||||
{
|
||||
_matcher
|
||||
.Verify(x => x.Match(_password, _user.Salt, _user.Hash), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenThePassword(string password)
|
||||
{
|
||||
_password = password;
|
||||
}
|
||||
|
||||
private void GivenTheUserIs(User user)
|
||||
{
|
||||
_user = user;
|
||||
_config
|
||||
.Setup(x => x.Users)
|
||||
.Returns(new List<User>{_user});
|
||||
}
|
||||
|
||||
private void GivenTheMatcherReturns(bool expected)
|
||||
{
|
||||
_matcher
|
||||
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Returns(expected);
|
||||
}
|
||||
|
||||
private void GivenTheUserName(string userName)
|
||||
{
|
||||
_userName = userName;
|
||||
}
|
||||
|
||||
private void WhenIValidate()
|
||||
{
|
||||
_context = new ResourceOwnerPasswordValidationContext
|
||||
{
|
||||
UserName = _userName,
|
||||
Password = _password
|
||||
};
|
||||
_validator.ValidateAsync(_context).Wait();
|
||||
}
|
||||
|
||||
private void ThenTheUserIsValidated()
|
||||
{
|
||||
_context.Result.IsError.ShouldBe(false);
|
||||
}
|
||||
|
||||
private void ThenTheUserIsNotValidated()
|
||||
{
|
||||
_context.Result.IsError.ShouldBe(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,20 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.Controllers;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Newtonsoft.Json;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.UnitTests.Controllers
|
||||
{
|
||||
@ -19,18 +25,21 @@ namespace Ocelot.UnitTests.Controllers
|
||||
private Mock<IFileConfigurationSetter> _configSetter;
|
||||
private IActionResult _result;
|
||||
private FileConfiguration _fileConfiguration;
|
||||
private Mock<IServiceProvider> _provider;
|
||||
private Mock<INode> _node;
|
||||
|
||||
public FileConfigurationControllerTests()
|
||||
{
|
||||
_provider = new Mock<IServiceProvider>();
|
||||
_configGetter = new Mock<IFileConfigurationProvider>();
|
||||
_configSetter = new Mock<IFileConfigurationSetter>();
|
||||
_controller = new FileConfigurationController(_configGetter.Object, _configSetter.Object);
|
||||
_controller = new FileConfigurationController(_configGetter.Object, _configSetter.Object, _provider.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_get_file_configuration()
|
||||
{
|
||||
var expected = new OkResponse<FileConfiguration>(new FileConfiguration());
|
||||
var expected = new Responses.OkResponse<FileConfiguration>(new FileConfiguration());
|
||||
|
||||
this.Given(x => x.GivenTheGetConfigurationReturns(expected))
|
||||
.When(x => x.WhenIGetTheFileConfiguration())
|
||||
@ -41,7 +50,7 @@ namespace Ocelot.UnitTests.Controllers
|
||||
[Fact]
|
||||
public void should_return_error_when_cannot_get_config()
|
||||
{
|
||||
var expected = new ErrorResponse<FileConfiguration>(It.IsAny<Error>());
|
||||
var expected = new Responses.ErrorResponse<FileConfiguration>(It.IsAny<Error>());
|
||||
|
||||
this.Given(x => x.GivenTheGetConfigurationReturns(expected))
|
||||
.When(x => x.WhenIGetTheFileConfiguration())
|
||||
@ -56,26 +65,81 @@ namespace Ocelot.UnitTests.Controllers
|
||||
var expected = new FileConfiguration();
|
||||
|
||||
this.Given(x => GivenTheFileConfiguration(expected))
|
||||
.And(x => GivenTheConfigSetterReturnsAnError(new OkResponse()))
|
||||
.And(x => GivenTheConfigSetterReturns(new OkResponse()))
|
||||
.When(x => WhenIPostTheFileConfiguration())
|
||||
.Then(x => x.ThenTheConfigrationSetterIsCalledCorrectly())
|
||||
.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()
|
||||
{
|
||||
var expected = new FileConfiguration();
|
||||
|
||||
this.Given(x => GivenTheFileConfiguration(expected))
|
||||
.And(x => GivenTheConfigSetterReturnsAnError(new ErrorResponse(new FakeError())))
|
||||
.And(x => GivenTheConfigSetterReturns(new ErrorResponse(new FakeError())))
|
||||
.When(x => WhenIPostTheFileConfiguration())
|
||||
.Then(x => x.ThenTheConfigrationSetterIsCalledCorrectly())
|
||||
.And(x => ThenTheResponseIs<BadRequestObjectResult>())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheConfigSetterReturnsAnError(Response response)
|
||||
|
||||
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>()))
|
||||
.Returns(new Rafty.Concensus.OkResponse<UpdateFileConfiguration>(new UpdateFileConfiguration(new FileConfiguration())));
|
||||
}
|
||||
|
||||
private void GivenTheNodeReturnsError()
|
||||
{
|
||||
_node
|
||||
.Setup(x => x.Accept(It.IsAny<UpdateFileConfiguration>()))
|
||||
.Returns(new Rafty.Concensus.ErrorResponse<UpdateFileConfiguration>("error", new UpdateFileConfiguration(new FileConfiguration())));
|
||||
}
|
||||
|
||||
private void GivenTheConfigSetterReturns(Response response)
|
||||
{
|
||||
_configSetter
|
||||
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
|
||||
@ -103,7 +167,7 @@ namespace Ocelot.UnitTests.Controllers
|
||||
_result.ShouldBeOfType<T>();
|
||||
}
|
||||
|
||||
private void GivenTheGetConfigurationReturns(Response<FileConfiguration> fileConfiguration)
|
||||
private void GivenTheGetConfigurationReturns(Ocelot.Responses.Response<FileConfiguration> fileConfiguration)
|
||||
{
|
||||
_configGetter
|
||||
.Setup(x => x.Get())
|
||||
@ -128,4 +192,4 @@ namespace Ocelot.UnitTests.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Ocelot.Controllers;
|
||||
using Ocelot.Cache;
|
||||
using System;
|
||||
using Moq;
|
||||
using Ocelot.Cache;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -75,6 +75,16 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
.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_use_logger_factory()
|
||||
{
|
||||
@ -85,6 +95,13 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheCorrectAdminPathIsRegitered()
|
||||
{
|
||||
_serviceProvider = _services.BuildServiceProvider();
|
||||
var path = _serviceProvider.GetService<IAdministrationPath>();
|
||||
path.Path.ShouldBe("/administration");
|
||||
}
|
||||
|
||||
private void OnlyOneVersionOfEachCacheIsRegistered()
|
||||
{
|
||||
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
||||
@ -111,6 +128,18 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
}
|
||||
}
|
||||
|
||||
private void WhenISetUpRafty()
|
||||
{
|
||||
try
|
||||
{
|
||||
_ocelotBuilder.AddAdministration("/administration", "secret").AddRafty();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ex = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThenAnOcelotBuilderIsReturned()
|
||||
{
|
||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||
|
45
test/Ocelot.UnitTests/Raft/OcelotFiniteStateMachineTests.cs
Normal file
45
test/Ocelot.UnitTests/Raft/OcelotFiniteStateMachineTests.cs
Normal file
@ -0,0 +1,45 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user