public async Task ApplyAsync_CanReplaceFoundEndpoints()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { new RouteValueDictionary(new { slug = "test", }), };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    state
                })));
            };

            Filter = (c, values, state, endpoints) => new ValueTask <IReadOnlyList <Endpoint> >(new[]
            {
                new Endpoint((ctx) => Task.CompletedTask, new EndpointMetadataCollection(Array.Empty <object>()), "ReplacedEndpoint")
            });

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.Collection(
                candidates[0].Values.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("action", kvp.Key);
                Assert.Equal("Index", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("controller", kvp.Key);
                Assert.Equal("Home", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("slug", kvp.Key);
                Assert.Equal("test", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("state", kvp.Key);
                Assert.Same(State, kvp.Value);
            });
            Assert.Equal("ReplacedEndpoint", candidates[0].Endpoint.DisplayName);
            Assert.True(candidates.IsValidCandidate(0));
        }
        public async Task ApplyAsync_ThrowsForTransformerWithInvalidLifetime()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { new RouteValueDictionary(new { slug = "test", }), };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    state
                })));
            };

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = new ServiceCollection().AddScoped(sp => new CustomTransformer {
                    State = "Invalid"
                }).BuildServiceProvider(),
            };

            // Act & Assert
            await Assert.ThrowsAsync <InvalidOperationException>(() => policy.ApplyAsync(httpContext, candidates));
        }
        public async Task ApplyAsync_HasMatchNoEndpointFound()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { null, };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary()));
            };

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.Null(candidates[0].Endpoint);
            Assert.Null(candidates[0].Values);
            Assert.False(candidates.IsValidCandidate(0));
        }
        public async Task ApplyAsync_NoMatch()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { null, };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            candidates.SetValidity(0, false);

            Transform = (c, values, state) =>
            {
                throw new InvalidOperationException();
            };

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.False(candidates.IsValidCandidate(0));
        }
        public async Task ApplyAsync_HasMatchFindsEndpoint_WithRouteValues()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { new RouteValueDictionary(new { slug = "test", }), };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    state
                })));
            };

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.Same(ControllerEndpoints[0], candidates[0].Endpoint);
            Assert.Collection(
                candidates[0].Values.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("action", kvp.Key);
                Assert.Equal("Index", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("controller", kvp.Key);
                Assert.Equal("Home", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("slug", kvp.Key);
                Assert.Equal("test", kvp.Value);
            },
                kvp =>
            {
                Assert.Equal("state", kvp.Key);
                Assert.Same(State, kvp.Value);
            });
            Assert.True(candidates.IsValidCandidate(0));
        }
        public async Task ApplyAsync_CanExpandTheListOfFoundEndpoints()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(SelectorCache, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { new RouteValueDictionary(new { slug = "test", }), };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    state
                })));
            };

            Filter = (c, values, state, endpoints) => new ValueTask <IReadOnlyList <Endpoint> >(new[]
            {
                ControllerEndpoints[1], ControllerEndpoints[2]
            });

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.Equal(2, candidates.Count);
            Assert.True(candidates.IsValidCandidate(0));
            Assert.True(candidates.IsValidCandidate(1));
            Assert.Same(ControllerEndpoints[1], candidates[0].Endpoint);
            Assert.Same(ControllerEndpoints[2], candidates[1].Endpoint);
        }
        public async Task ApplyAsync_CanDiscardFoundEndpoints()
        {
            // Arrange
            var policy = new DynamicControllerEndpointMatcherPolicy(Selector, Comparer);

            var endpoints = new[] { DynamicEndpoint, };
            var values    = new RouteValueDictionary[] { new RouteValueDictionary(new { slug = "test", }), };
            var scores    = new[] { 0, };

            var candidates = new CandidateSet(endpoints, values, scores);

            Transform = (c, values, state) =>
            {
                return(new ValueTask <RouteValueDictionary>(new RouteValueDictionary(new
                {
                    controller = "Home",
                    action = "Index",
                    state
                })));
            };

            Filter = (c, values, state, endpoints) =>
            {
                return(new ValueTask <IReadOnlyList <Endpoint> >(Array.Empty <Endpoint>()));
            };

            var httpContext = new DefaultHttpContext()
            {
                RequestServices = Services,
            };

            // Act
            await policy.ApplyAsync(httpContext, candidates);

            // Assert
            Assert.False(candidates.IsValidCandidate(0));
        }