// 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.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_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 : IParameterTransformer { public string Transform(string value) { return value?.ToUpperInvariant(); } } [Fact] public void GetLink_ParameterTransformer() { // Arrange var endpoint = EndpointFactory.CreateRouteEndpoint("{controller:upper-case}/{name}"); var routeOptions = new RouteOptions(); routeOptions.ConstraintMap["upper-case"] = typeof(UpperCaseParameterTransform); Action configure = (s) => { s.AddSingleton(typeof(UpperCaseParameterTransform), new UpperCaseParameterTransform()); }; var linkGenerator = CreateLinkGenerator(routeOptions, configure, endpoint); // Act var link = linkGenerator.GetPathByRouteValues(routeName: null, new { controller = "Home", name = "Test" }); // Assert Assert.Equal("/HOME/Test", 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" }), 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" }), 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(new { controller = "Home", }); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetPathByAddress(httpContext, 1, values: new RouteValueDictionary(new { action = "Index", })); // 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(new { controller = "Home", }); httpContext.Request.Scheme = "http"; httpContext.Request.Host = new HostString("example.com"); // Act var uri = linkGenerator.GetUriByAddress(httpContext, 1, values: new RouteValueDictionary(new { action = "Index", })); // Assert Assert.Equal("http://example.com/Home/Index", uri); } [Fact] public void GetTemplateByAddress_WithNoMatch_ReturnsNull() { // 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 template = linkGenerator.GetTemplateByAddress(address: 0); // Assert Assert.Null(template); } [Fact] public void GetTemplateByAddress_WithMatch_ReturnsTemplate() { // 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 template = linkGenerator.GetTemplateByAddress(address: 1); // Assert Assert.NotNull(template); Assert.Collection( Assert.IsType(template).Endpoints, e => Assert.Same(endpoint1, e), e => Assert.Same(endpoint2, e)); } [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); } protected override void AddAdditionalServices(IServiceCollection services) { services.AddSingleton, IntEndpointFinder>(); } private class IntEndpointFinder : IEndpointFinder { private readonly CompositeEndpointDataSource _dataSource; public IntEndpointFinder(CompositeEndpointDataSource 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; } } } }