// 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 Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.AspNetCore.Routing { // Tests LinkGenerator functionality using GetXyzByAddress - see tests for the extension // methods for more E2E tests. // // Does not cover template processing in detail, those scenarios are validated by TemplateBinderTests // and DefaultLinkGeneratorProcessTemplateTest public class DefaultLinkGeneratorTest : LinkGeneratorTestBase { [Fact] public void GetPathByAddress_WithoutHttpContext_NoMatches_ReturnsNull() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint); // Act var path = linkGenerator.GetPathByAddress(0, values: null); // Assert Assert.Null(path); } [Fact] public void GetPathByAddress_WithHttpContext_NoMatches_ReturnsNull() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint); // Act var path = linkGenerator.GetPathByAddress(CreateHttpContext(), 0, values: null); // Assert Assert.Null(path); } [Fact] public void GetUriByAddress_WithoutHttpContext_NoMatches_ReturnsNull() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint); // Act var uri = linkGenerator.GetUriByAddress(0, values: null, "http", new HostString("example.com")); // Assert Assert.Null(uri); } [Fact] public void GetUriByAddress_WithHttpContext_NoMatches_ReturnsNull() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint); // Act var uri = linkGenerator.GetUriByAddress(CreateHttpContext(), 0, values: null); // Assert Assert.Null(uri); } [Fact] public void GetPathByAddress_WithoutHttpContext_HasMatches_ReturnsFirstSuccessfulTemplateResult() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress(1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", })); // Assert Assert.Equal("/Home/Index", path); } [Fact] public void GetPathByAddress_WithHttpContext_HasMatches_ReturnsFirstSuccessfulTemplateResult() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress(CreateHttpContext(), 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", })); // Assert Assert.Equal("/Home/Index", path); } [Fact] public void GetUriByAddress_WithoutHttpContext_HasMatches_ReturnsFirstSuccessfulTemplateResult() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetUriByAddress( 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", }), "http", new HostString("example.com")); // Assert Assert.Equal("http://example.com/Home/Index", path); } [Fact] public void GetUriByAddress_WithHttpContext_HasMatches_ReturnsFirstSuccessfulTemplateResult() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetUriByAddress(httpContext, 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", })); // Assert Assert.Equal("http://example.com/Home/Index", uri); } [Fact] public void GetPathByAddress_WithoutHttpContext_WithLinkOptions() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress( 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", }), options: new LinkOptions() { AppendTrailingSlash = true, }); // Assert Assert.Equal("/Home/Index/", path); } [Fact] public void GetPathByAddress_WithParameterTransformer() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); Action configureServices = s => { s.Configure(o => { o.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer); }); }; var linkGenerator = CreateLinkGenerator(configureServices, endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress( 1, values: new RouteValueDictionary(new { controller = "TestController", action = "Index", })); // Assert Assert.Equal("/test-controller/Index", path); } [Fact] public void GetPathByAddress_WithParameterTransformer_WithLowercaseUrl() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller:slugify}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); Action configureServices = s => { s.Configure(o => { o.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer); }); }; var linkGenerator = CreateLinkGenerator(configureServices, endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress( 1, values: new RouteValueDictionary(new { controller = "TestController", action = "Index", }), options: new LinkOptions() { LowercaseUrls = true, }); // Assert Assert.Equal("/test-controller/index", path); } [Fact] public void GetPathByAddress_WithHttpContext_WithLinkOptions() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress( CreateHttpContext(), 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", }), options: new LinkOptions() { AppendTrailingSlash = true, }); // Assert Assert.Equal("/Home/Index/", path); } [Fact] public void GetUriByAddress_WithoutHttpContext_WithLinkOptions() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetUriByAddress( 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", }), "http", new HostString("example.com"), options: new LinkOptions() { AppendTrailingSlash = true, }); // Assert Assert.Equal("http://example.com/Home/Index/", path); } [Fact] public void GetUriByAddress_WithHttpContext_WithLinkOptions() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetUriByAddress( httpContext, 1, values: new RouteValueDictionary(new { controller = "Home", action = "Index", }), options: new LinkOptions() { AppendTrailingSlash = true, }); // Assert Assert.Equal("http://example.com/Home/Index/", uri); } // Includes characters that need to be encoded [Fact] public void GetPathByAddress_WithoutHttpContext_WithPathBaseAndFragment() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetPathByAddress( 1, values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }), new PathString("/Foo/Bar?encodeme?"), new FragmentString("#Fragment?")); // Assert Assert.Equal("/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex?query=some%3Fquery#Fragment?", path); } private class UpperCaseParameterTransform : IOutboundParameterTransformer { public string TransformOutbound(object value) { return value?.ToString()?.ToUpperInvariant(); } } [Fact] public void GetLink_ParameterTransformer() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller:upper-case}/{name}", requiredValues: new { controller = "Home", name = "Test" }); Action configure = (s) => { s.Configure(o => { o.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform); }); }; var linkGenerator = CreateLinkGenerator(configure, endpoint); // Act var link = linkGenerator.GetPathByRouteValues(routeName: null, new { controller = "Home", name = "Test" }); // Assert Assert.Equal("/HOME/Test", link); } [Fact] public void GetLink_ParameterTransformer_ForQueryString() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint( "{controller:upper-case}/{name}", requiredValues: new { controller = "Home", name = "Test", c = "hithere", }, policies: new { c = new UpperCaseParameterTransform(), }); Action configure = (s) => { s.Configure(o => { o.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform); }); }; var linkGenerator = CreateLinkGenerator(configure, endpoint); // Act var link = linkGenerator.GetPathByRouteValues(routeName: null, new { controller = "Home", name = "Test", c = "hithere", }); // Assert Assert.Equal("/HOME/Test?c=HITHERE", link); } // Includes characters that need to be encoded [Fact] public void GetPathByAddress_WithHttpContext_WithPathBaseAndFragment() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); // Act var path = linkGenerator.GetPathByAddress( httpContext, 1, values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }), fragment: new FragmentString("#Fragment?")); // Assert Assert.Equal("/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex?query=some%3Fquery#Fragment?", path); } // Includes characters that need to be encoded [Fact] public void GetUriByAddress_WithoutHttpContext_WithPathBaseAndFragment() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); // Act var path = linkGenerator.GetUriByAddress( 1, values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }), "http", new HostString("example.com"), new PathString("/Foo/Bar?encodeme?"), new FragmentString("#Fragment?")); // Assert Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex?query=some%3Fquery#Fragment?", path); } // Includes characters that need to be encoded [Fact] public void GetUriByAddress_WithHttpContext_WithPathBaseAndFragment() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); // Act var uri = linkGenerator.GetUriByAddress( httpContext, 1, values: new RouteValueDictionary(new { controller = "Home", action = "In?dex", query = "some?query" }), fragment: new FragmentString("#Fragment?")); // Assert Assert.Equal("http://example.com/Foo/Bar%3Fencodeme%3F/Home/In%3Fdex?query=some%3Fquery#Fragment?", uri); } [Fact] public void GetPathByAddress_WithHttpContext_IncludesAmbientValues() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetPathByAddress( httpContext, 1, values: new RouteValueDictionary(new { action = "Index", }), ambientValues: new RouteValueDictionary(new { controller = "Home", })); // Assert Assert.Equal("/Home/Index", uri); } [Fact] public void GetUriByAddress_WithHttpContext_IncludesAmbientValues() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetUriByAddress( httpContext, 1, values: new RouteValueDictionary(new { action = "Index", }), ambientValues: new RouteValueDictionary(new { controller = "Home", })); // Assert Assert.Equal("http://example.com/Home/Index", uri); } [Fact] public void GetPathByAddress_WithHttpContext_CanOverrideUriParts() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.PathBase = "/Foo"; // Act var uri = linkGenerator.GetPathByAddress( httpContext, 1, values: new RouteValueDictionary(new { action = "Index", controller = "Home", }), pathBase: "/"); // Assert Assert.Equal("/Home/Index", uri); } [Fact] public void GetUriByAddress_WithHttpContext_CanOverrideUriParts() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id?}", metadata: new object[] { new IntMetadata(1), }); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var httpContext = CreateHttpContext(); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); httpContext.Request.PathBase = "/Foo"; // Act var uri = linkGenerator.GetUriByAddress( httpContext, 1, values: new RouteValueDictionary(new { action = "Index", controller = "Home", }), scheme: "ftp", host: new HostString("example.com:5000"), pathBase: "/"); // Assert Assert.Equal("ftp://example.com:5000/Home/Index", uri); } [Fact] public void GetTemplateBinder_CanCache() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var dataSource = new DynamicEndpointDataSource(endpoint1); var linkGenerator = CreateLinkGenerator(dataSources: new[] { dataSource }); var expected = linkGenerator.GetTemplateBinder(endpoint1); // Act var actual = linkGenerator.GetTemplateBinder(endpoint1); // Assert Assert.Same(expected, actual); } [Fact] public void GetTemplateBinder_CanClearCache() { // Arrange var endpoint1 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); var dataSource = new DynamicEndpointDataSource(endpoint1); var linkGenerator = CreateLinkGenerator(dataSources: new[] { dataSource }); var original = linkGenerator.GetTemplateBinder(endpoint1); var endpoint2 = EndpointFactory.CreateRouteEndpoint("{controller}/{action}/{id}", metadata: new object[] { new IntMetadata(1), }); dataSource.AddEndpoint(endpoint2); // Act var actual = linkGenerator.GetTemplateBinder(endpoint1); // Assert Assert.NotSame(original, actual); } [Theory] [InlineData(new string[] { }, new string[] { }, "/")] [InlineData(new string[] { "id" }, new string[] { "3" }, "/Home/Index/3")] [InlineData(new string[] { "custom" }, new string[] { "Custom" }, "/?custom=Custom")] public void GetPathByRouteValues_UsesFirstTemplateThatSucceeds(string[] routeNames, string[] routeValues, string expectedPath) { // Arrange var endpointControllerAction = EndpointFactory.CreateRouteEndpoint( "Home/Index", order: 3, defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); var endpointController = EndpointFactory.CreateRouteEndpoint( "Home", order: 2, defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); var endpointEmpty = EndpointFactory.CreateRouteEndpoint( "", order: 1, defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); // This endpoint should be used to generate the link when an id is present var endpointControllerActionParameter = EndpointFactory.CreateRouteEndpoint( "Home/Index/{id}", order: 0, defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); var linkGenerator = CreateLinkGenerator(endpointControllerAction, endpointController, endpointEmpty, endpointControllerActionParameter); var context = new EndpointSelectorContext() { RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) }; var httpContext = CreateHttpContext(); httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) { values[routeNames[i]] = routeValues[i]; } // Act var generatedPath = linkGenerator.GetPathByRouteValues( httpContext, routeName: null, values: values); // Assert Assert.Equal(expectedPath, generatedPath); } [Theory] [InlineData(new string[] { }, new string[] { }, "/")] [InlineData(new string[] { "id" }, new string[] { "3" }, "/Home/Index/3")] [InlineData(new string[] { "custom" }, new string[] { "Custom" }, "/?custom=Custom")] [InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Login", "3" }, "/Home/Login/3")] [InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Fake", "3" }, null)] public void GetPathByRouteValues_ParameterMatchesRequireValues_HasAmbientValues(string[] routeNames, string[] routeValues, string expectedPath) { // Arrange var homeIndex = EndpointFactory.CreateRouteEndpoint( "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); var homeLogin = EndpointFactory.CreateRouteEndpoint( "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Login", })) }); var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); var context = new EndpointSelectorContext() { RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) }; var httpContext = CreateHttpContext(); httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) { values[routeNames[i]] = routeValues[i]; } // Act var generatedPath = linkGenerator.GetPathByRouteValues( httpContext, routeName: null, values: values); // Assert Assert.Equal(expectedPath, generatedPath); } [Theory] [InlineData(new string[] { }, new string[] { }, null)] [InlineData(new string[] { "id" }, new string[] { "3" }, null)] [InlineData(new string[] { "custom" }, new string[] { "Custom" }, null)] [InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Login", "3" }, "/Home/Login/3")] [InlineData(new string[] { "controller", "action", "id" }, new string[] { "Home", "Fake", "3" }, null)] public void GetPathByRouteValues_ParameterMatchesRequireValues_NoAmbientValues(string[] routeNames, string[] routeValues, string expectedPath) { // Arrange var homeIndex = EndpointFactory.CreateRouteEndpoint( "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Index", })) }); var homeLogin = EndpointFactory.CreateRouteEndpoint( "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index", }, metadata: new[] { new RouteValuesAddressMetadata(new RouteValueDictionary(new { controller = "Home", action = "Login", })) }); var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); var context = new EndpointSelectorContext(); var httpContext = CreateHttpContext(); httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) { values[routeNames[i]] = routeValues[i]; } // Act var generatedPath = linkGenerator.GetPathByRouteValues( httpContext, routeName: null, values: values); // Assert Assert.Equal(expectedPath, generatedPath); } protected override void AddAdditionalServices(IServiceCollection services) { services.AddSingleton, IntAddressScheme>(); } private class IntAddressScheme : IEndpointAddressScheme { private readonly EndpointDataSource _dataSource; public IntAddressScheme(EndpointDataSource dataSource) { _dataSource = dataSource; } public IEnumerable FindEndpoints(int address) { return _dataSource.Endpoints.Where(e => e.Metadata.GetMetadata().Value == address); } } private class IntMetadata { public IntMetadata(int value) { Value = value; } public int Value { get; } } } }