744 lines
34 KiB
C#
744 lines
34 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;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Http.Features;
|
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
using Microsoft.AspNetCore.Mvc.Routing;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.AspNetCore.Routing.Matching;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.Extensions.Primitives;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
|
{
|
|
public class MvcEndpointDataSourceTests
|
|
{
|
|
[Fact]
|
|
public void Endpoints_AccessParameters_InitializedFromProvider()
|
|
{
|
|
// Arrange
|
|
var routeValue = "Value";
|
|
var requiredValues = new Dictionary<string, string>
|
|
{
|
|
["Name"] = routeValue
|
|
};
|
|
var displayName = "DisplayName!";
|
|
var order = 1;
|
|
var template = "/Template!";
|
|
var filterDescriptor = new FilterDescriptor(new ControllerActionFilter(), 1);
|
|
|
|
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
|
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
|
{
|
|
new ActionDescriptor
|
|
{
|
|
RouteValues = requiredValues,
|
|
DisplayName = displayName,
|
|
AttributeRouteInfo = new AttributeRouteInfo
|
|
{
|
|
Order = order,
|
|
Template = template
|
|
},
|
|
FilterDescriptors = new List<FilterDescriptor>
|
|
{
|
|
filterDescriptor
|
|
}
|
|
}
|
|
}, 0));
|
|
|
|
var dataSource = CreateMvcEndpointDataSource(mockDescriptorProvider.Object);
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
|
|
var endpointValue = matcherEndpoint.RequiredValues["Name"];
|
|
Assert.Equal(routeValue, endpointValue);
|
|
|
|
Assert.Equal(displayName, matcherEndpoint.DisplayName);
|
|
Assert.Equal(order, matcherEndpoint.Order);
|
|
Assert.Equal(template, matcherEndpoint.RoutePattern.RawText);
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_InvokeReturnedEndpoint_ActionInvokerProviderCalled()
|
|
{
|
|
// Arrange
|
|
var featureCollection = new FeatureCollection();
|
|
featureCollection.Set<IEndpointFeature>(new EndpointFeature
|
|
{
|
|
Values = new RouteValueDictionary()
|
|
});
|
|
|
|
var httpContextMock = new Mock<HttpContext>();
|
|
httpContextMock.Setup(m => m.Features).Returns(featureCollection);
|
|
|
|
var descriptorProviderMock = new Mock<IActionDescriptorCollectionProvider>();
|
|
descriptorProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>
|
|
{
|
|
new ActionDescriptor
|
|
{
|
|
AttributeRouteInfo = new AttributeRouteInfo
|
|
{
|
|
Template = string.Empty
|
|
},
|
|
FilterDescriptors = new List<FilterDescriptor>()
|
|
}
|
|
}, 0));
|
|
|
|
var actionInvokerCalled = false;
|
|
var actionInvokerMock = new Mock<IActionInvoker>();
|
|
actionInvokerMock.Setup(m => m.InvokeAsync()).Returns(() =>
|
|
{
|
|
actionInvokerCalled = true;
|
|
return Task.CompletedTask;
|
|
});
|
|
|
|
var actionInvokerProviderMock = new Mock<IActionInvokerFactory>();
|
|
actionInvokerProviderMock.Setup(m => m.CreateInvoker(It.IsAny<ActionContext>())).Returns(actionInvokerMock.Object);
|
|
|
|
var dataSource = CreateMvcEndpointDataSource(
|
|
descriptorProviderMock.Object,
|
|
new MvcEndpointInvokerFactory(actionInvokerProviderMock.Object));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
|
|
var invokerDelegate = matcherEndpoint.Invoker((next) => Task.CompletedTask);
|
|
|
|
invokerDelegate(httpContextMock.Object);
|
|
|
|
Assert.True(actionInvokerCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetChangeToken_MultipleChangeTokenProviders_ComposedResult()
|
|
{
|
|
// Arrange
|
|
var featureCollection = new FeatureCollection();
|
|
featureCollection.Set<IEndpointFeature>(new EndpointFeature
|
|
{
|
|
Values = new RouteValueDictionary()
|
|
});
|
|
|
|
var httpContextMock = new Mock<HttpContext>();
|
|
httpContextMock.Setup(m => m.Features).Returns(featureCollection);
|
|
|
|
var descriptorProviderMock = new Mock<IActionDescriptorCollectionProvider>();
|
|
descriptorProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>(), 0));
|
|
|
|
var actionInvokerMock = new Mock<IActionInvoker>();
|
|
|
|
var actionInvokerProviderMock = new Mock<IActionInvokerFactory>();
|
|
actionInvokerProviderMock.Setup(m => m.CreateInvoker(It.IsAny<ActionContext>())).Returns(actionInvokerMock.Object);
|
|
|
|
var changeTokenMock = new Mock<IChangeToken>();
|
|
|
|
var changeProvider1Mock = new Mock<IActionDescriptorChangeProvider>();
|
|
changeProvider1Mock.Setup(m => m.GetChangeToken()).Returns(changeTokenMock.Object);
|
|
var changeProvider2Mock = new Mock<IActionDescriptorChangeProvider>();
|
|
changeProvider2Mock.Setup(m => m.GetChangeToken()).Returns(changeTokenMock.Object);
|
|
|
|
var dataSource = CreateMvcEndpointDataSource(
|
|
descriptorProviderMock.Object,
|
|
new MvcEndpointInvokerFactory(actionInvokerProviderMock.Object),
|
|
new[] { changeProvider1Mock.Object, changeProvider2Mock.Object });
|
|
|
|
// Act
|
|
var changeToken = dataSource.GetChangeToken();
|
|
|
|
// Assert
|
|
var compositeChangeToken = Assert.IsType<CompositeChangeToken>(changeToken);
|
|
Assert.Equal(2, compositeChangeToken.ChangeTokens.Count);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("{controller}/{action}/{id?}", new[] { "TestController/TestAction/{id?}" })]
|
|
[InlineData("{controller}/{id?}", new string[] { })]
|
|
[InlineData("{action}/{id?}", new string[] { })]
|
|
[InlineData("{Controller}/{Action}/{id?}", new[] { "TestController/TestAction/{id?}" })]
|
|
[InlineData("{CONTROLLER}/{ACTION}/{id?}", new[] { "TestController/TestAction/{id?}" })]
|
|
[InlineData("{controller}/{action=TestAction}", new[] { "TestController", "TestController/TestAction" })]
|
|
[InlineData("{controller}/{action=TestAction}/{id?}", new[] { "TestController", "TestController/TestAction/{id?}" })]
|
|
[InlineData("{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestController", "TestController/TestAction/{id?}" })]
|
|
[InlineData("{controller}/{action}/{*catchAll}", new[] { "TestController/TestAction/{*catchAll}" })]
|
|
[InlineData("{controller}/{action=TestAction}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{*catchAll}" })]
|
|
[InlineData("{controller}/{action=TestAction}/{id?}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{*catchAll}" })]
|
|
//[InlineData("{controller}/{action}.{ext?}", new[] { "TestController/TestAction.{ext?}" })]
|
|
//[InlineData("{controller}/{action=TestAction}.{ext?}", new[] { "TestController", "TestController/TestAction.{ext?}" })]
|
|
public void Endpoints_SingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "TestController", action = "TestAction" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var inspectors = finalEndpointTemplates
|
|
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
|
|
.ToArray();
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints, inspectors);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("{area}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
|
|
[InlineData("{controller}/{action}/{id?}", new string[] { })]
|
|
[InlineData("{area=TestArea}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
|
|
[InlineData("{area=TestArea}/{controller}/{action=TestAction}/{id?}", new[] { "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
|
[InlineData("{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestArea", "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
|
|
[InlineData("{area:exists}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
|
|
public void Endpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "TestController", action = "TestAction", area = "TestArea" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var inspectors = finalEndpointTemplates
|
|
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
|
|
.ToArray();
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints, inspectors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_SingleAction_WithActionDefault()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "TestController", action = "TestAction" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
string.Empty,
|
|
"{controller}/{action}",
|
|
new RouteValueDictionary(new { action = "TestAction" })));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints,
|
|
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
|
|
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_CalledMultipleTimes_ReturnsSameInstance()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollectionProviderMock = new Mock<IActionDescriptorCollectionProvider>();
|
|
actionDescriptorCollectionProviderMock
|
|
.Setup(m => m.ActionDescriptors)
|
|
.Returns(new ActionDescriptorCollection(new[]
|
|
{
|
|
CreateActionDescriptor(new { controller = "TestController", action = "TestAction" })
|
|
}, version: 0));
|
|
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollectionProviderMock.Object);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
string.Empty,
|
|
"{controller}/{action}",
|
|
new RouteValueDictionary(new { action = "TestAction" })));
|
|
|
|
// Act
|
|
var endpoints1 = dataSource.Endpoints;
|
|
var endpoints2 = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints1,
|
|
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
|
|
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
|
|
Assert.Same(endpoints1, endpoints2);
|
|
|
|
actionDescriptorCollectionProviderMock.VerifyGet(m => m.ActionDescriptors, Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_ChangeTokenTriggered_EndpointsRecreated()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollectionProviderMock = new Mock<IActionDescriptorCollectionProvider>();
|
|
actionDescriptorCollectionProviderMock
|
|
.Setup(m => m.ActionDescriptors)
|
|
.Returns(new ActionDescriptorCollection(new[]
|
|
{
|
|
CreateActionDescriptor(new { controller = "TestController", action = "TestAction" })
|
|
}, version: 0));
|
|
|
|
CancellationTokenSource cts = null;
|
|
|
|
var changeProviderMock = new Mock<IActionDescriptorChangeProvider>();
|
|
changeProviderMock.Setup(m => m.GetChangeToken()).Returns(() =>
|
|
{
|
|
cts = new CancellationTokenSource();
|
|
var changeToken = new CancellationChangeToken(cts.Token);
|
|
|
|
return changeToken;
|
|
});
|
|
|
|
var dataSource = CreateMvcEndpointDataSource(
|
|
actionDescriptorCollectionProviderMock.Object,
|
|
actionDescriptorChangeProviders: new[] { changeProviderMock.Object });
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
string.Empty,
|
|
"{controller}/{action}",
|
|
new RouteValueDictionary(new { action = "TestAction" })));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
Assert.Collection(endpoints,
|
|
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
|
|
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
|
|
|
|
actionDescriptorCollectionProviderMock
|
|
.Setup(m => m.ActionDescriptors)
|
|
.Returns(new ActionDescriptorCollection(new[]
|
|
{
|
|
CreateActionDescriptor(new { controller = "NewTestController", action = "NewTestAction" })
|
|
}, version: 1));
|
|
|
|
cts.Cancel();
|
|
|
|
// Assert
|
|
var newEndpoints = dataSource.Endpoints;
|
|
|
|
Assert.NotSame(endpoints, newEndpoints);
|
|
Assert.Collection(newEndpoints,
|
|
(e) => Assert.Equal("NewTestController/NewTestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_MultipleActions_WithActionConstraint()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "TestController", action = "TestAction" },
|
|
new { controller = "TestController", action = "TestAction1" },
|
|
new { controller = "TestController", action = "TestAction2" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
string.Empty,
|
|
"{controller}/{action}",
|
|
constraints: new RouteValueDictionary(new { action = "(TestAction1|TestAction2)" })));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints,
|
|
(e) => Assert.Equal("TestController/TestAction1", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
|
|
(e) => Assert.Equal("TestController/TestAction2", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("{controller}/{action}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController1/TestAction3", "TestController2/TestAction1" })]
|
|
[InlineData("{controller}/{action:regex((TestAction1|TestAction2))}", new[] { "TestController1/TestAction1", "TestController1/TestAction2", "TestController2/TestAction1" })]
|
|
public void Endpoints_MultipleActions(string endpointInfoRoute, string[] finalEndpointTemplates)
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "TestController1", action = "TestAction1" },
|
|
new { controller = "TestController1", action = "TestAction2" },
|
|
new { controller = "TestController1", action = "TestAction3" },
|
|
new { controller = "TestController2", action = "TestAction1" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
string.Empty,
|
|
endpointInfoRoute));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
var inspectors = finalEndpointTemplates
|
|
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
|
|
.ToArray();
|
|
|
|
// Assert
|
|
Assert.Collection(endpoints, inspectors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_ConventionalRoute_WithNoRouteName_DoesNotAddRouteNameMetadata()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "Home", action = "Index" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(
|
|
CreateEndpointInfo(string.Empty, "named/{controller}/{action}/{id?}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
|
Assert.Null(routeNameMetadata);
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_CanCreateMultipleEndpoints_WithSameRouteName()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "Home", action = "Index" },
|
|
new { controller = "Products", action = "Details" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(
|
|
CreateEndpointInfo("namedRoute", "named/{controller}/{action}/{id?}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Collection(
|
|
endpoints,
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
|
Assert.NotNull(routeNameMetadata);
|
|
Assert.Equal("namedRoute", routeNameMetadata.Name);
|
|
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
},
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
var routeNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteNameMetadata>();
|
|
Assert.NotNull(routeNameMetadata);
|
|
Assert.Equal("namedRoute", routeNameMetadata.Name);
|
|
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void Endpoints_ConventionalRoutes_StaticallyDefinedOrder_IsMaintained()
|
|
{
|
|
// Arrange
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(
|
|
new { controller = "Home", action = "Index" },
|
|
new { controller = "Products", action = "Details" });
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
name: string.Empty,
|
|
template: "{controller}/{action}/{id?}"));
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
|
|
name: "namedRoute",
|
|
"named/{controller}/{action}/{id?}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Collection(
|
|
endpoints,
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
Assert.Equal("Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
Assert.Equal(1, matcherEndpoint.Order);
|
|
},
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
Assert.Equal(2, matcherEndpoint.Order);
|
|
},
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
Assert.Equal("Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
Assert.Equal(1, matcherEndpoint.Order);
|
|
},
|
|
(ep) =>
|
|
{
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
|
|
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
|
|
Assert.Equal(2, matcherEndpoint.Order);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void RequiredValue_WithNoCorresponding_TemplateParameter_DoesNotProduceEndpoint()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(new { area = "admin", controller = "home", action = "index" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Empty(endpoints);
|
|
}
|
|
|
|
// Since area, controller, action and page are special, check to see if the followin test succeeds for a
|
|
// custom required value too.
|
|
[Fact(Skip = "Needs review")]
|
|
public void NonReservedRequiredValue_WithNoCorresponding_TemplateParameter_DoesNotProduceEndpoint()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(new { controller = "home", action = "index", foo = "bar" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Empty(endpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void TemplateParameter_WithNoDefaultOrRequiredValue_DoesNotProduceEndpoint()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(new { controller = "home", action = "index" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area}/{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Empty(endpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void TemplateParameter_WithDefaultValue_AndNullRequiredValue_DoesNotProduceEndpoint()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(new { area = (string)null, controller = "home", action = "index" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area=admin}/{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Empty(endpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void TemplateParameter_WithNullRequiredValue_DoesNotProduceEndpoint()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(new { area = (string)null, controller = "home", action = "index" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{area}/{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
Assert.Empty(endpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void NoDefaultValues_RequiredValues_UsedToCreateDefaultValues()
|
|
{
|
|
// Arrange
|
|
var expectedDefaults = new RouteValueDictionary(new { controller = "Foo", action = "Bar" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: expectedDefaults);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, "{controller}/{action}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
|
|
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
|
|
}
|
|
|
|
[Fact]
|
|
public void RequiredValues_NotPresent_InDefaultValues_IsAddedToDefaultValues()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(
|
|
new { controller = "Foo", action = "Bar", subarea = "test" });
|
|
var expectedDefaults = requiredValues;
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(
|
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
|
|
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
|
|
}
|
|
|
|
[Fact]
|
|
public void RequiredValues_IsSubsetOf_DefaultValues()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(
|
|
new { controller = "Foo", action = "Bar", subarea = "test" });
|
|
var expectedDefaults = new RouteValueDictionary(
|
|
new { controller = "Foo", action = "Bar", subarea = "test", subscription = "general" });
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(
|
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}/{subscription=general}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.RoutePattern.RawText);
|
|
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
|
|
}
|
|
|
|
[Fact]
|
|
public void RequiredValues_HavingNull_AndNotPresentInDefaultValues_IsAddedToDefaultValues()
|
|
{
|
|
// Arrange
|
|
var requiredValues = new RouteValueDictionary(
|
|
new { area = (string)null, controller = "Foo", action = "Bar", page = (string)null });
|
|
var expectedDefaults = requiredValues;
|
|
var actionDescriptorCollection = GetActionDescriptorCollection(requiredValues: requiredValues);
|
|
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
|
|
dataSource.ConventionalEndpointInfos.Add(
|
|
CreateEndpointInfo(string.Empty, "{controller=Home}/{action=Index}"));
|
|
|
|
// Act
|
|
var endpoints = dataSource.Endpoints;
|
|
|
|
// Assert
|
|
var endpoint = Assert.Single(endpoints);
|
|
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
|
|
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
|
|
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
|
|
}
|
|
|
|
private MvcEndpointDataSource CreateMvcEndpointDataSource(
|
|
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null,
|
|
MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null,
|
|
IEnumerable<IActionDescriptorChangeProvider> actionDescriptorChangeProviders = null)
|
|
{
|
|
if (actionDescriptorCollectionProvider == null)
|
|
{
|
|
var mockDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
|
mockDescriptorProvider.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List<ActionDescriptor>(), 0));
|
|
|
|
actionDescriptorCollectionProvider = mockDescriptorProvider.Object;
|
|
}
|
|
|
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
|
serviceProviderMock.Setup(m => m.GetService(typeof(IActionDescriptorCollectionProvider))).Returns(actionDescriptorCollectionProvider);
|
|
|
|
var dataSource = new MvcEndpointDataSource(
|
|
actionDescriptorCollectionProvider,
|
|
mvcEndpointInvokerFactory ?? new MvcEndpointInvokerFactory(new ActionInvokerFactory(Array.Empty<IActionInvokerProvider>())),
|
|
actionDescriptorChangeProviders ?? Array.Empty<IActionDescriptorChangeProvider>(),
|
|
serviceProviderMock.Object);
|
|
|
|
return dataSource;
|
|
}
|
|
|
|
private MvcEndpointInfo CreateEndpointInfo(
|
|
string name,
|
|
string template,
|
|
RouteValueDictionary defaults = null,
|
|
IDictionary<string, object> constraints = null,
|
|
RouteValueDictionary dataTokens = null)
|
|
{
|
|
var routeOptions = new RouteOptions();
|
|
var routeOptionsSetup = new MvcCoreRouteOptionsSetup();
|
|
routeOptionsSetup.Configure(routeOptions);
|
|
|
|
var constraintResolver = new DefaultInlineConstraintResolver(Options.Create<RouteOptions>(routeOptions));
|
|
return new MvcEndpointInfo(name, template, defaults, constraints, dataTokens, constraintResolver);
|
|
}
|
|
|
|
private IActionDescriptorCollectionProvider GetActionDescriptorCollection(params object[] requiredValues)
|
|
{
|
|
var actionDescriptors = new List<ActionDescriptor>();
|
|
foreach (var requiredValue in requiredValues)
|
|
{
|
|
actionDescriptors.Add(CreateActionDescriptor(requiredValue));
|
|
}
|
|
|
|
var actionDescriptorCollectionProviderMock = new Mock<IActionDescriptorCollectionProvider>();
|
|
actionDescriptorCollectionProviderMock
|
|
.Setup(m => m.ActionDescriptors)
|
|
.Returns(new ActionDescriptorCollection(actionDescriptors, version: 0));
|
|
return actionDescriptorCollectionProviderMock.Object;
|
|
}
|
|
|
|
private ActionDescriptor CreateActionDescriptor(string controller, string action, string area = null)
|
|
{
|
|
return CreateActionDescriptor(new { controller = controller, action = action, area = area });
|
|
}
|
|
|
|
private ActionDescriptor CreateActionDescriptor(object requiredValues)
|
|
{
|
|
var actionDescriptor = new ActionDescriptor();
|
|
var routeValues = new RouteValueDictionary(requiredValues);
|
|
foreach (var kvp in routeValues)
|
|
{
|
|
actionDescriptor.RouteValues[kvp.Key] = kvp.Value?.ToString();
|
|
}
|
|
return actionDescriptor;
|
|
}
|
|
|
|
private void AssertIsSubset(
|
|
IReadOnlyDictionary<string, object> subset,
|
|
IReadOnlyDictionary<string, object> fullSet)
|
|
{
|
|
foreach (var subsetPair in subset)
|
|
{
|
|
var isPresent = fullSet.TryGetValue(subsetPair.Key, out var fullSetPairValue);
|
|
Assert.True(isPresent);
|
|
Assert.Equal(subsetPair.Value, fullSetPairValue);
|
|
}
|
|
}
|
|
}
|
|
} |