// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. #if NET45 using System; using Microsoft.AspNet.Testing; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Moq; using Xunit; namespace Microsoft.AspNet.Routing.Template.Tests { public class TemplateRouteTests { #region Route Matching // PathString in HttpAbstractions guarantees a leading slash - so no value in testing other cases. [Fact] public async void Match_Success_LeadingSlash() { // Arrange var route = CreateRoute("{controller}/{action}"); var context = CreateRouteContext("/Home/Index"); // Act await route.RouteAsync(context); // Assert Assert.True(context.IsHandled); Assert.Equal(2, context.Values.Count); Assert.Equal("Home", context.Values["controller"]); Assert.Equal("Index", context.Values["action"]); } [Fact] public async void Match_Success_RootUrl() { // Arrange var route = CreateRoute(""); var context = CreateRouteContext("/"); // Act await route.RouteAsync(context); // Assert Assert.True(context.IsHandled); Assert.Equal(0, context.Values.Count); } [Fact] public async void Match_Success_Defaults() { // Arrange var route = CreateRoute("{controller}/{action}", new { action = "Index" }); var context = CreateRouteContext("/Home"); // Act await route.RouteAsync(context); // Assert Assert.True(context.IsHandled); Assert.Equal(2, context.Values.Count); Assert.Equal("Home", context.Values["controller"]); Assert.Equal("Index", context.Values["action"]); } [Fact] public async void Match_Fails() { // Arrange var route = CreateRoute("{controller}/{action}"); var context = CreateRouteContext("/Home"); // Act await route.RouteAsync(context); // Assert Assert.False(context.IsHandled); } [Fact] public async void Match_RejectedByHandler() { // Arrange var route = CreateRoute("{controller}", accept: false); var context = CreateRouteContext("/Home"); // Act await route.RouteAsync(context); // Assert Assert.False(context.IsHandled); // Issue #16 tracks this. Assert.NotNull(context.Values); } private static RouteContext CreateRouteContext(string requestPath) { var request = new Mock(MockBehavior.Strict); request.SetupGet(r => r.Path).Returns(new PathString(requestPath)); var context = new Mock(MockBehavior.Strict); context.SetupGet(c => c.Request).Returns(request.Object); return new RouteContext(context.Object); } #endregion #region Route Binding [Fact] public void GetVirtualPath_Success() { // Arrange var route = CreateRoute("{controller}"); var context = CreateVirtualPathContext(new { controller = "Home" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.True(context.IsBound); Assert.Equal("Home", path); } [Fact] public void GetVirtualPath_Fail() { // Arrange var route = CreateRoute("{controller}/{action}"); var context = CreateVirtualPathContext(new { controller = "Home" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.False(context.IsBound); Assert.Null(path); } [Fact] public void GetVirtualPath_RejectedByHandler() { // Arrange var route = CreateRoute("{controller}", accept: false); var context = CreateVirtualPathContext(new { controller = "Home" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.False(context.IsBound); Assert.Null(path); } [Fact] public void GetVirtualPath_Success_AmbientValues() { // Arrange var route = CreateRoute("{controller}/{action}"); var context = CreateVirtualPathContext(new { action = "Index" }, new { controller = "Home" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.True(context.IsBound); Assert.Equal("Home/Index", path); } [Fact] public void RouteGenerationRejectsConstraints() { // Arrange var context = CreateVirtualPathContext(new { p1 = "abcd" }); TemplateRoute r = CreateRoute( "{p1}/{p2}", new { p2 = "catchall" }, true, new RouteValueDictionary(new { p2 = "\\d{4}" })); // Act var virtualPath = r.GetVirtualPath(context); // Assert Assert.False(context.IsBound); Assert.Null(virtualPath); } [Fact] public void RouteGenerationAcceptsConstraints() { // Arrange var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" }); TemplateRoute r = CreateRoute( "{p1}/{p2}", new { p2 = "catchall" }, true, new RouteValueDictionary(new { p2 = "\\d{4}" })); // Act var virtualPath = r.GetVirtualPath(context); // Assert Assert.True(context.IsBound); Assert.NotNull(virtualPath); Assert.Equal("hello/1234", virtualPath); } [Fact] public void RouteWithCatchAllRejectsConstraints() { // Arrange var context = CreateVirtualPathContext(new { p1 = "abcd" }); TemplateRoute r = CreateRoute( "{p1}/{*p2}", new { p2 = "catchall" }, true, new RouteValueDictionary(new { p2 = "\\d{4}" })); // Act var virtualPath = r.GetVirtualPath(context); // Assert Assert.False(context.IsBound); Assert.Null(virtualPath); } [Fact] public void RouteWithCatchAllAcceptsConstraints() { // Arrange // Arrange var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" }); TemplateRoute r = CreateRoute( "{p1}/{*p2}", new { p2 = "catchall" }, true, new RouteValueDictionary(new { p2 = "\\d{4}" })); // Act var virtualPath = r.GetVirtualPath(context); // Assert Assert.True(context.IsBound); Assert.NotNull(virtualPath); Assert.Equal("hello/1234", virtualPath); } [Fact] public void GetVirtualPathWithNonParameterConstraintReturnsUrlWithoutQueryString() { // Arrange var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" }); var target = new Mock(); target.Setup(e => e.Match(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) .Returns(true) .Verifiable(); TemplateRoute r = CreateRoute( "{p1}/{p2}", new { p2 = "catchall" }, true, new RouteValueDictionary(new { p2 = target.Object })); // Act var virtualPath = r.GetVirtualPath(context); // Assert Assert.True(context.IsBound); Assert.NotNull(virtualPath); Assert.Equal("hello/1234", virtualPath); target.VerifyAll(); } [Fact] public void GetVirtualPath_Sends_ProvidedValues() { // Arrange VirtualPathContext childContext = null; var target = new Mock(MockBehavior.Strict); target .Setup(r => r.GetVirtualPath(It.IsAny())) .Callback(c => { childContext = c; c.IsBound = true; }) .Returns(null); var route = CreateRoute(target.Object, "{controller}/{action}"); var context = CreateVirtualPathContext(new { action = "Store" }, new { Controller = "Home", action = "Blog"}); var expectedValues = new RouteValueDictionary(new {controller = "Home", action = "Store"}); // Act var path = route.GetVirtualPath(context); // Assert Assert.Equal("Home/Store", path); Assert.Equal(expectedValues, childContext.ProvidedValues); } [Fact] public void GetVirtualPath_Sends_ProvidedValues_IncludingDefaults() { // Arrange VirtualPathContext childContext = null; var target = new Mock(MockBehavior.Strict); target .Setup(r => r.GetVirtualPath(It.IsAny())) .Callback(c => { childContext = c; c.IsBound = true; }) .Returns(null); var route = CreateRoute(target.Object, "Admin/{controller}/{action}", new {area = "Admin"}); var context = CreateVirtualPathContext(new { action = "Store" }, new { Controller = "Home", action = "Blog" }); var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store", area = "Admin" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.Equal("Admin/Home/Store", path); Assert.Equal(expectedValues, childContext.ProvidedValues); } [Fact] public void GetVirtualPath_Sends_ProvidedValues_ButNotQueryStringValues() { // Arrange VirtualPathContext childContext = null; var target = new Mock(MockBehavior.Strict); target .Setup(r => r.GetVirtualPath(It.IsAny())) .Callback(c => { childContext = c; c.IsBound = true; }) .Returns(null); var route = CreateRoute(target.Object, "{controller}/{action}"); var context = CreateVirtualPathContext(new { action = "Store", id = 5 }, new { Controller = "Home", action = "Blog" }); var expectedValues = new RouteValueDictionary(new { controller = "Home", action = "Store" }); // Act var path = route.GetVirtualPath(context); // Assert Assert.Equal("Home/Store?id=5", path); Assert.Equal(expectedValues, childContext.ProvidedValues); } private static VirtualPathContext CreateVirtualPathContext(object values) { return CreateVirtualPathContext(new RouteValueDictionary(values), null); } private static VirtualPathContext CreateVirtualPathContext(object values, object ambientValues) { return CreateVirtualPathContext(new RouteValueDictionary(values), new RouteValueDictionary(ambientValues)); } private static VirtualPathContext CreateVirtualPathContext(IDictionary values, IDictionary ambientValues) { var context = new Mock(MockBehavior.Strict); return new VirtualPathContext(context.Object, ambientValues, values); } private static VirtualPathContext CreateVirtualPathContext(string routeName) { return new VirtualPathContext(null, null, null, routeName); } #endregion #region Route Registration [Fact] public void RegisteringRouteWithInvalidConstraints_Throws() { // Arrange var collection = new RouteCollection(); collection.DefaultHandler = new Mock().Object; // Assert ExceptionAssert.Throws(() => collection.MapRoute("mockName", "{controller}/{action}", defaults: null, constraints: new { controller = "a.*", action = new Object() }), "The constraint entry 'action' on the route with route template '{controller}/{action}' " + "must have a string value or be of a type which implements '" + typeof(IRouteConstraint) + "'."); } [Fact] public void RegisteringRouteWithTwoConstraints() { // Arrange var collection = new RouteCollection(); collection.DefaultHandler = new Mock().Object; var mockConstraint = new Mock().Object; collection.MapRoute("mockName", "{controller}/{action}", defaults: null, constraints: new { controller = "a.*", action = mockConstraint }); var constraints = ((TemplateRoute)collection[0]).Constraints; // Assert Assert.Equal(2, constraints.Count); Assert.IsType(constraints["controller"]); Assert.Equal(mockConstraint, constraints["action"]); } [Fact] public void RegisteringRouteWithRouteName_WithNullDefaults_AddsTheRoute() { // Arrange var collection = new RouteCollection(); collection.DefaultHandler = new Mock().Object; collection.MapRoute(name: "RouteName", template: "{controller}/{action}", defaults: null); // Act var name = ((TemplateRoute)collection[0]).Name; // Assert Assert.Equal("RouteName", name); } [Fact] public void RegisteringRouteWithRouteName_WithNullDefaultsAndConstraints_AddsTheRoute() { // Arrange var collection = new RouteCollection(); collection.DefaultHandler = new Mock().Object; collection.MapRoute(name: "RouteName", template: "{controller}/{action}", defaults: null, constraints: null); // Act var name = ((TemplateRoute)collection[0]).Name; // Assert Assert.Equal("RouteName", name); } #endregion private static TemplateRoute CreateRoute(string template, bool accept = true) { return new TemplateRoute(CreateTarget(accept), template); } private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true, IDictionary constraints = null) { return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults), constraints); } private static TemplateRoute CreateRoute(IRouter target, string template) { return new TemplateRoute(target, template, new RouteValueDictionary(), constraints: null); } private static TemplateRoute CreateRoute(IRouter target, string template, object defaults) { return new TemplateRoute(target, template, new RouteValueDictionary(defaults), constraints: null); } private static IRouter CreateTarget(bool accept = true) { var target = new Mock(MockBehavior.Strict); target .Setup(e => e.GetVirtualPath(It.IsAny())) .Callback(c => c.IsBound = accept) .Returns(rc => null); target .Setup(e => e.RouteAsync(It.IsAny())) .Callback(async (c) => c.IsHandled = accept) .Returns(Task.FromResult(null)); return target.Object; } } } #endif