716 lines
28 KiB
C#
716 lines
28 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Extensions.Logging.Abstractions;
|
||
using Microsoft.Extensions.Logging.Testing;
|
||
using Microsoft.Extensions.ObjectPool;
|
||
using Microsoft.Extensions.Options;
|
||
using Moq;
|
||
using Xunit;
|
||
|
||
namespace Microsoft.AspNetCore.Routing
|
||
{
|
||
public class RouteCollectionTest
|
||
{
|
||
private static readonly RequestDelegate NullHandler = (c) => Task.CompletedTask;
|
||
|
||
[Theory]
|
||
[InlineData(@"Home/Index/23", "/home/index/23", true, false)]
|
||
[InlineData(@"Home/Index/23", "/Home/Index/23", false, false)]
|
||
[InlineData(@"Home/Index/23", "/home/index/23/", true, true)]
|
||
[InlineData(@"Home/Index/23", "/Home/Index/23/", false, true)]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/Home/Index/23/?Param1=ABC&Param2=Xyz", false, true)]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/Home/Index/23?Param1=ABC&Param2=Xyz", false, false)]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/home/index/23/?Param1=ABC&Param2=Xyz", true, true)]
|
||
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "/Home/Index/23/#Param1=ABC&Param2=Xyz", false, true)]
|
||
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "/home/index/23#Param1=ABC&Param2=Xyz", true, false)]
|
||
[InlineData(@"Home/Index/23/?Param1=ABC&Param2=Xyz", "/home/index/23/?Param1=ABC&Param2=Xyz", true, true)]
|
||
[InlineData(@"Home/Index/23/#Param1=ABC&Param2=Xyz", "/home/index/23/#Param1=ABC&Param2=Xyz", true, false)]
|
||
public void GetVirtualPath_CanLowerCaseUrls_And_AppendTrailingSlash_BasedOnOptions(
|
||
string returnUrl,
|
||
string expectedUrl,
|
||
bool lowercaseUrls,
|
||
bool appendTrailingSlash)
|
||
{
|
||
// Arrange
|
||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns(new VirtualPathData(target.Object, returnUrl));
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(target.Object);
|
||
var virtualPathContext = CreateVirtualPathContext(
|
||
options: GetRouteOptions(
|
||
lowerCaseUrls: lowercaseUrls,
|
||
appendTrailingSlash: appendTrailingSlash));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Equal(expectedUrl, pathData.VirtualPath);
|
||
Assert.Same(target.Object, pathData.Router);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData(@"\u0130", @"/\u0130", true)]
|
||
[InlineData(@"\u0049", @"/\u0049", true)]
|
||
[InlineData(@"<22>ino", @"/<2F>ino", true)]
|
||
public void GetVirtualPath_DoesntLowerCaseUrls_Invariant(
|
||
string returnUrl,
|
||
string lowercaseUrl,
|
||
bool lowercaseUrls)
|
||
{
|
||
// Arrange
|
||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns(new VirtualPathData(target.Object, returnUrl));
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(target.Object);
|
||
var virtualPathContext = CreateVirtualPathContext(options: GetRouteOptions(lowercaseUrls));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Equal(lowercaseUrl, pathData.VirtualPath);
|
||
Assert.Same(target.Object, pathData.Router);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/Home/Index/23?Param1=ABC&Param2=Xyz", false, true, false)]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/Home/Index/23?Param1=ABC&Param2=Xyz", false, false, false)]
|
||
[InlineData(@"Home/Index/23?Param1=ABC&Param2=Xyz", "/home/index/23/?param1=abc¶m2=xyz", true, true, true)]
|
||
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "/Home/Index/23/#Param1=ABC&Param2=Xyz", false, true, true)]
|
||
[InlineData(@"Home/Index/23#Param1=ABC&Param2=Xyz", "/home/index/23#Param1=ABC&Param2=Xyz", true, false, false)]
|
||
[InlineData(@"Home/Index/23/?Param1=ABC&Param2=Xyz", "/home/index/23/?param1=abc¶m2=xyz", true, true, true)]
|
||
[InlineData(@"Home/Index/23/#Param1=ABC&Param2=Xyz", "/home/index/23/#Param1=ABC&Param2=Xyz", true, false, true)]
|
||
[InlineData(@"Home/Index/23/#Param1=ABC&Param2=Xyz", "/home/index/23/#param1=abc¶m2=xyz", true, true, true)]
|
||
public void GetVirtualPath_CanLowerCaseUrls_QueryStrings_BasedOnOptions(
|
||
string returnUrl,
|
||
string expectedUrl,
|
||
bool lowercaseUrls,
|
||
bool lowercaseQueryStrings, bool appendTrailingSlash)
|
||
{
|
||
// Arrange
|
||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns(new VirtualPathData(target.Object, returnUrl));
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(target.Object);
|
||
var virtualPathContext = CreateVirtualPathContext(
|
||
options: GetRouteOptions(
|
||
lowerCaseUrls: lowercaseUrls,
|
||
lowercaseQueryStrings: lowercaseQueryStrings,
|
||
appendTrailingSlash: appendTrailingSlash));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Equal(expectedUrl, pathData.VirtualPath);
|
||
Assert.Same(target.Object, pathData.Router);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Theory]
|
||
[MemberData(nameof(DataTokensTestData))]
|
||
public void GetVirtualPath_ReturnsDataTokens(RouteValueDictionary dataTokens, string routerName)
|
||
{
|
||
// Arrange
|
||
var virtualPath = "/TestVirtualPath";
|
||
|
||
var pathContextValues = new RouteValueDictionary { { "controller", virtualPath } };
|
||
|
||
var pathContext = CreateVirtualPathContext(
|
||
pathContextValues,
|
||
GetRouteOptions(),
|
||
routerName);
|
||
|
||
var route = CreateTemplateRoute("{controller}", routerName, dataTokens);
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(route);
|
||
|
||
var expectedDataTokens = dataTokens ?? new RouteValueDictionary();
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(pathContext);
|
||
|
||
// Assert
|
||
Assert.NotNull(pathData);
|
||
Assert.Same(route, pathData.Router);
|
||
|
||
Assert.Equal(virtualPath, pathData.VirtualPath);
|
||
|
||
Assert.Equal(expectedDataTokens.Count, pathData.DataTokens.Count);
|
||
foreach (var dataToken in expectedDataTokens)
|
||
{
|
||
Assert.True(pathData.DataTokens.ContainsKey(dataToken.Key));
|
||
Assert.Equal(dataToken.Value, pathData.DataTokens[dataToken.Key]);
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public async Task RouteAsync_FirstMatches()
|
||
{
|
||
// Arrange
|
||
var routes = new RouteCollection();
|
||
|
||
var route1 = CreateRoute(accept: true);
|
||
routes.Add(route1.Object);
|
||
|
||
var route2 = CreateRoute(accept: false);
|
||
routes.Add(route2.Object);
|
||
|
||
var context = CreateRouteContext("/Cool");
|
||
|
||
// Act
|
||
await routes.RouteAsync(context);
|
||
|
||
// Assert
|
||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(0));
|
||
Assert.NotNull(context.Handler);
|
||
|
||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||
Assert.Same(route1.Object, context.RouteData.Routers[0]);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task RouteAsync_SecondMatches()
|
||
{
|
||
// Arrange
|
||
|
||
var routes = new RouteCollection();
|
||
var route1 = CreateRoute(accept: false);
|
||
routes.Add(route1.Object);
|
||
|
||
var route2 = CreateRoute(accept: true);
|
||
routes.Add(route2.Object);
|
||
|
||
var context = CreateRouteContext("/Cool");
|
||
|
||
// Act
|
||
await routes.RouteAsync(context);
|
||
|
||
// Assert
|
||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||
Assert.NotNull(context.Handler);
|
||
|
||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||
Assert.Same(route2.Object, context.RouteData.Routers[0]);
|
||
}
|
||
|
||
[Fact]
|
||
public async Task RouteAsync_NoMatch()
|
||
{
|
||
// Arrange
|
||
var routes = new RouteCollection();
|
||
var route1 = CreateRoute(accept: false);
|
||
routes.Add(route1.Object);
|
||
|
||
var route2 = CreateRoute(accept: false);
|
||
routes.Add(route2.Object);
|
||
|
||
var context = CreateRouteContext("/Cool");
|
||
|
||
// Act
|
||
await routes.RouteAsync(context);
|
||
|
||
// Assert
|
||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||
Assert.Null(context.Handler);
|
||
|
||
Assert.Empty(context.RouteData.Routers);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData(false, "/RouteName")]
|
||
[InlineData(true, "/routename")]
|
||
public void NamedRouteTests_GetNamedRoute_ReturnsValue(bool lowercaseUrls, string expectedUrl)
|
||
{
|
||
// Arrange
|
||
var routeCollection = GetNestedRouteCollection(new string[] { "Route1", "Route2", "RouteName", "Route3" });
|
||
var virtualPathContext = CreateVirtualPathContext(
|
||
routeName: "RouteName",
|
||
options: GetRouteOptions(lowercaseUrls));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Equal(expectedUrl, pathData.VirtualPath);
|
||
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
|
||
Assert.Equal(virtualPathContext.RouteName, namedRouter.Name);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Fact]
|
||
public void NamedRouteTests_GetNamedRoute_RouteNotFound()
|
||
{
|
||
// Arrange
|
||
var routeCollection = GetNestedRouteCollection(new string[] { "Route1", "Route2", "Route3" });
|
||
var virtualPathContext = CreateVirtualPathContext("NonExistantRoute");
|
||
|
||
// Act
|
||
var stringVirtualPath = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Null(stringVirtualPath);
|
||
}
|
||
|
||
[Fact]
|
||
public void NamedRouteTests_GetNamedRoute_AmbiguousRoutesInCollection_DoesNotThrowForUnambiguousRoute()
|
||
{
|
||
// Arrange
|
||
var routeCollection = GetNestedRouteCollection(new string[] { "Route1", "Route2", "Route3", "Route4" });
|
||
|
||
// Add Duplicate route.
|
||
routeCollection.Add(CreateNamedRoute("Route3"));
|
||
var virtualPathContext = CreateVirtualPathContext(routeName: "Route1", options: GetRouteOptions(true));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
// Assert
|
||
Assert.Equal("/route1", pathData.VirtualPath);
|
||
var namedRouter = Assert.IsAssignableFrom<INamedRouter>(pathData.Router);
|
||
Assert.Equal("Route1", namedRouter.Name);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Fact]
|
||
public void NamedRouteTests_GetNamedRoute_AmbiguousRoutesInCollection_ThrowsForAmbiguousRoute()
|
||
{
|
||
// Arrange
|
||
var ambiguousRoute = "ambiguousRoute";
|
||
var routeCollection = GetNestedRouteCollection(new string[] { "Route1", "Route2", ambiguousRoute, "Route4" });
|
||
|
||
// Add Duplicate route.
|
||
routeCollection.Add(CreateNamedRoute(ambiguousRoute));
|
||
var virtualPathContext = CreateVirtualPathContext(routeName: ambiguousRoute, options: GetRouteOptions());
|
||
|
||
// Act & Assert
|
||
var ex = Assert.Throws<InvalidOperationException>(() => routeCollection.GetVirtualPath(virtualPathContext));
|
||
Assert.Equal(
|
||
"The supplied route name 'ambiguousRoute' is ambiguous and matched more than one route.",
|
||
ex.Message);
|
||
}
|
||
|
||
[Fact]
|
||
public void GetVirtualPath_AmbiguousRoutes_RequiresRouteValueValidation_Error()
|
||
{
|
||
// Arrange
|
||
var namedRoute = CreateNamedRoute("Ambiguous", accept: false);
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(namedRoute);
|
||
|
||
var innerRouteCollection = new RouteCollection();
|
||
innerRouteCollection.Add(namedRoute);
|
||
routeCollection.Add(innerRouteCollection);
|
||
|
||
var virtualPathContext = CreateVirtualPathContext("Ambiguous");
|
||
|
||
// Act & Assert
|
||
var ex = Assert.Throws<InvalidOperationException>(() => routeCollection.GetVirtualPath(virtualPathContext));
|
||
Assert.Equal("The supplied route name 'Ambiguous' is ambiguous and matched more than one route.", ex.Message);
|
||
}
|
||
|
||
// "Integration" tests for RouteCollection
|
||
|
||
public static IEnumerable<object[]> IntegrationTestData
|
||
{
|
||
get
|
||
{
|
||
yield return new object[] {
|
||
"{controller}/{action}",
|
||
new RouteValueDictionary { { "controller", "Home" }, { "action", "Index" } },
|
||
"/home/index",
|
||
true };
|
||
|
||
yield return new object[] {
|
||
"{controller}/{action}/",
|
||
new RouteValueDictionary { { "controller", "Home" }, { "action", "Index" } },
|
||
"/Home/Index",
|
||
false };
|
||
|
||
yield return new object[] {
|
||
"api/{action}/",
|
||
new RouteValueDictionary { { "action", "Create" } },
|
||
"/api/create",
|
||
true };
|
||
|
||
yield return new object[] {
|
||
"api/{action}/{id}",
|
||
new RouteValueDictionary {
|
||
{ "action", "Create" },
|
||
{ "id", "23" },
|
||
{ "Param1", "Value1" },
|
||
{ "Param2", "Value2" } },
|
||
"/api/create/23?Param1=Value1&Param2=Value2",
|
||
true };
|
||
|
||
yield return new object[] {
|
||
"api/{action}/{id}",
|
||
new RouteValueDictionary {
|
||
{ "action", "Create" },
|
||
{ "id", "23" },
|
||
{ "Param1", "Value1" },
|
||
{ "Param2", "Value2" } },
|
||
"/api/Create/23?Param1=Value1&Param2=Value2",
|
||
false };
|
||
}
|
||
}
|
||
|
||
[Theory]
|
||
[MemberData(nameof(IntegrationTestData))]
|
||
public void GetVirtualPath_Success(
|
||
string template,
|
||
RouteValueDictionary values,
|
||
string expectedUrl,
|
||
bool lowercaseUrls)
|
||
{
|
||
// Arrange
|
||
var routeCollection = new RouteCollection();
|
||
var route = CreateTemplateRoute(template);
|
||
routeCollection.Add(route);
|
||
var context = CreateVirtualPathContext(values, options: GetRouteOptions(lowercaseUrls));
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(context);
|
||
|
||
// Assert
|
||
Assert.Equal(expectedUrl, pathData.VirtualPath);
|
||
Assert.Same(route, pathData.Router);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
public static IEnumerable<object[]> RestoresRouteDataForEachRouterData
|
||
{
|
||
get
|
||
{
|
||
// Here 'area' segment doesn't have a value but the later segments have values. This is an invalid
|
||
// route match and the url generation should look into the next available route in the collection.
|
||
yield return new object[] {
|
||
new Route[]
|
||
{
|
||
CreateTemplateRoute("{area?}/{controller=Home}/{action=Index}/{id?}", "1"),
|
||
CreateTemplateRoute("{controller=Home}/{action=Index}/{id?}", "2")
|
||
},
|
||
new RouteValueDictionary(new { controller = "Test", action = "Index" }),
|
||
"/Test",
|
||
"2" };
|
||
|
||
// Here the segment 'a' is valid but 'b' is not as it would be empty. This would be an invalid route match, but
|
||
// the route value of 'a' should still be present to be evaluated for the next available route.
|
||
yield return new object[] {
|
||
new[]
|
||
{
|
||
CreateTemplateRoute("{a}/{b?}/{c}", "1"),
|
||
CreateTemplateRoute("{a=Home}/{b=Index}", "2")
|
||
},
|
||
new RouteValueDictionary(new { a = "Test", c = "Foo" }),
|
||
"/Test?c=Foo",
|
||
"2" };
|
||
}
|
||
}
|
||
|
||
[Theory]
|
||
[MemberData(nameof(RestoresRouteDataForEachRouterData))]
|
||
public void GetVirtualPath_RestoresRouteData_ForEachRouter(
|
||
Route[] routes,
|
||
RouteValueDictionary routeValues,
|
||
string expectedUrl,
|
||
string expectedRouteToMatch)
|
||
{
|
||
// Arrange
|
||
var routeCollection = new RouteCollection();
|
||
foreach (var route in routes)
|
||
{
|
||
routeCollection.Add(route);
|
||
}
|
||
var context = CreateVirtualPathContext(routeValues);
|
||
|
||
// Act
|
||
var pathData = routeCollection.GetVirtualPath(context);
|
||
|
||
// Assert
|
||
Assert.Equal(expectedUrl, pathData.VirtualPath);
|
||
Assert.Same(expectedRouteToMatch, ((INamedRouter)pathData.Router).Name);
|
||
Assert.Empty(pathData.DataTokens);
|
||
}
|
||
|
||
[Fact]
|
||
public void GetVirtualPath_NoBestEffort_NoMatch()
|
||
{
|
||
// Arrange
|
||
var route1 = CreateRoute(accept: false, match: false, matchValue: "bad");
|
||
var route2 = CreateRoute(accept: false, match: false, matchValue: "bad");
|
||
var route3 = CreateRoute(accept: false, match: false, matchValue: "bad");
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(route1.Object);
|
||
routeCollection.Add(route2.Object);
|
||
routeCollection.Add(route3.Object);
|
||
|
||
var virtualPathContext = CreateVirtualPathContext();
|
||
|
||
// Act
|
||
var path = routeCollection.GetVirtualPath(virtualPathContext);
|
||
|
||
Assert.Null(path);
|
||
|
||
// All of these should be called
|
||
route1.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
|
||
route2.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
|
||
route3.Verify(r => r.GetVirtualPath(It.IsAny<VirtualPathContext>()), Times.Once());
|
||
}
|
||
|
||
// DataTokens test data for RouterCollection.GetVirtualPath
|
||
public static IEnumerable<object[]> DataTokensTestData
|
||
{
|
||
get
|
||
{
|
||
yield return new object[] { null, null };
|
||
yield return new object[] { new RouteValueDictionary(), null };
|
||
yield return new object[] { new RouteValueDictionary() { { "tokenKey", "tokenValue" } }, null };
|
||
|
||
yield return new object[] { null, "routerA" };
|
||
yield return new object[] { new RouteValueDictionary(), "routerA" };
|
||
yield return new object[] { new RouteValueDictionary() { { "tokenKey", "tokenValue" } }, "routerA" };
|
||
}
|
||
}
|
||
|
||
private static RouteCollection GetRouteCollectionWithNamedRoutes(IEnumerable<string> routeNames)
|
||
{
|
||
var routes = new RouteCollection();
|
||
foreach (var routeName in routeNames)
|
||
{
|
||
var route1 = CreateNamedRoute(routeName, accept: true);
|
||
routes.Add(route1);
|
||
}
|
||
|
||
return routes;
|
||
}
|
||
|
||
private static RouteCollection GetNestedRouteCollection(string[] routeNames)
|
||
{
|
||
var random = new Random();
|
||
int index = random.Next(0, routeNames.Length - 1);
|
||
var first = routeNames.Take(index).ToArray();
|
||
var second = routeNames.Skip(index).ToArray();
|
||
|
||
var rc1 = GetRouteCollectionWithNamedRoutes(first);
|
||
var rc2 = GetRouteCollectionWithNamedRoutes(second);
|
||
var rc3 = new RouteCollection();
|
||
var rc4 = new RouteCollection();
|
||
|
||
rc1.Add(rc3);
|
||
rc4.Add(rc2);
|
||
|
||
// Add a few unnamedRoutes.
|
||
rc1.Add(CreateRoute(accept: false).Object);
|
||
rc2.Add(CreateRoute(accept: false).Object);
|
||
rc3.Add(CreateRoute(accept: false).Object);
|
||
rc3.Add(CreateRoute(accept: false).Object);
|
||
rc4.Add(CreateRoute(accept: false).Object);
|
||
rc4.Add(CreateRoute(accept: false).Object);
|
||
|
||
var routeCollection = new RouteCollection();
|
||
routeCollection.Add(rc1);
|
||
routeCollection.Add(rc4);
|
||
|
||
return routeCollection;
|
||
}
|
||
|
||
private static INamedRouter CreateNamedRoute(string name, bool accept = false, string matchValue = null)
|
||
{
|
||
if (matchValue == null)
|
||
{
|
||
matchValue = name;
|
||
}
|
||
|
||
var target = new Mock<INamedRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns<VirtualPathContext>(c =>
|
||
c.RouteName == name ? new VirtualPathData(target.Object, matchValue) : null)
|
||
.Verifiable();
|
||
|
||
target
|
||
.SetupGet(e => e.Name)
|
||
.Returns(name);
|
||
|
||
target
|
||
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
|
||
.Callback<RouteContext>((c) => c.Handler = accept ? NullHandler : null)
|
||
.Returns(Task.FromResult<object>(null))
|
||
.Verifiable();
|
||
|
||
return target.Object;
|
||
}
|
||
|
||
private static Route CreateTemplateRoute(
|
||
string template,
|
||
string routerName = null,
|
||
RouteValueDictionary dataTokens = null,
|
||
IInlineConstraintResolver constraintResolver = null)
|
||
{
|
||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns<VirtualPathContext>(rc => null);
|
||
|
||
if (constraintResolver == null)
|
||
{
|
||
constraintResolver = new Mock<IInlineConstraintResolver>().Object;
|
||
}
|
||
|
||
return new Route(
|
||
target.Object,
|
||
routerName,
|
||
template,
|
||
defaults: null,
|
||
constraints: null,
|
||
dataTokens: dataTokens,
|
||
inlineConstraintResolver: constraintResolver);
|
||
}
|
||
|
||
private static VirtualPathContext CreateVirtualPathContext(
|
||
string routeName = null,
|
||
ILoggerFactory loggerFactory = null,
|
||
Action<RouteOptions> options = null)
|
||
{
|
||
if (loggerFactory == null)
|
||
{
|
||
loggerFactory = NullLoggerFactory.Instance;
|
||
}
|
||
|
||
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
||
|
||
var services = new ServiceCollection();
|
||
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
||
services.AddOptions();
|
||
services.AddRouting();
|
||
if (options != null)
|
||
{
|
||
services.Configure(options);
|
||
}
|
||
|
||
var context = new Mock<HttpContext>(MockBehavior.Strict);
|
||
context.SetupGet(m => m.RequestServices).Returns(services.BuildServiceProvider());
|
||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||
|
||
return new VirtualPathContext(context.Object, null, null, routeName);
|
||
}
|
||
|
||
private static VirtualPathContext CreateVirtualPathContext(
|
||
RouteValueDictionary values,
|
||
Action<RouteOptions> options = null,
|
||
string routeName = null)
|
||
{
|
||
var services = new ServiceCollection();
|
||
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
||
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
||
services.AddOptions();
|
||
services.AddRouting();
|
||
if (options != null)
|
||
{
|
||
services.Configure<RouteOptions>(options);
|
||
}
|
||
|
||
var context = new DefaultHttpContext
|
||
{
|
||
RequestServices = services.BuildServiceProvider(),
|
||
};
|
||
|
||
return new VirtualPathContext(
|
||
context,
|
||
ambientValues: null,
|
||
values: values,
|
||
routeName: routeName);
|
||
}
|
||
|
||
private static RouteContext CreateRouteContext(
|
||
string requestPath,
|
||
ILoggerFactory loggerFactory = null,
|
||
RouteOptions options = null)
|
||
{
|
||
if (loggerFactory == null)
|
||
{
|
||
loggerFactory = NullLoggerFactory.Instance;
|
||
}
|
||
|
||
if (options == null)
|
||
{
|
||
options = new RouteOptions();
|
||
}
|
||
|
||
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
||
request.SetupGet(r => r.Path).Returns(requestPath);
|
||
|
||
var optionsAccessor = new Mock<IOptions<RouteOptions>>(MockBehavior.Strict);
|
||
optionsAccessor.SetupGet(o => o.Value).Returns(options);
|
||
|
||
var context = new Mock<HttpContext>(MockBehavior.Strict);
|
||
context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||
.Returns(loggerFactory);
|
||
context.Setup(m => m.RequestServices.GetService(typeof(IOptions<RouteOptions>)))
|
||
.Returns(optionsAccessor.Object);
|
||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||
|
||
return new RouteContext(context.Object);
|
||
}
|
||
|
||
private static Mock<IRouter> CreateRoute(
|
||
bool accept = true,
|
||
bool match = false,
|
||
string matchValue = "value")
|
||
{
|
||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||
target
|
||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||
.Returns(accept || match ? new VirtualPathData(target.Object, matchValue) : null)
|
||
.Verifiable();
|
||
|
||
target
|
||
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
|
||
.Callback<RouteContext>((c) => c.Handler = accept ? NullHandler : null)
|
||
.Returns(Task.FromResult<object>(null))
|
||
.Verifiable();
|
||
|
||
return target;
|
||
}
|
||
|
||
private static Action<RouteOptions> GetRouteOptions(
|
||
bool lowerCaseUrls = false,
|
||
bool appendTrailingSlash = false,
|
||
bool lowercaseQueryStrings = false)
|
||
{
|
||
return (options) =>
|
||
{
|
||
options.LowercaseUrls = lowerCaseUrls;
|
||
options.AppendTrailingSlash = appendTrailingSlash;
|
||
options.LowercaseQueryStrings = lowercaseQueryStrings;
|
||
};
|
||
}
|
||
}
|
||
}
|