Feature/fix #463 (#506)

* #463 save both files

* #463 made it so we dont save to disk on startup unless using admin api
This commit is contained in:
Tom Pallister
2018-07-27 23:13:22 +01:00
committed by GitHub
parent 9f4448378a
commit 1817564ea5
8 changed files with 484 additions and 274 deletions

View File

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

View File

@ -31,6 +31,7 @@ using static Ocelot.Infrastructure.Wait;
namespace Ocelot.AcceptanceTests
{
using Configuration.Repository;
using Microsoft.Net.Http.Headers;
using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue;
@ -928,5 +929,35 @@ namespace Ocelot.AcceptanceTests
var content = await response.Content.ReadAsStringAsync();
content.ShouldBe(expectedBody);
}
public void GivenOcelotIsRunningWithBlowingUpDiskRepo(IFileConfigurationRepository fake)
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddSingleton<IFileConfigurationRepository>(fake);
s.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
}
}

View File

@ -276,9 +276,23 @@ namespace Ocelot.IntegrationTests
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration"))
.And(x => ThenTheResponseShouldBe(updatedConfiguration))
.And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration))
.BDDfy();
}
private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected)
{
var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json";
var resultText = File.ReadAllText(ocelotJsonPath);
var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented);
resultText.ShouldBe(expectedText);
var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json";
resultText = File.ReadAllText(environmentSpecificPath);
expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented);
resultText.ShouldBe(expectedText);
}
[Fact]
public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute()
{

View File

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