// 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.Http; using Microsoft.AspNetCore.Routing.Patterns; using Xunit; namespace Microsoft.AspNetCore.Routing.Matchers { public class DefaultEndpointSelectorTest { [Fact] public async Task SelectAsync_NoCandidates_DoesNothing() { // Arrange var endpoints = new MatcherEndpoint[] { }; var scores = new int[] { }; var candidateSet = CreateCandidateSet(endpoints, scores); var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Null(feature.Endpoint); } [Fact] public async Task SelectAsync_NoValidCandidates_DoesNothing() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test"), }; var scores = new int[] { 0, }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].Values = new RouteValueDictionary(); candidateSet[0].IsValidCandidate = false; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Null(feature.Endpoint); Assert.Null(feature.Values); } [Fact] public async Task SelectAsync_SingleCandidate_ChoosesCandidate() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test"), }; var scores = new int[] { 0, }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].Values = new RouteValueDictionary(); candidateSet[0].IsValidCandidate = true; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Same(endpoints[0], feature.Endpoint); Assert.Same(endpoints[0].Invoker, feature.Invoker); Assert.NotNull(feature.Values); } [Fact] public async Task SelectAsync_SingleValidCandidate_ChoosesCandidate() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), }; var scores = new int[] { 0, 0 }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].IsValidCandidate = false; candidateSet[1].IsValidCandidate = true; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Same(endpoints[1], feature.Endpoint); } [Fact] public async Task SelectAsync_SingleValidCandidateInGroup_ChoosesCandidate() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), }; var scores = new int[] { 0, 0, 1 }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].IsValidCandidate = false; candidateSet[1].IsValidCandidate = true; candidateSet[2].IsValidCandidate = true; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Same(endpoints[1], feature.Endpoint); } [Fact] public async Task SelectAsync_ManyGroupsLastCandidate_ChoosesCandidate() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), CreateEndpoint("/test4"), CreateEndpoint("/test5"), }; var scores = new int[] { 0, 1, 2, 3, 4 }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].IsValidCandidate = false; candidateSet[1].IsValidCandidate = false; candidateSet[2].IsValidCandidate = false; candidateSet[3].IsValidCandidate = false; candidateSet[4].IsValidCandidate = true; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act await selector.SelectAsync(httpContext, feature, candidateSet); // Assert Assert.Same(endpoints[4], feature.Endpoint); } [Fact] public async Task SelectAsync_MultipleValidCandidatesInGroup_ReportsAmbiguity() { // Arrange var endpoints = new MatcherEndpoint[] { CreateEndpoint("/test1"), CreateEndpoint("/test2"), CreateEndpoint("/test3"), }; var scores = new int[] { 0, 1, 1 }; var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].IsValidCandidate = false; candidateSet[1].IsValidCandidate = true; candidateSet[2].IsValidCandidate = true; var (httpContext, feature) = CreateContext(); var selector = CreateSelector(); // Act var ex = await Assert.ThrowsAsync(() => selector.SelectAsync(httpContext, feature, candidateSet)); // Assert Assert.Equal( @"The request matched multiple endpoints. Matches: test: /test2 test: /test3", ex.Message); Assert.Null(feature.Endpoint); } private static (HttpContext httpContext, IEndpointFeature feature) CreateContext() { return (new DefaultHttpContext(), new EndpointFeature()); } private static MatcherEndpoint CreateEndpoint(string template) { return new MatcherEndpoint( MatcherEndpoint.EmptyInvoker, RoutePatternFactory.Parse(template), new RouteValueDictionary(), 0, EndpointMetadataCollection.Empty, $"test: {template}"); } private static CandidateSet CreateCandidateSet(MatcherEndpoint[] endpoints, int[] scores) { return new CandidateSet(endpoints, scores); } private static DefaultEndpointSelector CreateSelector() { return new DefaultEndpointSelector(); } } }