mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 11:50:49 +08:00 
			
		
		
		
	Feat/monorepo (#734)
* copied everything from repos back to ocelot repo * added src projects to sln * removed all test projects that have no tests * added all test projects to sln * removed test not on master * merged unit tests * merged acceptance tests * merged integration tests * fixed namepaces * build script creates packages for all projects * updated docs to make sure no references to external repos that we will remove * +semver: breaking
This commit is contained in:
		
							
								
								
									
										838
									
								
								test/Ocelot.IntegrationTests/AdministrationTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										838
									
								
								test/Ocelot.IntegrationTests/AdministrationTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,838 @@
 | 
			
		||||
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 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;
 | 
			
		||||
using Ocelot.Administration;
 | 
			
		||||
using Ocelot.IntegrationTests;
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
                .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()
 | 
			
		||||
        {
 | 
			
		||||
            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: false)
 | 
			
		||||
                       .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                   config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                   config.AddEnvironmentVariables();
 | 
			
		||||
               })
 | 
			
		||||
               .ConfigureServices(x =>
 | 
			
		||||
               {
 | 
			
		||||
                   x.AddOcelot()
 | 
			
		||||
                       .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: false)
 | 
			
		||||
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                    config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                    config.AddEnvironmentVariables();
 | 
			
		||||
                })
 | 
			
		||||
                .ConfigureServices(x => {
 | 
			
		||||
                    x.AddSingleton(_webHostBuilder);
 | 
			
		||||
                    x.AddOcelot()
 | 
			
		||||
                    .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: false)
 | 
			
		||||
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                    config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                    config.AddEnvironmentVariables();
 | 
			
		||||
                })
 | 
			
		||||
                .ConfigureServices(x =>
 | 
			
		||||
                {
 | 
			
		||||
                    x.AddOcelot()
 | 
			
		||||
                        .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: false)
 | 
			
		||||
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                    config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                    config.AddEnvironmentVariables();
 | 
			
		||||
                })
 | 
			
		||||
                .ConfigureServices(x => {
 | 
			
		||||
                    x.AddSingleton(_webHostBuilder);
 | 
			
		||||
                    x.AddOcelot()
 | 
			
		||||
                        .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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								test/Ocelot.IntegrationTests/BearerToken.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								test/Ocelot.IntegrationTests/BearerToken.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
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; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										224
									
								
								test/Ocelot.IntegrationTests/CacheManagerTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								test/Ocelot.IntegrationTests/CacheManagerTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.IntegrationTests
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.IO;
 | 
			
		||||
    using System.Net;
 | 
			
		||||
    using System.Net.Http;
 | 
			
		||||
    using System.Net.Http.Headers;
 | 
			
		||||
    using Configuration.File;
 | 
			
		||||
    using DependencyInjection;
 | 
			
		||||
    using global::CacheManager.Core;
 | 
			
		||||
    using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
    using Microsoft.Extensions.Configuration;
 | 
			
		||||
    using Microsoft.Extensions.Logging;
 | 
			
		||||
    using Newtonsoft.Json;
 | 
			
		||||
    using Ocelot.Middleware;
 | 
			
		||||
    using Shouldly;
 | 
			
		||||
    using TestStack.BDDfy;
 | 
			
		||||
    using Xunit;
 | 
			
		||||
    using Ocelot.Administration;
 | 
			
		||||
    using Ocelot.IntegrationTests;
 | 
			
		||||
    using Ocelot.Cache.CacheManager;
 | 
			
		||||
 | 
			
		||||
    public class CacheManagerTests : 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 CacheManagerTests()
 | 
			
		||||
        {
 | 
			
		||||
            _httpClient = new HttpClient();
 | 
			
		||||
            _httpClientTwo = new HttpClient();
 | 
			
		||||
            _ocelotBaseUrl = "http://localhost:5000";
 | 
			
		||||
            _httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [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();
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        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 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 =>
 | 
			
		||||
                {
 | 
			
		||||
                    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 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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,10 +22,17 @@
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Administration\Ocelot.Administration.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Cache.CacheManager\Ocelot.Cache.CacheManager.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Provider.Consul\Ocelot.Provider.Consul.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Provider.Eureka\Ocelot.Provider.Eureka.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Provider.Polly\Ocelot.Provider.Polly.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\..\src\Ocelot.Provider.Rafty\Ocelot.Provider.Rafty.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.4" />
 | 
			
		||||
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Data.SQLite" Version="2.2.0" />
 | 
			
		||||
    <PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
 | 
			
		||||
      <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
@@ -41,6 +48,8 @@
 | 
			
		||||
    <PackageReference Include="xunit" Version="2.3.1" />
 | 
			
		||||
    <PackageReference Include="Shouldly" Version="3.0.0" />
 | 
			
		||||
    <PackageReference Include="TestStack.BDDfy" Version="4.3.2" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Data.SQLite" Version="2.2.0" />
 | 
			
		||||
    <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" />
 | 
			
		||||
    <PackageReference Include="IdentityServer4" Version="2.2.0" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										516
									
								
								test/Ocelot.IntegrationTests/RaftTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								test/Ocelot.IntegrationTests/RaftTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,516 @@
 | 
			
		||||
namespace Ocelot.IntegrationTests
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.IO;
 | 
			
		||||
    using System.Linq;
 | 
			
		||||
    using System.Net.Http;
 | 
			
		||||
    using System.Net.Http.Headers;
 | 
			
		||||
    using System.Threading;
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Administration;
 | 
			
		||||
    using Configuration.File;
 | 
			
		||||
    using DependencyInjection;
 | 
			
		||||
    using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
    using Microsoft.Data.Sqlite;
 | 
			
		||||
    using Microsoft.Extensions.Configuration;
 | 
			
		||||
    using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
    using Middleware;
 | 
			
		||||
    using Newtonsoft.Json;
 | 
			
		||||
    using Shouldly;
 | 
			
		||||
    using Xunit;
 | 
			
		||||
    using Xunit.Abstractions;
 | 
			
		||||
    using Ocelot.Administration;
 | 
			
		||||
    using Ocelot.IntegrationTests;
 | 
			
		||||
    using Ocelot.Provider.Rafty;
 | 
			
		||||
    using Ocelot.Infrastructure;
 | 
			
		||||
    using Rafty.Infrastructure;
 | 
			
		||||
    using Wait = Rafty.Infrastructure.Wait;
 | 
			
		||||
 | 
			
		||||
    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 Wait.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 Wait.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 Wait.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 Wait.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: false)
 | 
			
		||||
                            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
 | 
			
		||||
                        config.AddJsonFile("ocelot.json", false, false);
 | 
			
		||||
                        config.AddJsonFile("peers.json", optional: true, reloadOnChange: false);
 | 
			
		||||
#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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user