// 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.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing.EndpointConstraints; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using Xunit; namespace Microsoft.AspNetCore.Routing.Matchers { // Many of these are integration tests that exercise the system end to end, // so we're reusing the services here. public class DfaMatcherTest { private MatcherEndpoint CreateEndpoint(string template, int order, object defaultValues = null, EndpointMetadataCollection metadata = null) { return new MatcherEndpoint( (next) => null, template, new RouteValueDictionary(defaultValues), new RouteValueDictionary(), new List(), order, metadata ?? EndpointMetadataCollection.Empty, template); } private Matcher CreateDfaMatcher(EndpointDataSource dataSource) { var services = new ServiceCollection() .AddLogging() .AddOptions() .AddRouting() .AddDispatcher() .BuildServiceProvider(); var factory = services.GetRequiredService(); return Assert.IsType(factory.CreateMatcher(dataSource)); } [Fact] public async Task MatchAsync_ValidRouteConstraint_EndpointMatched() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List { CreateEndpoint("/{p:int}", 0) }); var treeMatcher = CreateDfaMatcher(endpointDataSource); var httpContext = new DefaultHttpContext(); httpContext.Request.Path = "/1"; var endpointFeature = new EndpointFeature(); // Act await treeMatcher.MatchAsync(httpContext, endpointFeature); // Assert Assert.NotNull(endpointFeature.Endpoint); } [Fact] public async Task MatchAsync_InvalidRouteConstraint_NoEndpointMatched() { // Arrange var endpointDataSource = new DefaultEndpointDataSource(new List { CreateEndpoint("/{p:int}", 0) }); var treeMatcher = CreateDfaMatcher(endpointDataSource); var httpContext = new DefaultHttpContext(); httpContext.Request.Path = "/One"; var endpointFeature = new EndpointFeature(); // Act await treeMatcher.MatchAsync(httpContext, endpointFeature); // Assert Assert.Null(endpointFeature.Endpoint); } [Fact] public async Task MatchAsync_DuplicateTemplatesAndDifferentOrder_LowerOrderEndpointMatched() { // Arrange var higherOrderEndpoint = CreateEndpoint("/Teams", 1); var lowerOrderEndpoint = CreateEndpoint("/Teams", 0); var endpointDataSource = new DefaultEndpointDataSource(new List { higherOrderEndpoint, lowerOrderEndpoint }); var treeMatcher = CreateDfaMatcher(endpointDataSource); var httpContext = new DefaultHttpContext(); httpContext.Request.Path = "/Teams"; var endpointFeature = new EndpointFeature(); // Act await treeMatcher.MatchAsync(httpContext, endpointFeature); // Assert Assert.Equal(lowerOrderEndpoint, endpointFeature.Endpoint); } [Fact] public async Task MatchAsync_MultipleMatches_EndpointSelectorCalled() { // Arrange var endpointWithoutConstraint = CreateEndpoint("/Teams", 0); var endpointWithConstraint = CreateEndpoint( "/Teams", 0, metadata: new EndpointMetadataCollection(new object[] { new HttpMethodEndpointConstraint(new[] { "POST" }) })); var endpointDataSource = new DefaultEndpointDataSource(new List { endpointWithoutConstraint, endpointWithConstraint }); var treeMatcher = CreateDfaMatcher(endpointDataSource); var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "POST"; httpContext.Request.Path = "/Teams"; var endpointFeature = new EndpointFeature(); // Act await treeMatcher.MatchAsync(httpContext, endpointFeature); // Assert Assert.Equal(endpointWithConstraint, endpointFeature.Endpoint); } } }