using System.Collections.Generic; using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; using Ocelot.Configuration.Creator; using Ocelot.Configuration.Provider; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Responses; using Ocelot.Values; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.UnitTests.DownstreamRouteFinder { public class DownstreamRouteFinderTests { private readonly IDownstreamRouteFinder _downstreamRouteFinder; private readonly Mock _mockMatcher; private readonly Mock _finder; private string _upstreamUrlPath; private Response _result; private List _reRoutesConfig; private OcelotConfiguration _config; private Response _match; private string _upstreamHttpMethod; private string _upstreamHost; public DownstreamRouteFinderTests() { _mockMatcher = new Mock(); _finder = new Mock(); _downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object); } [Fact] public void should_return_highest_priority_when_first() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) .Build() }, string.Empty, serviceProviderConfig)) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .Build() ))) .BDDfy(); } [Fact] public void should_return_highest_priority_when_lowest() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 0)) .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .Build() }, string.Empty, serviceProviderConfig)) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamTemplatePattern(new UpstreamPathTemplate("test", 1)) .WithUpstreamHttpMethod(new List { "Post" }) .Build() ))) .BDDfy(); } [Fact] public void should_return_route() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>( new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_not_append_slash_to_upstream_url_path() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) .And(x =>x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>( new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher")) .BDDfy(); } [Fact] public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .BDDfy(); } [Fact] public void should_return_correct_route_for_http_verb() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPathForAPost") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() ))) .BDDfy(); } [Fact] public void should_not_return_route() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("somPath") .WithUpstreamPathTemplate("somePath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("somePath", 1)) .Build(), }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(false)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenAnErrorResponseIsReturned()) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get", "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() ))) .BDDfy(); } [Fact] public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List()) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Post" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() ))) .BDDfy(); } [Fact] public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) .And( x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenAnErrorResponseIsReturned()) .And(x => x.ThenTheUrlMatcherIsNotCalled()) .BDDfy(); } [Fact] public void should_return_route_when_host_matches() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) .And(x => GivenTheUpstreamHostIs("MATCH")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>( new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .WithUpstreamHost("MATCH") .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_return_route_when_upstreamhost_is_null() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) .And(x => GivenTheUpstreamHostIs("MATCH")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>( new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .BDDfy(); } [Fact] public void should_not_return_route_when_host_doesnt_match() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) .And(x => GivenTheUpstreamHostIs("DONTMATCH")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .WithUpstreamHost("MATCH") .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then(x => x.ThenAnErrorResponseIsReturned()) .And(x => x.ThenTheUrlMatcherIsNotCalled()) .BDDfy(); } [Fact] public void should_return_route_when_host_matches_but_null_host_on_same_path_first() { var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) .And(x => GivenTheUpstreamHostIs("MATCH")) .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( new OkResponse>( new List()))) .And(x => x.GivenTheConfigurationIs(new List { new ReRouteBuilder() .WithDownstreamPathTemplate("THENULLPATH") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamPathTemplate("someUpstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .WithUpstreamHost("MATCH") .Build() }, string.Empty, serviceProviderConfig )) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .When(x => x.WhenICallTheFinder()) .Then( x => x.ThenTheFollowingIsReturned(new DownstreamRoute( new List(), new ReRouteBuilder() .WithDownstreamPathTemplate("someDownstreamPath") .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamTemplatePattern(new UpstreamPathTemplate("someUpstreamPath", 1)) .Build() ))) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(2)) .BDDfy(); } private void GivenTheUpstreamHostIs(string upstreamHost) { _upstreamHost = upstreamHost; } private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) { _finder .Setup(x => x.Find(It.IsAny(), It.IsAny())) .Returns(response); } private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) { _upstreamHttpMethod = upstreamHttpMethod; } private void ThenAnErrorResponseIsReturned() { _result.IsError.ShouldBeTrue(); } private void ThenTheUrlMatcherIsCalledCorrectly() { _mockMatcher .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); } private void ThenTheUrlMatcherIsCalledCorrectly(int times) { _mockMatcher .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Exactly(times)); } private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) { _mockMatcher .Verify(x => x.Match(expectedUpstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Once); } private void ThenTheUrlMatcherIsNotCalled() { _mockMatcher .Verify(x => x.Match(_upstreamUrlPath, _reRoutesConfig[0].UpstreamPathTemplate.Value), Times.Never); } private void GivenTheUrlMatcherReturns(Response match) { _match = match; _mockMatcher .Setup(x => x.Match(It.IsAny(), It.IsAny())) .Returns(_match); } private void GivenTheConfigurationIs(List reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) { _reRoutesConfig = reRoutesConfig; _config = new OcelotConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, ""); } private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) { _upstreamUrlPath = upstreamUrlPath; } private void WhenICallTheFinder() { _result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod, _config, _upstreamHost); } private void ThenTheFollowingIsReturned(DownstreamRoute expected) { _result.Data.ReRoute.DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamPathTemplate.Value); _result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority); for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) { _result.Data.TemplatePlaceholderNameAndValues[i].Name.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Name); _result.Data.TemplatePlaceholderNameAndValues[i].Value.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Value); } _result.IsError.ShouldBeFalse(); } } }