mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-24 11:12:51 +08:00
520 lines
22 KiB
C#
520 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Ocelot.Configuration.File;
|
|
using Shouldly;
|
|
using TestStack.BDDfy;
|
|
using Xunit;
|
|
|
|
namespace Ocelot.AcceptanceTests
|
|
{
|
|
public class RoutingTests : IDisposable
|
|
{
|
|
private IWebHost _builder;
|
|
private readonly Steps _steps;
|
|
private string _downstreamPath;
|
|
|
|
public RoutingTests()
|
|
{
|
|
_steps = new Steps();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_404_when_no_configuration_at_all()
|
|
{
|
|
this.Given(x => _steps.GivenThereIsAConfiguration(new FileConfiguration()))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_with_simple_url()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void bug()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/v1/vacancy",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/vacancy/",
|
|
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
|
|
ServiceName = "botCore",
|
|
LoadBalancer = "LeastConnection"
|
|
},
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/vacancy/{vacancyId}",
|
|
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
|
|
ServiceName = "botCore",
|
|
LoadBalancer = "LeastConnection"
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_when_path_missing_forward_slash_as_first_char()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "api/products",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_when_host_has_trailing_slash()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/products",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost/",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_not_care_about_no_trailing()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/products",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/products/",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_not_care_about_trailing()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/products",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/products",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_not_found()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/products",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/products/{productId}",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
QoSOptions = new FileQoSOptions()
|
|
{
|
|
ExceptionsAllowedBeforeBreaking = 3,
|
|
DurationOfBreak = 5,
|
|
TimeoutValue = 5000
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_with_complex_url()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/products/{productId}",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/products/{productId}",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Some Product"))
|
|
.BDDfy();
|
|
}
|
|
|
|
|
|
[Fact]
|
|
public void should_not_add_trailing_slash_to_downstream_url()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/products/{productId}",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/products/{productId}",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1"))
|
|
.Then(x => ThenTheDownstreamUrlPathShouldBe("/api/products/1"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_201_with_simple_url()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
DownstreamScheme = "http",
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string> { "Post" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.And(x => _steps.GivenThePostHasContent("postContent"))
|
|
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_201_with_complex_query_string()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/newThing",
|
|
UpstreamPathTemplate = "/newThing",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_with_placeholder_for_final_url_path()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/{urlPath}",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/myApp1Name/api/{urlPath}",
|
|
UpstreamHttpMethod = new List<string> { "Get" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/myApp1Name/api/products/1", 200, "Some Product"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Some Product"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_201_with_simple_url_and_multiple_upstream_http_method()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
DownstreamScheme = "http",
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string> { "Get", "Post" },
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.And(x => _steps.GivenThePostHasContent("postContent"))
|
|
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_response_200_with_simple_url_and_any_upstream_http_method()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
DownstreamScheme = "http",
|
|
UpstreamPathTemplate = "/",
|
|
UpstreamHttpMethod = new List<string>(),
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
|
.BDDfy();
|
|
}
|
|
|
|
[Fact]
|
|
public void should_return_404_when_calling_upstream_route_with_no_matching_downstream_re_route_github_issue_134()
|
|
{
|
|
var configuration = new FileConfiguration
|
|
{
|
|
ReRoutes = new List<FileReRoute>
|
|
{
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/v1/vacancy",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/vacancy/",
|
|
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
|
|
ServiceName = "botCore",
|
|
LoadBalancer = "LeastConnection"
|
|
},
|
|
new FileReRoute
|
|
{
|
|
DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}",
|
|
DownstreamScheme = "http",
|
|
DownstreamHost = "localhost",
|
|
DownstreamPort = 51879,
|
|
UpstreamPathTemplate = "/vacancy/{vacancyId}",
|
|
UpstreamHttpMethod = new List<string> { "Options", "Put", "Get", "Post", "Delete" },
|
|
ServiceName = "botCore",
|
|
LoadBalancer = "LeastConnection"
|
|
}
|
|
}
|
|
};
|
|
|
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura"))
|
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
|
.And(x => _steps.GivenOcelotIsRunning())
|
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1"))
|
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound))
|
|
.BDDfy();
|
|
}
|
|
|
|
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
|
{
|
|
_builder = new WebHostBuilder()
|
|
.UseUrls(baseUrl)
|
|
.UseKestrel()
|
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
|
.UseIISIntegration()
|
|
.Configure(app =>
|
|
{
|
|
app.UsePathBase(basePath);
|
|
app.Run(async context =>
|
|
{
|
|
_downstreamPath = context.Request.PathBase.Value;
|
|
context.Response.StatusCode = statusCode;
|
|
await context.Response.WriteAsync(responseBody);
|
|
});
|
|
})
|
|
.Build();
|
|
|
|
_builder.Start();
|
|
}
|
|
|
|
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
|
|
{
|
|
_downstreamPath.ShouldBe(expectedDownstreamPath);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_builder?.Dispose();
|
|
_steps.Dispose();
|
|
}
|
|
}
|
|
}
|