// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Http; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.Core.Test { public class DefaultActionSelectorTest { [Fact] public void GetCandidateActions_Match_NonArea() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: null, controller: "Home", action: "Index"); var selector = CreateSelector(actions); var context = CreateContext(new { controller = "Home", action = "Index" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_AreaExplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: "Admin", controller: "Home", action: "Index"); var selector = CreateSelector(actions); var context = CreateContext(new { area = "Admin", controller = "Home", action = "Index" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_AreaImplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: "Admin", controller: "Home", action: "Index"); var selector = CreateSelector(actions); var context = CreateContext( new { controller = "Home", action = "Index" }, new { area = "Admin", controller = "Home", action = "Diagnostics" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_NonAreaImplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: null, controller: "Home", action: "Edit"); var selector = CreateSelector(actions); var context = CreateContext( new { controller = "Home", action = "Edit" }, new { area = "Admin", controller = "Home", action = "Diagnostics" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_NonAreaExplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: null, controller: "Home", action: "Index"); var selector = CreateSelector(actions); var context = CreateContext( new { area = (string)null, controller = "Home", action = "Index" }, new { area = "Admin", controller = "Home", action = "Diagnostics" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_RestExplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: null, controller: "Product", action: null); var selector = CreateSelector(actions); var context = CreateContext( new { controller = "Product", action = (string)null }, new { controller = "Home", action = "Index" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_Match_RestImplicit() { // Arrange var actions = GetActions(); var expected = GetActions(actions, area: null, controller: "Product", action: null); var selector = CreateSelector(actions); var context = CreateContext( new { controller = "Product" }, new { controller = "Home", action = "Index" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Equal(expected, candidates); } [Fact] public void GetCandidateActions_NoMatch() { // Arrange var actions = GetActions(); var selector = CreateSelector(actions); var context = CreateContext( new { area = "Admin", controller = "Home", action = "Edit" }, new { area = "Admin", controller = "Home", action = "Index" }); // Act var candidates = selector.GetCandidateActions(context); // Assert Assert.Empty(candidates); } [Fact] public void HasValidAction_Match() { // Arrange var actions = GetActions(); var selector = CreateSelector(actions); var context = CreateContext(new { }); context.ProvidedValues = new RouteValueDictionary(new { controller = "Home", action = "Index"}); // Act var isValid = selector.HasValidAction(context); // Assert Assert.True(isValid); } [Fact] public void HasValidAction_NoMatch() { // Arrange var actions = GetActions(); var selector = CreateSelector(actions); var context = CreateContext(new { }); context.ProvidedValues = new RouteValueDictionary(new { controller = "Home", action = "FakeAction" }); // Act var isValid = selector.HasValidAction(context); // Assert Assert.False(isValid); } [Fact] public async Task SelectAsync_PrefersActionWithConstraints() { // Arrange var actionWithConstraints = new ActionDescriptor() { MethodConstraints = new List() { new HttpMethodConstraint(new string[] { "POST" }), }, Parameters = new List(), }; var actionWithoutConstraints = new ActionDescriptor() { Parameters = new List(), }; var actions = new ActionDescriptor[] { actionWithConstraints, actionWithoutConstraints }; var selector = CreateSelector(actions); var context = CreateRouteContext("POST"); // Act var action = await selector.SelectAsync(context); // Assert Assert.Same(action, actionWithConstraints); } private static ActionDescriptor[] GetActions() { return new ActionDescriptor[] { // Like a typical RPC controller CreateAction(area: null, controller: "Home", action: "Index"), CreateAction(area: null, controller: "Home", action: "Edit"), // Like a typical REST controller CreateAction(area: null, controller: "Product", action: null), CreateAction(area: null, controller: "Product", action: null), // RPC controller in an area with the same name as home CreateAction(area: "Admin", controller: "Home", action: "Index"), CreateAction(area: "Admin", controller: "Home", action: "Diagnostics"), }; } private static IEnumerable GetActions( IEnumerable actions, string area, string controller, string action) { return actions .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "area" && c.Comparer.Equals(c.RouteValue, area))) .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "controller" && c.Comparer.Equals(c.RouteValue, controller))) .Where(a => a.RouteConstraints.Any(c => c.RouteKey == "action" && c.Comparer.Equals(c.RouteValue, action))); } private static DefaultActionSelector CreateSelector(IReadOnlyList actions) { var actionProvider = new Mock(MockBehavior.Strict); actionProvider .Setup(p => p.ActionDescriptors).Returns(new ActionDescriptorsCollection(actions, 0)); var bindingProvider = new Mock(MockBehavior.Strict); bindingProvider .Setup(bp => bp.GetActionBindingContextAsync(It.IsAny())) .Returns(() => Task.FromResult(null)); return new DefaultActionSelector(actionProvider.Object, bindingProvider.Object); } private static VirtualPathContext CreateContext(object routeValues) { return CreateContext(routeValues, ambientValues: null); } private static VirtualPathContext CreateContext(object routeValues, object ambientValues) { return new VirtualPathContext( new Mock(MockBehavior.Strict).Object, new RouteValueDictionary(ambientValues), new RouteValueDictionary(routeValues)); } private static RouteContext CreateRouteContext(string httpMethod) { var routeData = new RouteData() { Values = new Dictionary(StringComparer.OrdinalIgnoreCase), }; routeData.Routers.Add(new Mock(MockBehavior.Strict).Object); var httpContext = new Mock(MockBehavior.Strict); var request = new Mock(MockBehavior.Strict); httpContext.SetupGet(c => c.Request).Returns(request.Object); request.SetupGet(r => r.Method).Returns(httpMethod); request.SetupGet(r => r.Path).Returns(new PathString()); return new RouteContext(httpContext.Object) { RouteData = routeData, }; } private static ActionDescriptor CreateAction(string area, string controller, string action) { var actionDescriptor = new ActionDescriptor() { Name = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action), RouteConstraints = new List(), }; actionDescriptor.RouteConstraints.Add( area == null ? new RouteDataActionConstraint("area", RouteKeyHandling.DenyKey) : new RouteDataActionConstraint("area", area)); actionDescriptor.RouteConstraints.Add( controller == null ? new RouteDataActionConstraint("controller", RouteKeyHandling.DenyKey) : new RouteDataActionConstraint("controller", controller)); actionDescriptor.RouteConstraints.Add( action == null ? new RouteDataActionConstraint("action", RouteKeyHandling.DenyKey) : new RouteDataActionConstraint("action", action)); return actionDescriptor; } } }