diff --git a/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs b/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs index 14a69237e7..bb887099f6 100644 --- a/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs +++ b/src/Microsoft.AspNet.Routing/RouteConstraintBuilder.cs @@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Routing { var constraint = kvp.Value as IRouteConstraint; - if (constraint == null) + if (constraint == null) { var regexPattern = kvp.Value as string; @@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Routing { throw new InvalidOperationException( Resources.FormatTemplateRoute_ValidationMustBeStringOrCustomConstraint( - kvp.Key, routeTemplate, typeof (IRouteConstraint))); + kvp.Key, routeTemplate, typeof(IRouteConstraint))); } else { diff --git a/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs b/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs index c458f5f721..4e01dc3297 100644 --- a/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs @@ -88,6 +88,25 @@ namespace Microsoft.AspNet.Routing.Tests routeDirection: RouteDirection.IncomingRequest)); } + [Fact] + public void ReturnsFalseOnValidAndInvalidConstraintsMixThatMatch() + { + var constraints = new Dictionary + { + {"a", new PassConstraint()}, + {"b", new FailConstraint()} + }; + + var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" }); + + Assert.False(RouteConstraintMatcher.Match( + constraints: constraints, + routeValues: routeValueDictionary, + httpContext: new Mock().Object, + route: new Mock().Object, + routeDirection: RouteDirection.IncomingRequest)); + } + [Fact] public void ReturnsTrueOnNullInput() { diff --git a/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs b/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs index 208dfa23cc..b6b31341da 100644 --- a/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/ConstraintsBuilderTests.cs @@ -110,6 +110,7 @@ namespace Microsoft.AspNet.Routing.Tests [Theory] [InlineData("abc", "abc", true)] + [InlineData("abc", "bbb|abc", true)] [InlineData("Abc", "abc", true)] [InlineData("Abc ", "abc", false)] [InlineData("Abcd", "abc", false)] diff --git a/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs b/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs index fa16277e37..e38231a714 100644 --- a/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/RegexConstraintTests.cs @@ -1,4 +1,7 @@ -using System.Text.RegularExpressions; +using System; +using System.Globalization; +using System.Text.RegularExpressions; +using System.Threading; using Xunit; namespace Microsoft.AspNet.Routing.Tests @@ -56,5 +59,37 @@ namespace Microsoft.AspNet.Routing.Tests // Assert Assert.False(constraint.EasyMatch("controller", values)); } + + [Fact] + public void RegexConstraintIsCultureInsensitive() + { + // Arrange + var constraint = new RegexConstraint("^([a-z]+)$"); + var values = new RouteValueDictionary(new { controller = "\u0130" }); + + var currentThread = Thread.CurrentThread; + var backupCulture = currentThread.CurrentCulture; + + bool matchInTurkish; + bool matchInUsEnglish; + + // Act + try + { + currentThread.CurrentCulture = new CultureInfo("tr-TR"); // Turkish culture + matchInTurkish = constraint.EasyMatch("controller", values); + + currentThread.CurrentCulture = new CultureInfo("en-US"); + matchInUsEnglish = constraint.EasyMatch("controller", values); + } + finally + { + currentThread.CurrentCulture = backupCulture; + } + + // Assert + Assert.False(matchInUsEnglish); // this just verifies the test + Assert.False(matchInTurkish); + } } } diff --git a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs index c6592375aa..0da9c2553f 100644 --- a/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs +++ b/test/Microsoft.AspNet.Routing.Tests/Template/TemplateRouteTests.cs @@ -3,7 +3,7 @@ #if NET45 using System; using Microsoft.AspNet.Testing; - using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; using Moq; @@ -171,6 +171,119 @@ namespace Microsoft.AspNet.Routing.Template.Tests Assert.Equal("Home/Index", path); } + [Fact] + public void RouteGenerationRejectsConstraints() + { + // Arrange + var context = CreateVirtualPathContext(new { p1 = "abcd" }); + + TemplateRoute r = CreateRoute( + "{p1}/{p2}", + new RouteValueDictionary(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 RouteValueDictionary(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 RouteValueDictionary(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 RouteValueDictionary(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 RouteValueDictionary(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); + } + private static VirtualPathContext CreateVirtualPathContext(object values) { return CreateVirtualPathContext(new RouteValueDictionary(values), null); @@ -219,9 +332,9 @@ namespace Microsoft.AspNet.Routing.Template.Tests collection.MapRoute("{controller}/{action}", defaults: null, - constraints: new {controller = "a.*", action = mockConstraint}); + constraints: new { controller = "a.*", action = mockConstraint }); - var constraints = ((TemplateRoute) collection[0]).Constraints; + var constraints = ((TemplateRoute)collection[0]).Constraints; // Assert Assert.Equal(2, constraints.Count); @@ -237,9 +350,9 @@ namespace Microsoft.AspNet.Routing.Template.Tests return new TemplateRoute(CreateTarget(accept), template); } - private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true) + private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true, IDictionary constraints = null) { - return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults), null); + return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults), constraints); } private static IRouter CreateTarget(bool accept = true)