Add sample matcher tests
This commit is contained in:
parent
1b470f3d3b
commit
08f12f2bfd
|
|
@ -41,7 +41,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
if (context.Handler != null)
|
if (context.Handler != null)
|
||||||
{
|
{
|
||||||
httpContext.Features.Set<IEndpointFeature>(feature);
|
|
||||||
await context.Handler(httpContext);
|
await context.Handler(httpContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,18 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matchers
|
namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
{
|
{
|
||||||
public sealed class MatcherEndpoint : Endpoint
|
public sealed class MatcherEndpoint : Endpoint
|
||||||
{
|
{
|
||||||
|
internal static readonly Func<RequestDelegate, RequestDelegate> EmptyInvoker = (next) =>
|
||||||
|
{
|
||||||
|
return (context) => Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
public MatcherEndpoint(
|
public MatcherEndpoint(
|
||||||
Func<RequestDelegate, RequestDelegate> invoker,
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
string template,
|
string template,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using Xunit.Sdk;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
{
|
||||||
|
internal static class DispatcherAssert
|
||||||
|
{
|
||||||
|
public static void AssertMatch(IEndpointFeature feature, Endpoint expected)
|
||||||
|
{
|
||||||
|
AssertMatch(feature, expected, new RouteValueDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AssertMatch(IEndpointFeature feature, Endpoint expected, RouteValueDictionary values)
|
||||||
|
{
|
||||||
|
if (feature.Endpoint == null)
|
||||||
|
{
|
||||||
|
throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!object.ReferenceEquals(expected, feature.Endpoint))
|
||||||
|
{
|
||||||
|
throw new XunitException(
|
||||||
|
$"Was expected to match '{expected.DisplayName}' but matched " +
|
||||||
|
$"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(feature.Values)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.Count != feature.Values.Count ||
|
||||||
|
!values.OrderBy(kvp => kvp.Key).SequenceEqual(feature.Values.OrderBy(kvp => kvp.Key)))
|
||||||
|
{
|
||||||
|
throw new XunitException(
|
||||||
|
$"Was expected to match '{expected.DisplayName}' with values {FormatRouteValues(values)} but matched " +
|
||||||
|
$"values: {FormatRouteValues(feature.Values)}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AssertNotMatch(IEndpointFeature feature)
|
||||||
|
{
|
||||||
|
if (feature.Endpoint != null)
|
||||||
|
{
|
||||||
|
throw new XunitException(
|
||||||
|
$"Was expected not to match '{feature.Endpoint.DisplayName}' " +
|
||||||
|
$"but matched with values: {FormatRouteValues(feature.Values)}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatRouteValues(RouteValueDictionary values)
|
||||||
|
{
|
||||||
|
return "{" + string.Join(", ", values.Select(kvp => $"{kvp.Key} = '{kvp.Value}'")) + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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.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_Success()
|
||||||
|
{
|
||||||
|
// 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_Sanitizies_TemplatePrefix(string template)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var (matcher, endpoint) = CreateMatcher(template);
|
||||||
|
var (httpContext, feature) = CreateContext("/simple");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await matcher.MatchAsync(httpContext, feature);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DispatcherAssert.AssertMatch(feature, endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (Matcher matcher, MatcherEndpoint endpoint) CreateMatcher(string template)
|
||||||
|
{
|
||||||
|
var endpoint = CreateEndpoint(template);
|
||||||
|
return (CreateMatcher(endpoint), endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing.Tree;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
{
|
||||||
|
// This is an adapter to use TreeRouter in the conformance tests
|
||||||
|
internal class TreeRouterMatcher : Matcher
|
||||||
|
{
|
||||||
|
private readonly TreeRouter _inner;
|
||||||
|
|
||||||
|
internal TreeRouterMatcher(TreeRouter inner)
|
||||||
|
{
|
||||||
|
_inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
|
||||||
|
{
|
||||||
|
if (httpContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(httpContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = new RouteContext(httpContext);
|
||||||
|
await _inner.RouteAsync(context);
|
||||||
|
|
||||||
|
if (context.Handler != null)
|
||||||
|
{
|
||||||
|
httpContext.Features.Get<IEndpointFeature>().Values = context.RouteData.Values;
|
||||||
|
await context.Handler(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Routing.Internal;
|
||||||
|
using Microsoft.AspNetCore.Routing.Template;
|
||||||
|
using Microsoft.AspNetCore.Routing.Tree;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
{
|
||||||
|
public class TreeRouterMatcherConformanceTest : MatcherConformanceTest
|
||||||
|
{
|
||||||
|
internal override Matcher CreateMatcher(MatcherEndpoint endpoint)
|
||||||
|
{
|
||||||
|
var builder = new TreeRouteBuilder(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
|
||||||
|
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
|
||||||
|
|
||||||
|
var handler = new RouteHandler(c =>
|
||||||
|
{
|
||||||
|
var feature = c.Features.Get<IEndpointFeature>();
|
||||||
|
feature.Endpoint = endpoint;
|
||||||
|
feature.Invoker = MatcherEndpoint.EmptyInvoker;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.MapInbound(handler, TemplateParser.Parse(endpoint.Template), "default", 0);
|
||||||
|
|
||||||
|
return new TreeRouterMatcher(builder.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue