// 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.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Routing.Matching { public abstract partial class MatcherConformanceTest { [Fact] public virtual async Task Match_EmptyRoute() { // Arrange var (matcher, endpoint) = CreateMatcher("/"); var (httpContext, context) = CreateContext("/"); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] public virtual async Task Match_SingleLiteralSegment() { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); var (httpContext, context) = CreateContext("/simple"); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] public virtual async Task Match_SingleLiteralSegment_TrailingSlash() { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); var (httpContext, context) = CreateContext("/simple/"); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Theory] [InlineData("/simple")] [InlineData("/sImpLe")] [InlineData("/SIMPLE")] public virtual async Task Match_SingleLiteralSegment_CaseInsensitive(string path) { // Arrange var (matcher, endpoint) = CreateMatcher("/Simple"); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Some matchers will optimize for the ASCII case [Theory] [InlineData("/SÏmple", "/SÏmple")] [InlineData("/ab\uD834\uDD1Ecd", "/ab\uD834\uDD1Ecd")] // surrogate pair public virtual async Task Match_SingleLiteralSegment_Unicode(string template, string path) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers should operate on the decoded representation - a matcher that calls // `httpContext.Request.Path.ToString()` will break this test. [Theory] [InlineData("/S%mple", "/S%mple")] [InlineData("/S\\imple", "/S\\imple")] // surrogate pair public virtual async Task Match_SingleLiteralSegment_PercentEncoded(string template, string path) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Theory] [InlineData("/")] [InlineData("/imple")] [InlineData("/siple")] [InlineData("/simple1")] [InlineData("/simple/not-simple")] [InlineData("/simple/a/b/c")] public virtual async Task NotMatch_SingleLiteralSegment(string path) { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] [InlineData("simple")] [InlineData("/simple")] [InlineData("~/simple")] public virtual async Task Match_Sanitizies_Template(string template) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext("/simple"); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers do their own 'splitting' of the path into segments, so including // some extra variation here [Theory] [InlineData("/a/b", "/a/b")] [InlineData("/a/b", "/A/B")] [InlineData("/a/b", "/a/b/")] [InlineData("/a/b/c", "/a/b/c")] [InlineData("/a/b/c", "/a/b/c/")] [InlineData("/a/b/c/d", "/a/b/c/d")] [InlineData("/a/b/c/d", "/a/b/c/d/")] public virtual async Task Match_MultipleLiteralSegments(string template, string path) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers do their own 'splitting' of the path into segments, so including // some extra variation here [Theory] [InlineData("/a/b", "/")] [InlineData("/a/b", "/a")] [InlineData("/a/b", "/a/")] [InlineData("/a/b", "/a//")] [InlineData("/a/b", "/aa/")] [InlineData("/a/b", "/a/bb")] [InlineData("/a/b", "/a/bb/")] [InlineData("/a/b/c", "/aa/b/c")] [InlineData("/a/b/c", "/a/bb/c/")] [InlineData("/a/b/c", "/a/b/cab")] [InlineData("/a/b/c", "/d/b/c/")] [InlineData("/a/b/c", "//b/c")] [InlineData("/a/b/c", "/a/b//")] [InlineData("/a/b/c", "/a/b/c/d")] [InlineData("/a/b/c", "/a/b/c/d/e")] public virtual async Task NotMatch_MultipleLiteralSegments(string template, string path) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertNotMatch(context, httpContext); } [Fact] public virtual async Task Match_SingleParameter() { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); var (httpContext, context) = CreateContext("/14"); var values = new RouteValueDictionary(new { p = "14", }); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] public virtual async Task Match_Constraint() { // Arrange var (matcher, endpoint) = CreateMatcher("/{p:int}"); var (httpContext, context) = CreateContext("/14"); var values = new RouteValueDictionary(new { p = "14", }); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] public virtual async Task Match_SingleParameter_TrailingSlash() { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); var (httpContext, context) = CreateContext("/14/"); var values = new RouteValueDictionary(new { p = "14", }); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] public virtual async Task Match_SingleParameter_WierdNames() { // Arrange var (matcher, endpoint) = CreateMatcher("/foo/{ }/{.!$%}/{dynamic.data}"); var (httpContext, context) = CreateContext("/foo/space/weirdmatch/matcherid"); var values = new RouteValueDictionary() { { " ", "space" }, { ".!$%", "weirdmatch" }, { "dynamic.data", "matcherid" }, }; // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Theory] [InlineData("/")] [InlineData("/a/b")] [InlineData("/a/b/c")] [InlineData("//")] public virtual async Task NotMatch_SingleParameter(string path) { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] [InlineData("/{a}/b", "/54/b", new string[] { "a", }, new string[] { "54", })] [InlineData("/{a}/b", "/54/b/", new string[] { "a", }, new string[] { "54", })] [InlineData("/{a}/{b}", "/54/73", new string[] { "a", "b" }, new string[] { "54", "73", })] [InlineData("/a/{b}/c", "/a/b/c", new string[] { "b", }, new string[] { "b", })] [InlineData("/a/{b}/c/", "/a/b/c", new string[] { "b", }, new string[] { "b", })] [InlineData("/{a}/b/{c}", "/54/b/c", new string[] { "a", "c", }, new string[] { "54", "c", })] [InlineData("/{a}/{b}/{c}", "/54/b/c", new string[] { "a", "b", "c", }, new string[] { "54", "b", "c", })] public virtual async Task Match_MultipleParameters(string template, string path, string[] keys, string[] values) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] [InlineData("/{a}/b", "/54/bb")] [InlineData("/{a}/b", "/54/b/17")] [InlineData("/{a}/b", "/54/b//")] [InlineData("/{a}/{b}", "//73")] [InlineData("/{a}/{b}", "/54//")] [InlineData("/{a}/{b}", "/54/73/18")] [InlineData("/a/{b}/c", "/aa/b/c")] [InlineData("/a/{b}/c", "/a/b/cc")] [InlineData("/a/{b}/c", "/a/b/c/d")] [InlineData("/{a}/b/{c}", "/54/bb/c")] [InlineData("/{a}/{b}/{c}", "/54/b/c/d")] [InlineData("/{a}/{b}/{c}", "/54/b/c//")] [InlineData("/{a}/{b}/{c}", "//b/c/")] [InlineData("/{a}/{b}/{c}", "/54//c/")] [InlineData("/{a}/{b}/{c}", "/54/b//")] public virtual async Task NotMatch_MultipleParameters(string template, string path) { // Arrange var (matcher, endpoint) = CreateMatcher(template); var (httpContext, context) = CreateContext(path); // Act await matcher.MatchAsync(httpContext, context); // Assert MatcherAssert.AssertNotMatch(context, httpContext); } } }