|
|
@@ -1,741 +0,0 @@
|
|
|
-// 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.Globalization;
|
|
|
-using System.Linq;
|
|
|
-using Xunit;
|
|
|
-
|
|
|
-// Avoid referencing the whole Microsoft.AspNetCore.Components.Routing namespace to
|
|
|
-// avoid the risk of accidentally relying on the non-legacy types in the legacy fork
|
|
|
-using RouteContext = Microsoft.AspNetCore.Components.Routing.RouteContext;
|
|
|
-
|
|
|
-namespace Microsoft.AspNetCore.Components.LegacyRouteMatching
|
|
|
-{
|
|
|
- public class LegacyRouteTableFactoryTests
|
|
|
- {
|
|
|
- [Fact]
|
|
|
- public void CanCacheRouteTable()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routes1 = LegacyRouteTableFactory.Create(new[] { GetType().Assembly, });
|
|
|
-
|
|
|
- // Act
|
|
|
- var routes2 = LegacyRouteTableFactory.Create(new[] { GetType().Assembly, });
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Same(routes1, routes2);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanCacheRouteTableWithDifferentAssembliesAndOrder()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routes1 = LegacyRouteTableFactory.Create(new[] { typeof(object).Assembly, GetType().Assembly, });
|
|
|
-
|
|
|
- // Act
|
|
|
- var routes2 = LegacyRouteTableFactory.Create(new[] { GetType().Assembly, typeof(object).Assembly, });
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Same(routes1, routes2);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void DoesNotCacheRouteTableForDifferentAssemblies()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routes1 = LegacyRouteTableFactory.Create(new[] { GetType().Assembly, });
|
|
|
-
|
|
|
- // Act
|
|
|
- var routes2 = LegacyRouteTableFactory.Create(new[] { GetType().Assembly, typeof(object).Assembly, });
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotSame(routes1, routes2);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanDiscoverRoute()
|
|
|
- {
|
|
|
- // Arrange & Act
|
|
|
- var routes = LegacyRouteTableFactory.Create(new[] { typeof(MyComponent), });
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Equal("Test1", Assert.Single(routes.Routes).Template.TemplateText);
|
|
|
- }
|
|
|
-
|
|
|
- [Route("Test1")]
|
|
|
- private class MyComponent : ComponentBase
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanDiscoverRoutes_WithInheritance()
|
|
|
- {
|
|
|
- // Arrange & Act
|
|
|
- var routes = LegacyRouteTableFactory.Create(new[] { typeof(MyComponent), typeof(MyInheritedComponent), });
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Collection(
|
|
|
- routes.Routes.OrderBy(r => r.Template.TemplateText),
|
|
|
- r => Assert.Equal("Test1", r.Template.TemplateText),
|
|
|
- r => Assert.Equal("Test2", r.Template.TemplateText));
|
|
|
- }
|
|
|
-
|
|
|
- [Route("Test2")]
|
|
|
- private class MyInheritedComponent : MyComponent
|
|
|
- {
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchRootTemplate()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/").Build();
|
|
|
- var context = new RouteContext("/");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchLiteralTemplate()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/literal").Build();
|
|
|
- var context = new RouteContext("/literal/");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchTemplateWithMultipleLiterals()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/some/awesome/route/").Build();
|
|
|
- var context = new RouteContext("/some/awesome/route");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void RouteMatchingIsCaseInsensitive()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/some/AWESOME/route/").Build();
|
|
|
- var context = new RouteContext("/Some/awesome/RouTe");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchEncodedSegments()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/some/ünicõdē/🛣/").Build();
|
|
|
- var context = new RouteContext("/some/%C3%BCnic%C3%B5d%C4%93/%F0%9F%9B%A3");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void DoesNotMatchIfSegmentsDontMatch()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/some/AWESOME/route/").Build();
|
|
|
- var context = new RouteContext("/some/brilliant/route");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Null(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData("/{value:bool}", "/maybe")]
|
|
|
- [InlineData("/{value:datetime}", "/1955-01-32")]
|
|
|
- [InlineData("/{value:decimal}", "/hello")]
|
|
|
- [InlineData("/{value:double}", "/0.1.2")]
|
|
|
- [InlineData("/{value:float}", "/0.1.2")]
|
|
|
- [InlineData("/{value:guid}", "/not-a-guid")]
|
|
|
- [InlineData("/{value:int}", "/3.141")]
|
|
|
- [InlineData("/{value:long}", "/3.141")]
|
|
|
- public void DoesNotMatchIfConstraintDoesNotMatch(string template, string contextUrl)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Null(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData("/some")]
|
|
|
- [InlineData("/some/awesome/route/with/extra/segments")]
|
|
|
- public void DoesNotMatchIfDifferentNumberOfSegments(string path)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/some/awesome/route/").Build();
|
|
|
- var context = new RouteContext(path);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Null(context.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData("/value1", "value1")]
|
|
|
- [InlineData("/value2/", "value2")]
|
|
|
- [InlineData("/d%C3%A9j%C3%A0%20vu", "déjà vu")]
|
|
|
- [InlineData("/d%C3%A9j%C3%A0%20vu/", "déjà vu")]
|
|
|
- [InlineData("/d%C3%A9j%C3%A0+vu", "déjà+vu")]
|
|
|
- public void CanMatchParameterTemplate(string path, string expectedValue)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/{parameter}").Build();
|
|
|
- var context = new RouteContext(path);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Single(context.Parameters, p => p.Key == "parameter" && (string)p.Value == expectedValue);
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData("/blog/value1", "value1")]
|
|
|
- [InlineData("/blog/value1/foo%20bar", "value1/foo bar")]
|
|
|
- public void CanMatchCatchAllParameterTemplate(string path, string expectedValue)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/blog/{*parameter}").Build();
|
|
|
- var context = new RouteContext(path);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Single(context.Parameters, p => p.Key == "parameter" && (string)p.Value == expectedValue);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchTemplateWithMultipleParameters()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/{some}/awesome/{route}/").Build();
|
|
|
- var context = new RouteContext("/an/awesome/path");
|
|
|
-
|
|
|
- var expectedParameters = new Dictionary<string, object>
|
|
|
- {
|
|
|
- ["some"] = "an",
|
|
|
- ["route"] = "path"
|
|
|
- };
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Equal(expectedParameters, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchTemplateWithMultipleParametersAndCatchAllParameter()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute("/{some}/awesome/{route}/with/{*catchAll}").Build();
|
|
|
- var context = new RouteContext("/an/awesome/path/with/some/catch/all/stuff");
|
|
|
-
|
|
|
- var expectedParameters = new Dictionary<string, object>
|
|
|
- {
|
|
|
- ["some"] = "an",
|
|
|
- ["route"] = "path",
|
|
|
- ["catchAll"] = "some/catch/all/stuff"
|
|
|
- };
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Equal(expectedParameters, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- public static IEnumerable<object[]> CanMatchParameterWithConstraintCases() => new object[][]
|
|
|
- {
|
|
|
- new object[] { "/{value:bool}", "/true", true },
|
|
|
- new object[] { "/{value:bool}", "/false", false },
|
|
|
- new object[] { "/{value:datetime}", "/1955-01-30", new DateTime(1955, 1, 30) },
|
|
|
- new object[] { "/{value:decimal}", "/5.3", 5.3m },
|
|
|
- new object[] { "/{value:double}", "/0.1", 0.1d },
|
|
|
- new object[] { "/{value:float}", "/0.1", 0.1f },
|
|
|
- new object[] { "/{value:guid}", "/1FCEF085-884F-416E-B0A1-71B15F3E206B", Guid.Parse("1FCEF085-884F-416E-B0A1-71B15F3E206B") },
|
|
|
- new object[] { "/{value:int}", "/123", 123 },
|
|
|
- new object[] { "/{value:int}", "/-123", -123},
|
|
|
- new object[] { "/{value:long}", "/9223372036854775807", long.MaxValue },
|
|
|
- new object[] { "/{value:long}", $"/-9223372036854775808", long.MinValue },
|
|
|
- };
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(CanMatchParameterWithConstraintCases))]
|
|
|
- public void CanMatchParameterWithConstraint(string template, string contextUrl, object convertedValue)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- if (context.Handler == null)
|
|
|
- {
|
|
|
- // Make it easier to track down failing tests when using MemberData
|
|
|
- throw new InvalidOperationException($"Failed to match template '{template}'.");
|
|
|
- }
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "value", convertedValue }
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchOptionalParameterWithoutConstraints()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var template = "/optional/{value?}";
|
|
|
- var contextUrl = "/optional/";
|
|
|
- string convertedValue = null;
|
|
|
-
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- if (context.Handler == null)
|
|
|
- {
|
|
|
- // Make it easier to track down failing tests when using MemberData
|
|
|
- throw new InvalidOperationException($"Failed to match template '{template}'.");
|
|
|
- }
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "value", convertedValue }
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- public static IEnumerable<object[]> CanMatchOptionalParameterWithConstraintCases() => new object[][]
|
|
|
-{
|
|
|
- new object[] { "/optional/{value:bool?}", "/optional/", null },
|
|
|
- new object[] { "/optional/{value:datetime?}", "/optional/", null },
|
|
|
- new object[] { "/optional/{value:decimal?}", "/optional/", null },
|
|
|
-};
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(CanMatchOptionalParameterWithConstraintCases))]
|
|
|
- public void CanMatchOptionalParameterWithConstraint(string template, string contextUrl, object convertedValue)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- if (context.Handler == null)
|
|
|
- {
|
|
|
- // Make it easier to track down failing tests when using MemberData
|
|
|
- throw new InvalidOperationException($"Failed to match template '{template}'.");
|
|
|
- }
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "value", convertedValue }
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void CanMatchMultipleOptionalParameterWithConstraint()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var template = "/optional/{value:datetime?}/{value2:datetime?}";
|
|
|
- var contextUrl = "/optional//";
|
|
|
- object convertedValue = null;
|
|
|
-
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- if (context.Handler == null)
|
|
|
- {
|
|
|
- // Make it easier to track down failing tests when using MemberData
|
|
|
- throw new InvalidOperationException($"Failed to match template '{template}'.");
|
|
|
- }
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "value", convertedValue },
|
|
|
- { "value2", convertedValue }
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- public static IEnumerable<object[]> CanMatchSegmentWithMultipleConstraintsCases() => new object[][]
|
|
|
-{
|
|
|
- new object[] { "/{value:double:int}/", "/15", 15 },
|
|
|
- new object[] { "/{value:double?:int?}/", "/", null },
|
|
|
-};
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(CanMatchSegmentWithMultipleConstraintsCases))]
|
|
|
- public void CanMatchSegmentWithMultipleConstraints(string template, string contextUrl, object convertedValue)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder().AddRoute(template).Build();
|
|
|
- var context = new RouteContext(contextUrl);
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "value", convertedValue }
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersLiteralTemplateOverTemplateWithParameters()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/an/awesome/path", typeof(TestHandler1))
|
|
|
- .AddRoute("/{some}/awesome/{route}/", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var context = new RouteContext("/an/awesome/path");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Null(context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersLiteralTemplateOverTemplateWithOptionalParameters()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/users/1", typeof(TestHandler1))
|
|
|
- .AddRoute("/users/{id?}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var context = new RouteContext("/users/1");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Null(context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersOptionalParamsOverNonOptionalParams()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/users/{id}", typeof(TestHandler1))
|
|
|
- .AddRoute("/users/{id?}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var contextWithParam = new RouteContext("/users/1");
|
|
|
- var contextWithoutParam = new RouteContext("/users/");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(contextWithParam);
|
|
|
- routeTable.Route(contextWithoutParam);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(contextWithParam.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler1), contextWithParam.Handler);
|
|
|
-
|
|
|
- Assert.NotNull(contextWithoutParam.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler2), contextWithoutParam.Handler);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersOptionalParamsOverNonOptionalParamsReverseOrder()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/users/{id}", typeof(TestHandler1))
|
|
|
- .AddRoute("/users/{id?}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var contextWithParam = new RouteContext("/users/1");
|
|
|
- var contextWithoutParam = new RouteContext("/users/");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(contextWithParam);
|
|
|
- routeTable.Route(contextWithoutParam);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(contextWithParam.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler1), contextWithParam.Handler);
|
|
|
-
|
|
|
- Assert.NotNull(contextWithoutParam.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler2), contextWithoutParam.Handler);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersLiteralTemplateOverParameterizedTemplates()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/users/1/friends", typeof(TestHandler1))
|
|
|
- .AddRoute("/users/{id}/{location}", typeof(TestHandler2))
|
|
|
- .AddRoute("/users/1/{location}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var context = new RouteContext("/users/1/friends");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler1), context.Handler);
|
|
|
- Assert.Null(context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersShorterRoutesOverLongerRoutes()
|
|
|
- {
|
|
|
- // Arrange & Act
|
|
|
- var handler = typeof(int);
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/an/awesome/path")
|
|
|
- .AddRoute("/an/awesome/", handler).Build();
|
|
|
-
|
|
|
- // Act
|
|
|
- Assert.Equal("an/awesome", routeTable.Routes[0].Template.TemplateText);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersMoreConstraintsOverFewer()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/products/{id}")
|
|
|
- .AddRoute("/products/{id:int}").Build();
|
|
|
- var context = new RouteContext("/products/456");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Equal(context.Parameters, new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "id", 456 }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void PrefersRoutesThatMatchMoreSegments()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/{anythingGoes}", typeof(TestHandler1))
|
|
|
- .AddRoute("/users/{id?}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var context = new RouteContext("/users/1");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.NotNull(context.Handler);
|
|
|
- Assert.Equal(typeof(TestHandler2), context.Handler);
|
|
|
- Assert.NotNull(context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void ProducesAStableOrderForNonAmbiguousRoutes()
|
|
|
- {
|
|
|
- // Arrange & Act
|
|
|
- var handler = typeof(int);
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/an/awesome/", handler)
|
|
|
- .AddRoute("/a/brilliant/").Build();
|
|
|
-
|
|
|
- // Act
|
|
|
- Assert.Equal("a/brilliant", routeTable.Routes[0].Template.TemplateText);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void DoesNotThrowIfStableSortComparesRouteWithItself()
|
|
|
- {
|
|
|
- // Test for https://github.com/dotnet/aspnetcore/issues/13313
|
|
|
- // Arrange & Act
|
|
|
- var builder = new TestRouteTableBuilder();
|
|
|
- builder.AddRoute("r16");
|
|
|
- builder.AddRoute("r05");
|
|
|
- builder.AddRoute("r09");
|
|
|
- builder.AddRoute("r00");
|
|
|
- builder.AddRoute("r13");
|
|
|
- builder.AddRoute("r02");
|
|
|
- builder.AddRoute("r03");
|
|
|
- builder.AddRoute("r10");
|
|
|
- builder.AddRoute("r15");
|
|
|
- builder.AddRoute("r14");
|
|
|
- builder.AddRoute("r12");
|
|
|
- builder.AddRoute("r07");
|
|
|
- builder.AddRoute("r11");
|
|
|
- builder.AddRoute("r08");
|
|
|
- builder.AddRoute("r06");
|
|
|
- builder.AddRoute("r04");
|
|
|
- builder.AddRoute("r01");
|
|
|
-
|
|
|
- var routeTable = builder.Build();
|
|
|
-
|
|
|
- // Act
|
|
|
- Assert.Equal(17, routeTable.Routes.Length);
|
|
|
- for (var i = 0; i < 17; i++)
|
|
|
- {
|
|
|
- var templateText = "r" + i.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0');
|
|
|
- Assert.Equal(templateText, routeTable.Routes[i].Template.TemplateText);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData("/literal", "/Literal/")]
|
|
|
- [InlineData("/{parameter}", "/{parameter}/")]
|
|
|
- [InlineData("/literal/{parameter}", "/Literal/{something}")]
|
|
|
- [InlineData("/{parameter}/literal/{something}", "{param}/Literal/{else}")]
|
|
|
- public void DetectsAmbiguousRoutes(string left, string right)
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var expectedMessage = $@"The following routes are ambiguous:
|
|
|
-'{left.Trim('/')}' in '{typeof(object).FullName}'
|
|
|
-'{right.Trim('/')}' in '{typeof(object).FullName}'
|
|
|
-";
|
|
|
- // Act
|
|
|
- var exception = Assert.Throws<InvalidOperationException>(() => new TestRouteTableBuilder()
|
|
|
- .AddRoute(left)
|
|
|
- .AddRoute(right).Build());
|
|
|
-
|
|
|
- Assert.Equal(expectedMessage, exception.Message);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void SuppliesNullForUnusedHandlerParameters()
|
|
|
- {
|
|
|
- // Arrange
|
|
|
- var routeTable = new TestRouteTableBuilder()
|
|
|
- .AddRoute("/", typeof(TestHandler1))
|
|
|
- .AddRoute("/products/{param1:int}", typeof(TestHandler1))
|
|
|
- .AddRoute("/products/{param2}/{PaRam1}", typeof(TestHandler1))
|
|
|
- .AddRoute("/{unrelated}", typeof(TestHandler2))
|
|
|
- .Build();
|
|
|
- var context = new RouteContext("/products/456");
|
|
|
-
|
|
|
- // Act
|
|
|
- routeTable.Route(context);
|
|
|
-
|
|
|
- // Assert
|
|
|
- Assert.Collection(routeTable.Routes,
|
|
|
- route =>
|
|
|
- {
|
|
|
- Assert.Same(typeof(TestHandler1), route.Handler);
|
|
|
- Assert.Equal("/", route.Template.TemplateText);
|
|
|
- Assert.Equal(new[] { "param1", "param2" }, route.UnusedRouteParameterNames);
|
|
|
- },
|
|
|
- route =>
|
|
|
- {
|
|
|
- Assert.Same(typeof(TestHandler2), route.Handler);
|
|
|
- Assert.Equal("{unrelated}", route.Template.TemplateText);
|
|
|
- Assert.Equal(Array.Empty<string>(), route.UnusedRouteParameterNames);
|
|
|
- },
|
|
|
- route =>
|
|
|
- {
|
|
|
- Assert.Same(typeof(TestHandler1), route.Handler);
|
|
|
- Assert.Equal("products/{param1:int}", route.Template.TemplateText);
|
|
|
- Assert.Equal(new[] { "param2" }, route.UnusedRouteParameterNames);
|
|
|
- },
|
|
|
- route =>
|
|
|
- {
|
|
|
- Assert.Same(typeof(TestHandler1), route.Handler);
|
|
|
- Assert.Equal("products/{param2}/{PaRam1}", route.Template.TemplateText);
|
|
|
- Assert.Equal(Array.Empty<string>(), route.UnusedRouteParameterNames);
|
|
|
- });
|
|
|
- Assert.Same(typeof(TestHandler1), context.Handler);
|
|
|
- Assert.Equal(new Dictionary<string, object>
|
|
|
- {
|
|
|
- { "param1", 456 },
|
|
|
- { "param2", null },
|
|
|
- }, context.Parameters);
|
|
|
- }
|
|
|
-
|
|
|
- private class TestRouteTableBuilder
|
|
|
- {
|
|
|
- IList<(string Template, Type Handler)> _routeTemplates = new List<(string, Type)>();
|
|
|
- Type _handler = typeof(object);
|
|
|
-
|
|
|
- public TestRouteTableBuilder AddRoute(string template, Type handler = null)
|
|
|
- {
|
|
|
- _routeTemplates.Add((template, handler ?? _handler));
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public LegacyRouteTable Build()
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- var templatesByHandler = _routeTemplates
|
|
|
- .GroupBy(rt => rt.Handler)
|
|
|
- .ToDictionary(group => group.Key, group => group.Select(g => g.Template).ToArray());
|
|
|
- return LegacyRouteTableFactory.Create(templatesByHandler);
|
|
|
- }
|
|
|
- catch (InvalidOperationException ex) when (ex.InnerException is InvalidOperationException)
|
|
|
- {
|
|
|
- // ToArray() will wrap our exception in its own.
|
|
|
- throw ex.InnerException;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- class TestHandler1 { }
|
|
|
- class TestHandler2 { }
|
|
|
- }
|
|
|
-}
|