| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- using System.Globalization;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Routing.Template;
- using Microsoft.AspNetCore.Routing.TestObjects;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
- using Microsoft.Extensions.ObjectPool;
- using Microsoft.Extensions.Options;
- using Moq;
- namespace Microsoft.AspNetCore.Routing.Tree;
- public class TreeRouterTest
- {
- private static readonly RequestDelegate NullHandler = (c) => Task.CompletedTask;
- [Theory]
- [InlineData("template/5", "template/{parameter:int}")]
- [InlineData("template/5", "template/{parameter}")]
- [InlineData("template/5", "template/{*parameter:int}")]
- [InlineData("template/5", "template/{*parameter}")]
- [InlineData("template/{parameter}", "template/{parameter:alpha}")] // constraint doesn't match
- [InlineData("template/{parameter:int}", "template/{parameter}")]
- [InlineData("template/{parameter:int}", "template/{*parameter:int}")]
- [InlineData("template/{parameter:int}", "template/{*parameter}")]
- [InlineData("template/{parameter}", "template/{*parameter:int}")]
- [InlineData("template/{parameter}", "template/{*parameter}")]
- [InlineData("template/{*parameter:int}", "template/{*parameter}")]
- public async Task TreeRouter_RouteAsync_RespectsPrecedence(
- string firstTemplate,
- string secondTemplate)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, firstTemplate);
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- MapInboundEntry(builder, secondTemplate);
- MapInboundEntry(builder, firstTemplate);
- var route = builder.Build();
- var context = CreateRouteContext("/template/5");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- [Theory]
- [InlineData("/", "")]
- [InlineData("/Literal1", "Literal1")]
- [InlineData("/Literal1/Literal2", "Literal1/Literal2")]
- [InlineData("/Literal1/Literal2/Literal3", "Literal1/Literal2/Literal3")]
- [InlineData("/Literal1/Literal2/Literal3/4", "Literal1/Literal2/Literal3/{*constrainedCatchAll:int}")]
- [InlineData("/Literal1/Literal2/Literal3/Literal4", "Literal1/Literal2/Literal3/{*catchAll}")]
- [InlineData("/1", "{constrained1:int}")]
- [InlineData("/1/2", "{constrained1:int}/{constrained2:int}")]
- [InlineData("/1/2/3", "{constrained1:int}/{constrained2:int}/{constrained3:int}")]
- [InlineData("/1/2/3/4", "{constrained1:int}/{constrained2:int}/{constrained3:int}/{*constrainedCatchAll:int}")]
- [InlineData("/1/2/3/CatchAll4", "{constrained1:int}/{constrained2:int}/{constrained3:int}/{*catchAll}")]
- [InlineData("/parameter1", "{parameter1}")]
- [InlineData("/parameter1/parameter2", "{parameter1}/{parameter2}")]
- [InlineData("/parameter1/parameter2/parameter3", "{parameter1}/{parameter2}/{parameter3}")]
- [InlineData("/parameter1/parameter2/parameter3/4", "{parameter1}/{parameter2}/{parameter3}/{*constrainedCatchAll:int}")]
- [InlineData("/parameter1/parameter2/parameter3/CatchAll4", "{parameter1}/{parameter2}/{parameter3}/{*catchAll}")]
- public async Task TreeRouter_RouteAsync_MatchesRouteWithTheRightLength(string url, string expected)
- {
- // Arrange
- var routes = new[] {
- "",
- "Literal1",
- "Literal1/Literal2",
- "Literal1/Literal2/Literal3",
- "Literal1/Literal2/Literal3/{*constrainedCatchAll:int}",
- "Literal1/Literal2/Literal3/{*catchAll}",
- "{constrained1:int}",
- "{constrained1:int}/{constrained2:int}",
- "{constrained1:int}/{constrained2:int}/{constrained3:int}",
- "{constrained1:int}/{constrained2:int}/{constrained3:int}/{*constrainedCatchAll:int}",
- "{constrained1:int}/{constrained2:int}/{constrained3:int}/{*catchAll}",
- "{parameter1}",
- "{parameter1}/{parameter2}",
- "{parameter1}/{parameter2}/{parameter3}",
- "{parameter1}/{parameter2}/{parameter3}/{*constrainedCatchAll:int}",
- "{parameter1}/{parameter2}/{parameter3}/{*catchAll}",
- };
- var expectedRouteGroup = CreateRouteGroup(0, expected);
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- foreach (var template in routes.Reverse())
- {
- MapInboundEntry(builder, template);
- }
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- public static TheoryData<string, object[]> MatchesRoutesWithDefaultsData =>
- new TheoryData<string, object[]>
- {
- { "/", new object[] { "1", "2", "3", "4" } },
- { "/a", new object[] { "a", "2", "3", "4" } },
- { "/a/b", new object[] { "a", "b", "3", "4" } },
- { "/a/b/c", new object[] { "a", "b", "c", "4" } },
- { "/a/b/c/d", new object[] { "a", "b", "c", "d" } }
- };
- [Theory]
- [MemberData(nameof(MatchesRoutesWithDefaultsData))]
- public async Task TreeRouter_RouteAsync_MatchesRoutesWithDefaults(string url, object[] routeValues)
- {
- // Arrange
- var routes = new[] {
- "{parameter1=1}/{parameter2=2}/{parameter3=3}/{parameter4=4}",
- };
- var expectedRouteGroup = CreateRouteGroup(0, "{parameter1=1}/{parameter2=2}/{parameter3=3}/{parameter4=4}");
- var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
- var expectedRouteValues = new RouteValueDictionary();
- for (var i = 0; i < routeValueKeys.Length; i++)
- {
- expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
- }
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- foreach (var template in routes.Reverse())
- {
- MapInboundEntry(builder, template);
- }
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- foreach (var entry in expectedRouteValues)
- {
- var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
- Assert.Equal(entry.Value, data.Value);
- }
- }
- public static TheoryData<string, object[]> MatchesConstrainedRoutesWithDefaultsData =>
- new TheoryData<string, object[]>
- {
- { "/", new object[] { "1", "2", "3", "4" } },
- { "/10", new object[] { "10", "2", "3", "4" } },
- { "/10/11", new object[] { "10", "11", "3", "4" } },
- { "/10/11/12", new object[] { "10", "11", "12", "4" } },
- { "/10/11/12/13", new object[] { "10", "11", "12", "13" } }
- };
- [Theory]
- [MemberData(nameof(MatchesConstrainedRoutesWithDefaultsData))]
- public async Task TreeRouter_RouteAsync_MatchesConstrainedRoutesWithDefaults(string url, object[] routeValues)
- {
- // Arrange
- var routes = new[] {
- "{parameter1:int=1}/{parameter2:int=2}/{parameter3:int=3}/{parameter4:int=4}",
- };
- var expectedRouteGroup = CreateRouteGroup(0, "{parameter1:int=1}/{parameter2:int=2}/{parameter3:int=3}/{parameter4:int=4}");
- var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
- var expectedRouteValues = new RouteValueDictionary();
- for (var i = 0; i < routeValueKeys.Length; i++)
- {
- expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
- }
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- foreach (var template in routes.Reverse())
- {
- MapInboundEntry(builder, template);
- }
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- foreach (var entry in expectedRouteValues)
- {
- var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
- Assert.Equal(entry.Value, data.Value);
- }
- }
- [Fact]
- public async Task TreeRouter_RouteAsync_MatchesCatchAllRoutesWithDefaults()
- {
- // Arrange
- var routes = new[] {
- "{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}",
- };
- var url = "/a/b/c";
- var routeValues = new[] { "a", "b", "c", "4" };
- var expectedRouteGroup = CreateRouteGroup(0, "{parameter1=1}/{parameter2=2}/{parameter3=3}/{*parameter4=4}");
- var routeValueKeys = new[] { "parameter1", "parameter2", "parameter3", "parameter4" };
- var expectedRouteValues = new RouteValueDictionary();
- for (var i = 0; i < routeValueKeys.Length; i++)
- {
- expectedRouteValues.Add(routeValueKeys[i], routeValues[i]);
- }
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- foreach (var template in routes.Reverse())
- {
- MapInboundEntry(builder, template);
- }
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- foreach (var entry in expectedRouteValues)
- {
- var data = Assert.Single(context.RouteData.Values, v => v.Key == entry.Key);
- Assert.Equal(entry.Value, data.Value);
- }
- }
- [Fact]
- public async Task TreeRouter_RouteAsync_DoesNotMatchRoutesWithIntermediateDefaultRouteValues()
- {
- // Arrange
- var url = "/a/b";
- var builder = CreateBuilder();
- MapInboundEntry(builder, "a/b/{parameter3=3}/d");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a")]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b")]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c")]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d")]
- public async Task TreeRouter_RouteAsync_DoesNotMatchRoutesWithMultipleIntermediateDefaultOrOptionalRouteValues(string template, string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d/e")]
- [InlineData("a/{b=3}/c/{d?}/e/{*f}", "/a/b/c/d/e/f")]
- public async Task RouteAsync_MatchRoutesWithMultipleIntermediateDefaultOrOptionalRouteValues_WhenAllIntermediateValuesAreProvided(string template, string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- }
- [Fact]
- public async Task TreeRouter_RouteAsync_DoesNotMatchShorterUrl()
- {
- // Arrange
- var routes = new[] {
- "Literal1/Literal2/Literal3",
- };
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to route the request, the route with a higher precedence gets tried first.
- foreach (var template in routes.Reverse())
- {
- MapInboundEntry(builder, template);
- }
- var route = builder.Build();
- var context = CreateRouteContext("/Literal1");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("template/5", "template/{parameter:int}")]
- [InlineData("template/5", "template/{parameter}")]
- [InlineData("template/5", "template/{*parameter:int}")]
- [InlineData("template/5", "template/{*parameter}")]
- [InlineData("template/{parameter:int}", "template/{parameter}")]
- [InlineData("template/{parameter:int}", "template/{*parameter:int}")]
- [InlineData("template/{parameter:int}", "template/{*parameter}")]
- [InlineData("template/{parameter}", "template/{*parameter:int}")]
- [InlineData("template/{parameter}", "template/{*parameter}")]
- [InlineData("template/{*parameter:int}", "template/{*parameter}")]
- public async Task TreeRouter_RouteAsync_RespectsOrderOverPrecedence(
- string firstTemplate,
- string secondTemplate)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, secondTemplate);
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative order and higher relative precedence
- // first to ensure that when we try to route the request, the route with the higher
- // relative order gets tried first.
- MapInboundEntry(builder, firstTemplate, order: 1);
- MapInboundEntry(builder, secondTemplate, order: 0);
- var route = builder.Build();
- var context = CreateRouteContext("/template/5");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- [Theory]
- [InlineData("///")]
- [InlineData("/a//")]
- [InlineData("/a/b//")]
- [InlineData("//b//")]
- [InlineData("///c")]
- [InlineData("///c/")]
- public async Task TryMatch_MultipleOptionalParameters_WithEmptyIntermediateSegmentsDoesNotMatch(string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "{controller?}/{action?}/{id?}");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("")]
- [InlineData("/")]
- [InlineData("/a")]
- [InlineData("/a/")]
- [InlineData("/a/b")]
- [InlineData("/a/b/")]
- [InlineData("/a/b/c")]
- [InlineData("/a/b/c/")]
- public async Task TryMatch_MultipleOptionalParameters_WithIncrementalOptionalValues(string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "{controller?}/{action?}/{id?}");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- }
- [Theory]
- [InlineData("///")]
- [InlineData("////")]
- [InlineData("/a//")]
- [InlineData("/a///")]
- [InlineData("//b/")]
- [InlineData("//b//")]
- [InlineData("///c")]
- [InlineData("///c/")]
- public async Task TryMatch_MultipleParameters_WithEmptyValues(string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "{controller}/{action}/{id}");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("/a/b/c//")]
- [InlineData("/a/b/c/////")]
- public async Task TryMatch_CatchAllParameters_WithEmptyValuesAtTheEnd(string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "{controller}/{action}/{*id}");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- }
- [Theory]
- [InlineData("/a/b//")]
- [InlineData("/a/b///c")]
- public async Task TryMatch_CatchAllParameters_WithEmptyValues(string url)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "{controller}/{action}/{*id}");
- var route = builder.Build();
- var context = CreateRouteContext(url);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("{*path}", "/a", "a")]
- [InlineData("{*path}", "/a/b/c", "a/b/c")]
- [InlineData("a/{*path}", "/a/b", "b")]
- [InlineData("a/{*path}", "/a/b/c/d", "b/c/d")]
- [InlineData("a/{*path:regex(10/20/30)}", "/a/10/20/30", "10/20/30")]
- public async Task TreeRouter_RouteAsync_MatchesWildCard_ForLargerPathSegments(
- string template,
- string requestPath,
- string expectedResult)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(requestPath);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- Assert.Equal(expectedResult, context.RouteData.Values["path"]);
- }
- [Theory]
- [InlineData("a/{*path}", "/a")]
- [InlineData("a/{*path}", "/a/")]
- public async Task TreeRouter_RouteAsync_MatchesCatchAll_NullValue(
- string template,
- string requestPath)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(requestPath);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- Assert.Null(context.RouteData.Values["path"]);
- }
- [Theory]
- [InlineData("a/{*path}", "/a")]
- [InlineData("a/{*path}", "/a/")]
- public async Task TreeRouter_RouteAsync_MatchesCatchAll_NullValue_DoesNotReplaceExistingValue(
- string template,
- string requestPath)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(requestPath);
- context.RouteData.Values["path"] = "existing-value";
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- Assert.Equal("existing-value", context.RouteData.Values["path"]);
- }
- [Theory]
- [InlineData("a/{*path=default}", "/a")]
- [InlineData("a/{*path=default}", "/a/")]
- public async Task TreeRouter_RouteAsync_MatchesCatchAll_UsesDefaultValue(
- string template,
- string requestPath)
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(requestPath);
- context.RouteData.Values["path"] = "existing-value";
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- Assert.Equal("default", context.RouteData.Values["path"]);
- }
- [Theory]
- [InlineData("template/5")]
- [InlineData("template/{parameter:int}")]
- [InlineData("template/{parameter}")]
- [InlineData("template/{*parameter:int}")]
- [InlineData("template/{*parameter}")]
- public async Task TreeRouter_RouteAsync_RespectsOrder(string template)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, template);
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative order first to ensure that when
- // we try to route the request, the route with the higher relative order gets tried first.
- MapInboundEntry(builder, template, order: 1);
- MapInboundEntry(builder, template, order: 0);
- var route = builder.Build();
- var context = CreateRouteContext("/template/5");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- [Theory]
- [InlineData("template/{first:int}", "template/{second:int}")]
- [InlineData("template/{first}", "template/{second}")]
- [InlineData("template/{*first:int}", "template/{*second:int}")]
- [InlineData("template/{*first}", "template/{*second}")]
- public async Task TreeRouter_RouteAsync_EnsuresStableOrdering(string first, string second)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, first);
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative template order first to ensure that when
- // we try to route the request, the route with the higher template order gets tried first.
- MapInboundEntry(builder, first);
- MapInboundEntry(builder, second);
- var route = builder.Build();
- var context = CreateRouteContext("/template/5");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- [Theory]
- [InlineData("template/{parameter:int}", "/template/5", true)]
- [InlineData("template/{parameter:int?}", "/template/5", true)]
- [InlineData("template/{parameter:int?}", "/template", true)]
- [InlineData("template/{parameter:int?}", "/template/qwer", false)]
- public async Task TreeRouter_WithOptionalInlineConstraint(
- string template,
- string request,
- bool expectedResult)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, template);
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(request);
- // Act
- await route.RouteAsync(context);
- // Assert
- if (expectedResult)
- {
- Assert.NotNull(context.Handler);
- Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
- }
- else
- {
- Assert.Null(context.Handler);
- }
- }
- [Theory]
- [InlineData("moo/{p1}.{p2?}", "/moo/foo.bar", "foo", "bar", null)]
- [InlineData("moo/{p1?}", "/moo/foo", "foo", null, null)]
- [InlineData("moo/{p1?}", "/moo", null, null, null)]
- [InlineData("moo/{p1}.{p2?}", "/moo/foo", "foo", null, null)]
- [InlineData("moo/{p1}.{p2?}", "/moo/foo..bar", "foo.", "bar", null)]
- [InlineData("moo/{p1}.{p2?}", "/moo/foo.moo.bar", "foo.moo", "bar", null)]
- [InlineData("moo/{p1}.{p2}", "/moo/foo.bar", "foo", "bar", null)]
- [InlineData("moo/foo.{p1}.{p2?}", "/moo/foo.moo.bar", "moo", "bar", null)]
- [InlineData("moo/foo.{p1}.{p2?}", "/moo/foo.moo", "moo", null, null)]
- [InlineData("moo/.{p2?}", "/moo/.foo", null, "foo", null)]
- [InlineData("moo/{p1}.{p2?}", "/moo/....", "..", ".", null)]
- [InlineData("moo/{p1}.{p2?}", "/moo/.bar", ".bar", null, null)]
- [InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo.bar", "foo", "moo", "bar")]
- [InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo", "foo", "moo", null)]
- [InlineData("moo/{p1}.{p2}.{p3}.{p4?}", "/moo/foo.moo.bar", "foo", "moo", "bar")]
- [InlineData("{p1}.{p2?}/{p3}", "/foo.moo/bar", "foo", "moo", "bar")]
- [InlineData("{p1}.{p2?}/{p3}", "/foo/bar", "foo", null, "bar")]
- [InlineData("{p1}.{p2?}/{p3}", "/.foo/bar", ".foo", null, "bar")]
- public async Task TreeRouter_WithOptionalCompositeParameter_Valid(
- string template,
- string request,
- string p1,
- string p2,
- string p3)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, template);
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(request);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(context.Handler);
- if (p1 != null)
- {
- Assert.Equal(p1, context.RouteData.Values["p1"]);
- }
- if (p2 != null)
- {
- Assert.Equal(p2, context.RouteData.Values["p2"]);
- }
- if (p3 != null)
- {
- Assert.Equal(p3, context.RouteData.Values["p3"]);
- }
- }
- [Theory]
- [InlineData("moo/{p1}.{p2?}", "/moo/foo.")]
- [InlineData("moo/{p1}.{p2?}", "/moo/.")]
- [InlineData("moo/{p1}.{p2}", "/foo.")]
- [InlineData("moo/{p1}.{p2}", "/foo")]
- [InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo.moo.")]
- [InlineData("moo/foo.{p2}.{p3?}", "/moo/bar.foo.moo")]
- [InlineData("moo/foo.{p2}.{p3?}", "/moo/kungfoo.moo.bar")]
- [InlineData("moo/foo.{p2}.{p3?}", "/moo/kungfoo.moo")]
- [InlineData("moo/{p1}.{p2}.{p3?}", "/moo/foo")]
- [InlineData("{p1}.{p2?}/{p3}", "/foo./bar")]
- [InlineData("moo/.{p2?}", "/moo/.")]
- [InlineData("{p1}.{p2}/{p3}", "/.foo/bar")]
- public async Task TreeRouter_WithOptionalCompositeParameter_Invalid(
- string template,
- string request)
- {
- // Arrange
- var expectedRouteGroup = CreateRouteGroup(0, template);
- var builder = CreateBuilder();
- MapInboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateRouteContext(request);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Null(context.Handler);
- }
- [Theory]
- [InlineData("template", "{*url:alpha}", "/template?url=dingo&id=5")]
- [InlineData("{*url:alpha}", "{*url}", "/dingo?id=5")]
- [InlineData("{id}", "{*url}", "/5?url=dingo")]
- [InlineData("{id}", "{*url:alpha}", "/5?url=dingo")]
- [InlineData("{id:int}", "{id}", "/5?url=dingo")]
- [InlineData("{id}", "{id:alpha}/{url}", "/5?url=dingo")] // constraint doesn't match
- [InlineData("template/api/{*url}", "template/api", "/template/api/dingo?id=5")]
- [InlineData("template/api", "template/{*url}", "/template/api?url=dingo&id=5")]
- [InlineData("template/api", "template/api{id}location", "/template/api?url=dingo&id=5")]
- [InlineData("template/api{id}location", "template/{id:int}", "/template/api5location?url=dingo")]
- public void TreeRouter_GenerateLink(string firstTemplate, string secondTemplate, string expectedPath)
- {
- // Arrange
- var values = new Dictionary<string, object>
- {
- {"url", "dingo" },
- {"id", 5 }
- };
- var route = CreateTreeRouter(firstTemplate, secondTemplate);
- var context = CreateVirtualPathContext(
- values: values,
- ambientValues: null);
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal(expectedPath, result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_LongerTemplateWithDefaultIsMoreSpecific()
- {
- // Arrange
- var firstTemplate = "template";
- var secondTemplate = "template/{parameter:int=1003}";
- var route = CreateTreeRouter(firstTemplate, secondTemplate);
- var context = CreateVirtualPathContext(
- values: null,
- ambientValues: null);
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- // The Binder binds to /template
- Assert.Equal("/template", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("template/{parameter:int=5}", "template", "/template/5")]
- [InlineData("template/{parameter}", "template", "/template/5")]
- [InlineData("template/{parameter}/{id}", "template/{parameter}", "/template/5/1234")]
- public void TreeRouter_GenerateLink_OrderingAgnostic(
- string firstTemplate,
- string secondTemplate,
- string expectedPath)
- {
- // Arrange
- var route = CreateTreeRouter(firstTemplate, secondTemplate);
- var parameter = 5;
- var id = 1234;
- var values = new Dictionary<string, object>
- {
- { nameof(parameter) , parameter},
- { nameof(id), id }
- };
- var context = CreateVirtualPathContext(
- values: null,
- ambientValues: values);
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal(expectedPath, result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("template", "template/{parameter}", "/template/5")]
- [InlineData("template/{parameter}", "template/{parameter}/{id}", "/template/5/1234")]
- [InlineData("template", "template/{parameter:int=5}", "/template/5")]
- public void TreeRouter_GenerateLink_UseAvailableVariables(
- string firstTemplate,
- string secondTemplate,
- string expectedPath)
- {
- // Arrange
- var route = CreateTreeRouter(firstTemplate, secondTemplate);
- var parameter = 5;
- var id = 1234;
- var values = new Dictionary<string, object>
- {
- { nameof(parameter) , parameter},
- { nameof(id), id }
- };
- var context = CreateVirtualPathContext(
- values: null,
- ambientValues: values);
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal(expectedPath, result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("template/5", "template/{parameter:int}")]
- [InlineData("template/5", "template/{parameter}")]
- [InlineData("template/5", "template/{*parameter:int}")]
- [InlineData("template/5", "template/{*parameter}")]
- [InlineData("template/{parameter:int}", "template/{parameter}")]
- [InlineData("template/{parameter:int}", "template/{*parameter:int}")]
- [InlineData("template/{parameter:int}", "template/{*parameter}")]
- [InlineData("template/{parameter}", "template/{*parameter:int}")]
- [InlineData("template/{parameter}", "template/{*parameter}")]
- [InlineData("template/{*parameter:int}", "template/{*parameter}")]
- public void TreeRouter_GenerateLink_RespectsPrecedence(string firstTemplate, string secondTemplate)
- {
- // Arrange
- var builder = CreateBuilder();
- // We setup the route entries in reverse order of precedence to ensure that when we
- // try to generate a link, the route with a higher precedence gets tried first.
- MapOutboundEntry(builder, secondTemplate);
- MapOutboundEntry(builder, firstTemplate);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: new { parameter = 5 });
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/template/5", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("template/{parameter:int}", "/template/5", 5)]
- [InlineData("template/{parameter:int?}", "/template/5", 5)]
- [InlineData("template/{parameter:int?}", "/template", null)]
- [InlineData("template/{parameter:int?}", null, "asdf")]
- [InlineData("template/{parameter:alpha?}", "/template/asdf", "asdf")]
- [InlineData("template/{parameter:alpha?}", "/template", null)]
- [InlineData("template/{parameter:int:range(1,20)?}", "/template", null)]
- [InlineData("template/{parameter:int:range(1,20)?}", "/template/5", 5)]
- [InlineData("template/{parameter:int:range(1,20)?}", null, 21)]
- public void TreeRouter_GenerateLink_OptionalInlineParameter(
- string template,
- string expectedPath,
- object parameter)
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, template);
- var route = builder.Build();
- VirtualPathContext context;
- if (parameter != null)
- {
- context = CreateVirtualPathContext(values: null, ambientValues: new { parameter = parameter });
- }
- else
- {
- context = CreateVirtualPathContext(values: null, ambientValues: null);
- }
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- if (expectedPath == null)
- {
- Assert.Null(result);
- }
- else
- {
- Assert.NotNull(result);
- Assert.Equal(expectedPath, result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- }
- [Theory]
- [InlineData("template/5", "template/{parameter:int}")]
- [InlineData("template/5", "template/{parameter}")]
- [InlineData("template/5", "template/{*parameter:int}")]
- [InlineData("template/5", "template/{*parameter}")]
- [InlineData("template/{parameter:int}", "template/{parameter}")]
- [InlineData("template/{parameter:int}", "template/{*parameter:int}")]
- [InlineData("template/{parameter:int}", "template/{*parameter}")]
- [InlineData("template/{parameter}", "template/{*parameter:int}")]
- [InlineData("template/{parameter}", "template/{*parameter}")]
- [InlineData("template/{*parameter:int}", "template/{*parameter}")]
- public void TreeRouter_GenerateLink_RespectsOrderOverPrecedence(string firstTemplate, string secondTemplate)
- {
- // Arrange
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative order and higher relative precedence
- // first to ensure that when we try to generate a link, the route with the higher
- // relative order gets tried first.
- MapOutboundEntry(builder, firstTemplate, order: 1);
- MapOutboundEntry(builder, secondTemplate, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(null, ambientValues: new { parameter = 5 });
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/template/5", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("template/5", "template/5")]
- [InlineData("template/{first:int}", "template/{second:int}")]
- [InlineData("template/{first}", "template/{second}")]
- [InlineData("template/{*first:int}", "template/{*second:int}")]
- [InlineData("template/{*first}", "template/{*second}")]
- public void TreeRouter_GenerateLink_RespectsOrder(string firstTemplate, string secondTemplate)
- {
- // Arrange
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative order first to ensure that when
- // we try to generate a link, the route with the higher relative order gets tried first.
- MapOutboundEntry(builder, firstTemplate, requiredValues: null, order: 1);
- MapOutboundEntry(builder, secondTemplate, requiredValues: null, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: new { first = 5, second = 5 });
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/template/5", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Theory]
- [InlineData("first/5", "second/5")]
- [InlineData("first/{first:int}", "second/{second:int}")]
- [InlineData("first/{first}", "second/{second}")]
- [InlineData("first/{*first:int}", "second/{*second:int}")]
- [InlineData("first/{*first}", "second/{*second}")]
- public void TreeRouter_GenerateLink_EnsuresStableOrder(string firstTemplate, string secondTemplate)
- {
- // Arrange
- var builder = CreateBuilder();
- // We setup the route entries with a lower relative template order first to ensure that when
- // we try to generate a link, the route with the higher template order gets tried first.
- MapOutboundEntry(builder, secondTemplate, requiredValues: null, order: 0);
- MapOutboundEntry(builder, firstTemplate, requiredValues: null, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: new { first = 5, second = 5 });
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/first/5", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_CreatesLinksForRoutesWithIntermediateDefaultRouteValues()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, template: "a/b/{parameter3=3}/d", requiredValues: null, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: null);
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/a/b/3/d", result.VirtualPath);
- }
- [Fact]
- public void TreeRouter_GeneratesLink_ForMultipleNamedEntriesWithTheSameTemplate()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "Template", name: "NamedEntry", order: 1);
- MapOutboundEntry(builder, "TEMPLATE", name: "NamedEntry", order: 2);
- // Act & Assert (does not throw)
- builder.Build();
- }
- [Fact]
- public void TreeRouter_GenerateLink_WithName()
- {
- // Arrange
- var builder = CreateBuilder();
- // The named route has a lower order which will ensure that we aren't trying the route as
- // if it were an unnamed route.
- MapOutboundEntry(builder, "named", requiredValues: null, order: 1, name: "NamedRoute");
- MapOutboundEntry(builder, "unnamed", requiredValues: null, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: null, name: "NamedRoute");
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/named", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Fact]
- public void TreeRouter_DoesNotGenerateLink_IfThereIsNoRouteForAGivenName()
- {
- // Arrange
- var builder = CreateBuilder();
- // The named route has a lower order which will ensure that we aren't trying the route as
- // if it were an unnamed route.
- MapOutboundEntry(builder, "named", requiredValues: null, order: 1, name: "NamedRoute");
- // Add an unnamed entry to ensure we don't fall back to generating a link for an unnamed route.
- MapOutboundEntry(builder, "unnamed", requiredValues: null, order: 0);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values: null, ambientValues: null, name: "NonExistingNamedRoute");
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.Null(result);
- }
- [Theory]
- [InlineData("template/{parameter:int}", null)]
- [InlineData("template/{parameter:int}", "NaN")]
- [InlineData("template/{parameter}", null)]
- [InlineData("template/{*parameter:int}", null)]
- [InlineData("template/{*parameter:int}", "NaN")]
- public void TreeRouter_DoesNotGenerateLink_IfValuesDoNotMatchNamedEntry(string template, string value)
- {
- // Arrange
- var builder = CreateBuilder();
- // The named route has a lower order which will ensure that we aren't trying the route as
- // if it were an unnamed route.
- MapOutboundEntry(builder, template, requiredValues: null, order: 1, name: "NamedRoute");
- // Add an unnamed entry to ensure we don't fall back to generating a link for an unnamed route.
- MapOutboundEntry(builder, "unnamed", requiredValues: null, order: 0);
- var route = builder.Build();
- var ambientValues = value == null ? null : new { parameter = value };
- var context = CreateVirtualPathContext(values: null, ambientValues: ambientValues, name: "NamedRoute");
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.Null(result);
- }
- [Theory]
- [InlineData("template/{parameter:int}", "5")]
- [InlineData("template/{parameter}", "5")]
- [InlineData("template/{*parameter:int}", "5")]
- [InlineData("template/{*parameter}", "5")]
- public void TreeRouter_GeneratesLink_IfValuesMatchNamedEntry(string template, string value)
- {
- // Arrange
- var builder = CreateBuilder();
- // The named route has a lower order which will ensure that we aren't trying the route as
- // if it were an unnamed route.
- MapOutboundEntry(builder, template, requiredValues: null, order: 1, name: "NamedRoute");
- // Add an unnamed entry to ensure we don't fall back to generating a link for an unnamed route.
- MapOutboundEntry(builder, "unnamed", requiredValues: null, order: 0);
- var route = builder.Build();
- var ambientValues = value == null ? null : new { parameter = value };
- var context = CreateVirtualPathContext(values: null, ambientValues: ambientValues, name: "NamedRoute");
- // Act
- var result = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(result);
- Assert.Equal("/template/5", result.VirtualPath);
- Assert.Same(route, result.Router);
- Assert.Empty(result.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_NoRequiredValues()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_NoMatch()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Details", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store" });
- // Act
- var path = route.GetVirtualPath(context);
- // Assert
- Assert.Null(path);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithAmbientValues()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { }, new { action = "Index", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_HasTwoOptionalParametersWithoutValues()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "Customers/SeparatePageModels/{handler?}/{id?}", new { page = "/Customers/SeparatePageModels/Index" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { page = "/Customers/SeparatePageModels/Index" }, new { page = "/Customers/SeparatePageModels/Edit", id = "17" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Customers/SeparatePageModels", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithParameters()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store/{action}", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store/Index", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithMoreParameters()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder,
- "api/{area}/dosomething/{controller}/{action}",
- new { action = "Index", controller = "Store", area = "AwesomeCo" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(
- new { action = "Index", controller = "Store" },
- new { area = "AwesomeCo" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/AwesomeCo/dosomething/Store/Index", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithDefault()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store/{action=Index}", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithConstraint()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store/{action}/{id:int}", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store", id = 5 });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store/Index/5", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_NoMatch_WithConstraint()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store/{action}/{id:int}", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var next = new StubRouter();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Store", id = "heyyyy" });
- // Act
- var path = route.GetVirtualPath(context);
- // Assert
- Assert.Null(path);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithMixedAmbientValues()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index" }, new { controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_Match_WithQueryString()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Index", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", id = 5 }, new { controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api/Store?id=5", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_RejectedByFirstRoute()
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, "api/Store", new { action = "Index", controller = "Store" });
- MapOutboundEntry(builder, "api2/{controller}", new { action = "Index", controller = "Blog" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { action = "Index", controller = "Blog" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/api2/Blog", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_ToArea()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry1 = MapOutboundEntry(builder, "Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 2;
- var entry2 = MapOutboundEntry(builder, "Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 1;
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { area = "Help", action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_ToArea_PredecedenceReversed()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry1 = MapOutboundEntry(builder, "Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 1;
- var entry2 = MapOutboundEntry(builder, "Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 2;
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { area = "Help", action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_ToArea_WithAmbientValues()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry1 = MapOutboundEntry(builder, "Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 2;
- var entry2 = MapOutboundEntry(builder, "Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 1;
- var route = builder.Build();
- var context = CreateVirtualPathContext(
- values: new { action = "Edit", controller = "Store" },
- ambientValues: new { area = "Help" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_OutOfArea_IgnoresAmbientValue()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry1 = MapOutboundEntry(builder, "Help/Store", new { area = "Help", action = "Edit", controller = "Store" });
- entry1.Precedence = 2;
- var entry2 = MapOutboundEntry(builder, "Store", new { area = (string)null, action = "Edit", controller = "Store" });
- entry2.Precedence = 1;
- var route = builder.Build();
- var context = CreateVirtualPathContext(
- values: new { action = "Edit", controller = "Store" },
- ambientValues: new { area = "Blog" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- public static IEnumerable<object[]> OptionalParamValues
- {
- get
- {
- return new object[][]
- {
- // defaults
- // ambient values
- // values
- new object[]
- {
- "Test/{val1}/{val2}.{val3?}",
- new {val1 = "someval1", val2 = "someval2", val3 = "someval3a"},
- new {val3 = "someval3v"},
- "/Test/someval1/someval2.someval3v",
- },
- new object[]
- {
- "Test/{val1}/{val2}.{val3?}",
- new {val3 = "someval3a"},
- new {val1 = "someval1", val2 = "someval2", val3 = "someval3v" },
- "/Test/someval1/someval2.someval3v",
- },
- new object[]
- {
- "Test/{val1}/{val2}.{val3?}",
- null,
- new {val1 = "someval1", val2 = "someval2" },
- "/Test/someval1/someval2",
- },
- new object[]
- {
- "Test/{val1}.{val2}.{val3}.{val4?}",
- new {val1 = "someval1", val2 = "someval2" },
- new {val4 = "someval4", val3 = "someval3" },
- "/Test/someval1.someval2.someval3.someval4",
- },
- new object[]
- {
- "Test/{val1}.{val2}.{val3}.{val4?}",
- new {val1 = "someval1", val2 = "someval2" },
- new {val3 = "someval3" },
- "/Test/someval1.someval2.someval3",
- },
- new object[]
- {
- "Test/.{val2?}",
- null,
- new {val2 = "someval2" },
- "/Test/.someval2",
- },
- new object[]
- {
- "Test/.{val2?}",
- null,
- null,
- "/Test/",
- },
- new object[]
- {
- "Test/{val1}.{val2}",
- new {val1 = "someval1", val2 = "someval2" },
- new {val3 = "someval3" },
- "/Test/someval1.someval2?val3=someval3",
- },
- };
- }
- }
- [Theory]
- [MemberData(nameof(OptionalParamValues))]
- public void TreeRouter_GenerateLink_Match_WithOptionalParameters(
- string template,
- object ambientValues,
- object values,
- string expected)
- {
- // Arrange
- var builder = CreateBuilder();
- MapOutboundEntry(builder, template);
- var route = builder.Build();
- var context = CreateVirtualPathContext(values, ambientValues);
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal(expected, pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public async Task TreeRouter_ReplacesExistingRouteValues_IfNotNull()
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "Foo/{*path}");
- var route = builder.Build();
- var context = CreateRouteContext("/Foo/Bar");
- var originalRouteData = context.RouteData;
- originalRouteData.Values.Add("path", "default");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal("Bar", context.RouteData.Values["path"]);
- }
- [Fact]
- public async Task TreeRouter_DoesNotReplaceExistingRouteValues_IfNull()
- {
- // Arrange
- var builder = CreateBuilder();
- MapInboundEntry(builder, "Foo/{*path}");
- var route = builder.Build();
- var context = CreateRouteContext("/Foo/");
- var originalRouteData = context.RouteData;
- originalRouteData.Values.Add("path", "default");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal("default", context.RouteData.Values["path"]);
- }
- [Fact]
- public async Task TreeRouter_SnapshotsRouteData()
- {
- // Arrange
- RouteValueDictionary nestedValues = null;
- List<IRouter> nestedRouters = null;
- var next = new Mock<IRouter>();
- next
- .Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
- .Callback<RouteContext>(c =>
- {
- nestedValues = new RouteValueDictionary(c.RouteData.Values);
- nestedRouters = new List<IRouter>(c.RouteData.Routers);
- c.Handler = null; // Not a match
- })
- .Returns(Task.CompletedTask);
- var builder = CreateBuilder();
- MapInboundEntry(builder, "api/Store", handler: next.Object);
- var route = builder.Build();
- var context = CreateRouteContext("/api/Store");
- var routeData = context.RouteData;
- routeData.Values.Add("action", "Index");
- var originalValues = new RouteValueDictionary(context.RouteData.Values);
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.Equal(originalValues, context.RouteData.Values);
- Assert.NotEqual(nestedValues, context.RouteData.Values);
- }
- [Fact]
- public async Task TreeRouter_SnapshotsRouteData_ResetsWhenNotMatched()
- {
- // Arrange
- RouteValueDictionary nestedValues = null;
- List<IRouter> nestedRouters = null;
- var next = new Mock<IRouter>();
- next
- .Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
- .Callback<RouteContext>(c =>
- {
- nestedValues = new RouteValueDictionary(c.RouteData.Values);
- nestedRouters = new List<IRouter>(c.RouteData.Routers);
- c.Handler = null; // Not a match
- })
- .Returns(Task.CompletedTask);
- var builder = CreateBuilder();
- MapInboundEntry(builder, "api/Store", handler: next.Object);
- var route = builder.Build();
- var context = CreateRouteContext("/api/Store");
- context.RouteData.Values.Add("action", "Index");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotEqual(nestedValues, context.RouteData.Values);
- // The new routedata is a copy
- Assert.Equal("Index", context.RouteData.Values["action"]);
- Assert.Equal("Index", nestedValues["action"]);
- Assert.DoesNotContain(context.RouteData.Values, kvp => kvp.Key == "test_route_group");
- Assert.Single(nestedValues, kvp => kvp.Key == "test_route_group");
- Assert.Empty(context.RouteData.Routers);
- Assert.Single(nestedRouters);
- Assert.Equal(next.Object.GetType(), nestedRouters[0].GetType());
- }
- [Fact]
- public async Task TreeRouter_SnapshotsRouteData_ResetsWhenThrows()
- {
- // Arrange
- RouteValueDictionary nestedValues = null;
- List<IRouter> nestedRouters = null;
- var next = new Mock<IRouter>();
- next
- .Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
- .Callback<RouteContext>(c =>
- {
- nestedValues = new RouteValueDictionary(c.RouteData.Values);
- nestedRouters = new List<IRouter>(c.RouteData.Routers);
- throw new Exception();
- })
- .Returns(Task.CompletedTask);
- var builder = CreateBuilder();
- MapInboundEntry(builder, "api/Store", handler: next.Object);
- var route = builder.Build();
- var context = CreateRouteContext("/api/Store");
- context.RouteData.Values.Add("action", "Index");
- // Act
- await Assert.ThrowsAsync<Exception>(() => route.RouteAsync(context));
- // Assert
- Assert.NotEqual(nestedValues, context.RouteData.Values);
- Assert.Equal("Index", context.RouteData.Values["action"]);
- Assert.Equal("Index", nestedValues["action"]);
- Assert.DoesNotContain(context.RouteData.Values, kvp => kvp.Key == "test_route_group");
- Assert.Single(nestedValues, kvp => kvp.Key == "test_route_group");
- Assert.Empty(context.RouteData.Routers);
- Assert.Single(nestedRouters);
- Assert.Equal(next.Object.GetType(), nestedRouters[0].GetType());
- }
- [Fact]
- public async Task TreeRouter_SnapshotsRouteData_ResetsBeforeMatchingEachRouteEntry()
- {
- // This test replicates a scenario raised as issue https://github.com/aspnet/Routing/issues/394
- // The RouteValueDictionary entries populated while matching route entries should not be left
- // in place if the route entry turns out not to match, because that would leak unwanted state
- // to subsequent route entries and might cause "An element with the key ... already exists"
- // exceptions.
- // Arrange
- RouteValueDictionary nestedValues = null;
- var next = new Mock<IRouter>();
- next
- .Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
- .Callback<RouteContext>(c =>
- {
- nestedValues = new RouteValueDictionary(c.RouteData.Values);
- c.Handler = NullHandler;
- })
- .Returns(Task.CompletedTask);
- var builder = CreateBuilder();
- MapInboundEntry(builder, "cat_{category1}/prod1_{product}"); // Matches on first segment but not on second
- MapInboundEntry(builder, "cat_{category2}/prod2_{product}", handler: next.Object);
- var route = builder.Build();
- var context = CreateRouteContext("/cat_examplecategory/prod2_exampleproduct");
- // Act
- await route.RouteAsync(context);
- // Assert
- Assert.NotNull(nestedValues);
- Assert.Equal("examplecategory", nestedValues["category2"]);
- Assert.Equal("exampleproduct", nestedValues["product"]);
- Assert.DoesNotContain(nestedValues, kvp => kvp.Key == "category1");
- }
- [Fact]
- public void TreeRouter_GenerateLink_MatchesNullRequiredValue_WithNullRequestValueString()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry = MapOutboundEntry(
- builder,
- "Help/Store",
- requiredValues: new { area = (string)null, action = "Edit", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { area = (string)null, action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_MatchesNullRequiredValue_WithEmptyRequestValueString()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry = MapOutboundEntry(
- builder,
- "Help/Store",
- requiredValues: new { area = (string)null, action = "Edit", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { area = "", action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_MatchesEmptyStringRequiredValue_WithNullRequestValueString()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry = MapOutboundEntry(
- builder,
- "Help/Store",
- requiredValues: new { foo = "", action = "Edit", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { foo = (string)null, action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- [Fact]
- public void TreeRouter_GenerateLink_MatchesEmptyStringRequiredValue_WithEmptyRequestValueString()
- {
- // Arrange
- var builder = CreateBuilder();
- var entry = MapOutboundEntry(
- builder,
- "Help/Store",
- requiredValues: new { foo = "", action = "Edit", controller = "Store" });
- var route = builder.Build();
- var context = CreateVirtualPathContext(new { foo = "", action = "Edit", controller = "Store" });
- // Act
- var pathData = route.GetVirtualPath(context);
- // Assert
- Assert.NotNull(pathData);
- Assert.Equal("/Help/Store", pathData.VirtualPath);
- Assert.Same(route, pathData.Router);
- Assert.Empty(pathData.DataTokens);
- }
- private static RouteContext CreateRouteContext(string requestPath)
- {
- var request = new Mock<HttpRequest>(MockBehavior.Strict);
- request.SetupGet(r => r.Path).Returns(new PathString(requestPath));
- var context = new Mock<HttpContext>(MockBehavior.Strict);
- context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
- .Returns(NullLoggerFactory.Instance);
- context.SetupGet(c => c.Request).Returns(request.Object);
- return new RouteContext(context.Object);
- }
- private static VirtualPathContext CreateVirtualPathContext(
- object values,
- object ambientValues = null,
- string name = null)
- {
- var mockHttpContext = new Mock<HttpContext>();
- mockHttpContext.Setup(h => h.RequestServices.GetService(typeof(ILoggerFactory)))
- .Returns(NullLoggerFactory.Instance);
- return new VirtualPathContext(
- mockHttpContext.Object,
- new RouteValueDictionary(ambientValues),
- new RouteValueDictionary(values),
- name);
- }
- private static InboundRouteEntry MapInboundEntry(
- TreeRouteBuilder builder,
- string template,
- int order = 0,
- IRouter handler = null)
- {
- var entry = builder.MapInbound(
- handler ?? new StubRouter(),
- TemplateParser.Parse(template),
- routeName: null,
- order: order);
- // Add a generated 'route group' so we can identify later which entry matched.
- entry.Defaults["test_route_group"] = CreateRouteGroup(order, template);
- return entry;
- }
- private static OutboundRouteEntry MapOutboundEntry(
- TreeRouteBuilder builder,
- string template,
- object requiredValues = null,
- int order = 0,
- string name = null,
- IRouter handler = null)
- {
- var entry = builder.MapOutbound(
- handler ?? new StubRouter(),
- TemplateParser.Parse(template),
- requiredLinkValues: new RouteValueDictionary(requiredValues),
- routeName: name,
- order: order);
- // Add a generated 'route group' so we can identify later which entry matched.
- entry.Defaults["test_route_group"] = CreateRouteGroup(order, template);
- return entry;
- }
- private static string CreateRouteGroup(int order, string template)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0}&{1}", order, template);
- }
- private static DefaultInlineConstraintResolver CreateConstraintResolver()
- {
- var options = new RouteOptions();
- var optionsMock = new Mock<IOptions<RouteOptions>>();
- optionsMock.SetupGet(o => o.Value).Returns(options);
- return new DefaultInlineConstraintResolver(optionsMock.Object, new TestServiceProvider());
- }
- private static TreeRouteBuilder CreateBuilder()
- {
- var objectPoolProvider = new DefaultObjectPoolProvider();
- var objectPolicy = new UriBuilderContextPooledObjectPolicy();
- var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
- var constraintResolver = CreateConstraintResolver();
- var builder = new TreeRouteBuilder(
- NullLoggerFactory.Instance,
- objectPool,
- constraintResolver);
- return builder;
- }
- private static TreeRouter CreateTreeRouter(
- string firstTemplate,
- string secondTemplate)
- {
- var builder = CreateBuilder();
- MapOutboundEntry(builder, firstTemplate);
- MapOutboundEntry(builder, secondTemplate);
- return builder.Build();
- }
- private class StubRouter : IRouter
- {
- public VirtualPathContext GenerationContext { get; set; }
- public RouteContext MatchingContext { get; set; }
- public Func<RouteContext, bool> MatchingDelegate { get; set; }
- public VirtualPathData GetVirtualPath(VirtualPathContext context)
- {
- GenerationContext = context;
- return null;
- }
- public Task RouteAsync(RouteContext context)
- {
- if (MatchingDelegate == null)
- {
- context.Handler = NullHandler;
- }
- else
- {
- context.Handler = MatchingDelegate(context) ? NullHandler : null;
- }
- return Task.FromResult(true);
- }
- }
- }
|