aspnetcore/test/Microsoft.AspNetCore.Routin.../Matchers/MatcherConformanceTest.cs

329 lines
12 KiB
C#

// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public abstract class MatcherConformanceTest
{
internal abstract Matcher CreateMatcher(MatcherEndpoint endpoint);
[Fact]
public virtual async Task Match_SingleLiteralSegment()
{
// Arrange
var (matcher, endpoint) = CreateMatcher("/simple");
var (httpContext, feature) = CreateContext("/simple");
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, endpoint);
}
[Fact]
public virtual async Task Match_SingleLiteralSegment_TrailingSlash()
{
// Arrange
var (matcher, endpoint) = CreateMatcher("/simple");
var (httpContext, feature) = CreateContext("/simple/");
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertNotMatch(feature);
}
[Theory]
[InlineData("simple")]
[InlineData("/simple")]
[InlineData("~/simple")]
public virtual async Task Match_Sanitizies_Template(string template)
{
// Arrange
var (matcher, endpoint) = CreateMatcher(template);
var (httpContext, feature) = CreateContext("/simple");
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertNotMatch(feature);
}
[Fact]
public virtual async Task Match_SingleParameter()
{
// Arrange
var (matcher, endpoint) = CreateMatcher("/{p}");
var (httpContext, feature) = CreateContext("/14");
var values = new RouteValueDictionary(new { p = "14", });
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, endpoint, values);
}
[Fact]
public virtual async Task Match_SingleParameter_TrailingSlash()
{
// Arrange
var (matcher, endpoint) = CreateMatcher("/{p}");
var (httpContext, feature) = CreateContext("/14/");
var values = new RouteValueDictionary(new { p = "14", });
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertNotMatch(feature);
}
[Theory]
[InlineData("/subscriptions/{subscriptionId}/providers/Microsoft.Insights/metricAlerts", "/subscriptions/foo/providers/Microsoft.Insights/metricAlerts", new string[] { "subscriptionId", }, new string[] { "foo", })]
[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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertMatch(feature, 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, feature) = CreateContext(path);
// Act
await matcher.MatchAsync(httpContext, feature);
// Assert
DispatcherAssert.AssertNotMatch(feature);
}
internal static (HttpContext httpContext, IEndpointFeature feature) CreateContext(string path)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Method = "TEST";
httpContext.Request.Path = path;
httpContext.RequestServices = CreateServices();
var feature = new EndpointFeature();
httpContext.Features.Set<IEndpointFeature>(feature);
return (httpContext, feature);
}
// The older routing implementations retrieve services when they first execute.
internal static IServiceProvider CreateServices()
{
var services = new ServiceCollection();
services.AddLogging();
return services.BuildServiceProvider();
}
internal static MatcherEndpoint CreateEndpoint(string template)
{
return new MatcherEndpoint(
MatcherEndpoint.EmptyInvoker,
template,
null,
0,
EndpointMetadataCollection.Empty,
"endpoint: " + template,
address: null);
}
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template)
{
var endpoint = CreateEndpoint(template);
return (CreateMatcher(endpoint), endpoint);
}
}
}