// Copyright (c) Microsoft Open Technologies, Inc. // All Rights Reserved // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING // WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF // TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR // NON-INFRINGEMENT. // See the Apache 2 License for the specific language governing // permissions and limitations under the License. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Microsoft.AspNet.DependencyInjection; using Microsoft.AspNet.Routing; 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 = new RequestContext(CreateHttpContext("POST"), new Dictionary()); // 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(IEnumerable actions) { var actionProvider = new Mock>(MockBehavior.Strict); actionProvider .Setup(p => p.Invoke(It.IsAny())) .Callback(c => c.Results.AddRange(actions)); 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 HttpContext CreateHttpContext(string httpMethod) { var context = new Mock(MockBehavior.Strict); var request = new Mock(MockBehavior.Strict); context.SetupGet(c => c.Request).Returns(request.Object); request.SetupGet(r => r.Method).Returns(httpMethod); return context.Object; } 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; } } }