/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Http/Routing/test/UnitTests/CompositeEndpointDataSourceTest.cs
541 строка
21 KB
James Newton-King
Add metrics to ASP.NET Core (#46834)
12 апр 2023, 10:34
Не верифицирован
12 апр 2023, 10:34
7d0c273
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.ObjectModel; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Routing; public class CompositeEndpointDataSourceTest { [Fact] public void CreatesShallowCopyOf_ListOfEndpoints() { // Arrange var endpoint1 = CreateEndpoint("/a"); var endpoint2 = CreateEndpoint("/b"); var dataSource = new DefaultEndpointDataSource(new Endpoint[] { endpoint1, endpoint2 }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); // Act var endpoints = compositeDataSource.Endpoints; // Assert Assert.NotSame(endpoints, dataSource.Endpoints); Assert.Equal(endpoints, dataSource.Endpoints); } [Fact] public void CreatesShallowCopyOf_ListOfGroupedEndpoints() { var endpoint1 = CreateEndpoint("/a"); var endpoint2 = CreateEndpoint("/b"); var dataSource = new TestGroupDataSource(new RouteEndpoint[] { endpoint1, endpoint2 }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); var prefix = RoutePatternFactory.Parse("/"); var conventions = Array.Empty<Action<EndpointBuilder>>(); var finallyConventions = Array.Empty<Action<EndpointBuilder>>(); var applicationServices = new ServiceCollection().BuildServiceProvider(); var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(new RouteGroupContext { Prefix = prefix, Conventions = conventions, FinallyConventions = finallyConventions, ApplicationServices = applicationServices }); var resolvedGroupEndpoints = Assert.Single(dataSource.ResolvedGroupedEndpoints); Assert.NotSame(groupedEndpoints, resolvedGroupEndpoints); Assert.Equal(groupedEndpoints, resolvedGroupEndpoints); } [Fact] public void RepeatedlyThrows_WhenChildDataSourcesThrow() { var ex = new Exception(); var compositeDataSource = new CompositeEndpointDataSource(new[] { new EndpointThrowingDataSource(ex), }); var groupContext = new RouteGroupContext { Prefix = RoutePatternFactory.Parse(""), Conventions = Array.Empty<Action<EndpointBuilder>>(), FinallyConventions = Array.Empty<Action<EndpointBuilder>>(), ApplicationServices = new ServiceCollection().BuildServiceProvider(), }; Assert.Same(ex, Assert.Throws<Exception>(() => compositeDataSource.Endpoints)); Assert.Same(ex, Assert.Throws<Exception>(() => compositeDataSource.Endpoints)); Assert.Same(ex, Assert.Throws<Exception>(() => compositeDataSource.GetGroupedEndpoints(groupContext))); Assert.Same(ex, Assert.Throws<Exception>(() => compositeDataSource.GetGroupedEndpoints(groupContext))); } [Fact] public void Endpoints_ReturnsAllEndpoints_FromMultipleDataSources() { // Arrange var endpoint1 = CreateEndpoint("/a"); var endpoint2 = CreateEndpoint("/b"); var endpoint3 = CreateEndpoint("/c"); var endpoint4 = CreateEndpoint("/d"); var endpoint5 = CreateEndpoint("/e"); var compositeDataSource = new CompositeEndpointDataSource(new[] { new DefaultEndpointDataSource(new Endpoint[] { endpoint1, endpoint2 }), new DefaultEndpointDataSource(new Endpoint[] { endpoint3, endpoint4 }), new DefaultEndpointDataSource(new Endpoint[] { endpoint5 }), }); // Act var endpoints = compositeDataSource.Endpoints; // Assert Assert.Collection( endpoints, (ep) => Assert.Same(endpoint1, ep), (ep) => Assert.Same(endpoint2, ep), (ep) => Assert.Same(endpoint3, ep), (ep) => Assert.Same(endpoint4, ep), (ep) => Assert.Same(endpoint5, ep)); } [Fact] public void DataSourceChanges_AreReflected_InEndpoints() { // Arrange1 var endpoint1 = CreateEndpoint("/a"); var dataSource1 = new DynamicEndpointDataSource(endpoint1); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource1 }); // Act1 var endpoints = compositeDataSource.Endpoints; // Assert1 var endpoint = Assert.Single(endpoints); Assert.Same(endpoint1, endpoint); // Arrange2 var endpoint2 = CreateEndpoint("/b"); // Act2 dataSource1.AddEndpoint(endpoint2); // Assert2 Assert.Collection( compositeDataSource.Endpoints, (ep) => Assert.Same(endpoint1, ep), (ep) => Assert.Same(endpoint2, ep)); // Arrange3 var endpoint3 = CreateEndpoint("/c"); // Act2 dataSource1.AddEndpoint(endpoint3); // Assert2 Assert.Collection( compositeDataSource.Endpoints, (ep) => Assert.Same(endpoint1, ep), (ep) => Assert.Same(endpoint2, ep), (ep) => Assert.Same(endpoint3, ep)); } [Fact] public void ConsumerChangeToken_IsRefreshed_WhenDataSourceCallbackFires() { // Arrange1 var endpoint1 = CreateEndpoint("/a"); var dataSource1 = new DynamicEndpointDataSource(endpoint1); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource1 }); // Act1 var endpoints = compositeDataSource.Endpoints; // Assert1 var changeToken1 = compositeDataSource.GetChangeToken(); var token = Assert.IsType<CancellationChangeToken>(changeToken1); Assert.False(token.HasChanged); // initial state // Arrange2 var endpoint2 = CreateEndpoint("/b"); // Act2 dataSource1.AddEndpoint(endpoint2); // Assert2 Assert.True(changeToken1.HasChanged); // old token is expected to be changed var changeToken2 = compositeDataSource.GetChangeToken(); // new token is in a unchanged state Assert.NotSame(changeToken2, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken2); Assert.False(token.HasChanged); // Arrange3 var endpoint3 = CreateEndpoint("/c"); // Act2 dataSource1.AddEndpoint(endpoint3); // Assert2 Assert.True(changeToken2.HasChanged); // old token is expected to be changed var changeToken3 = compositeDataSource.GetChangeToken(); // new token is in a unchanged state Assert.NotSame(changeToken3, changeToken2); Assert.NotSame(changeToken3, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken3); Assert.False(token.HasChanged); } [Fact] public void ConsumerChangeToken_IsRefreshed_WhenNewDataSourceCallbackFires() { var endpoint1 = CreateEndpoint("/a"); var dataSource1 = new DynamicEndpointDataSource(endpoint1); var observableCollection = new ObservableCollection<EndpointDataSource> { dataSource1 }; var compositeDataSource = new CompositeEndpointDataSource(observableCollection); var changeToken1 = compositeDataSource.GetChangeToken(); var token = Assert.IsType<CancellationChangeToken>(changeToken1); Assert.False(token.HasChanged); var endpoint2 = CreateEndpoint("/b"); // Update ObservableCollection with a new DynamicEndpointDataSource var dataSource2 = new DynamicEndpointDataSource(endpoint2); observableCollection.Add(dataSource2); Assert.True(changeToken1.HasChanged); var changeToken2 = compositeDataSource.GetChangeToken(); Assert.NotSame(changeToken2, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken2); Assert.False(token.HasChanged); // Update the newly added DynamicEndpointDataSource var endpoint3 = CreateEndpoint("/c"); dataSource2.AddEndpoint(endpoint3); Assert.True(changeToken2.HasChanged); var changeToken3 = compositeDataSource.GetChangeToken(); Assert.NotSame(changeToken3, changeToken2); Assert.NotSame(changeToken3, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken3); Assert.False(token.HasChanged); } [Fact] public void ConsumerChangeToken_IsNotRefreshed_AfterDisposal() { var endpoint1 = CreateEndpoint("/a"); var dataSource1 = new DynamicEndpointDataSource(endpoint1); var observableCollection = new ObservableCollection<EndpointDataSource> { dataSource1 }; var compositeDataSource = new CompositeEndpointDataSource(observableCollection); var changeToken1 = compositeDataSource.GetChangeToken(); var token = Assert.IsType<CancellationChangeToken>(changeToken1); Assert.False(token.HasChanged); var endpoint2 = CreateEndpoint("/b"); // Update DynamicEndpointDatasource dataSource1.AddEndpoint(endpoint2); Assert.True(changeToken1.HasChanged); var changeToken2 = compositeDataSource.GetChangeToken(); Assert.NotSame(changeToken2, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken2); Assert.False(token.HasChanged); // Update ObservableCollection var endpoint3 = CreateEndpoint("/c"); var datasource2 = new DynamicEndpointDataSource(endpoint3); observableCollection.Add(datasource2); Assert.True(changeToken2.HasChanged); var changeToken3 = compositeDataSource.GetChangeToken(); Assert.NotSame(changeToken3, changeToken2); Assert.NotSame(changeToken3, changeToken1); token = Assert.IsType<CancellationChangeToken>(changeToken3); Assert.False(token.HasChanged); compositeDataSource.Dispose(); // Update DynamicEndpointDatasource and ObservableCollection after disposing CompositeEndpointDataSource. var endpoint4 = CreateEndpoint("/d"); dataSource1.AddEndpoint(endpoint4); var endpoint5 = CreateEndpoint("/d"); var datasource3 = new DynamicEndpointDataSource(endpoint5); observableCollection.Add(datasource3); // Token is not changed since the CompositeEndpointDataSource was disposed prior to the last endpoint being added. Assert.False(changeToken3.HasChanged); } [Fact] public void GetGroupedEndpoints_ForwardedToChildDataSources() { var endpoint = CreateEndpoint("/a"); var dataSource = new TestGroupDataSource(new RouteEndpoint[] { endpoint }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); var prefix = RoutePatternFactory.Parse("/prefix"); var applicationServices = new ServiceCollection().BuildServiceProvider(); var metadata = new EndpointNameMetadata("name"); var conventions = new Action<EndpointBuilder>[] { b => b.Metadata.Add(metadata), }; var finallyConventions = Array.Empty<Action<EndpointBuilder>>(); var context = new RouteGroupContext { Prefix = prefix, Conventions = conventions, FinallyConventions = finallyConventions, ApplicationServices = applicationServices }; var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context); var receivedContext = Assert.Single(dataSource.ReceivedRouteGroupContexts); Assert.Same(context, receivedContext); var resolvedEndpoint = Assert.IsType<RouteEndpoint>(Assert.Single(groupedEndpoints)); Assert.Equal("/prefix/a", resolvedEndpoint.RoutePattern.RawText); Assert.Collection(resolvedEndpoint.Metadata, m => Assert.Same(metadata, m), m => Assert.IsAssignableFrom<IRouteDiagnosticsMetadata>(m)); } [Fact] public void GetGroupedEndpoints_GroupFinallyConventionsApplyToAllEndpoints() { var endpointMetadata = new EndpointMetadataCollection(new object[] { "initial-metadata" }); var endpoint1 = CreateEndpoint("/a", metadata: endpointMetadata); var endpoint2 = CreateEndpoint("/b", metadata: endpointMetadata); var dataSource = new TestGroupDataSource(new RouteEndpoint[] { endpoint1, endpoint2 }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); var prefix = RoutePatternFactory.Parse("/prefix"); var applicationServices = new ServiceCollection().BuildServiceProvider(); var metadata = new EndpointNameMetadata("name"); var finallyConventions = new Action<EndpointBuilder>[] { b => { if (b.Metadata.OfType<string>().SingleOrDefault() == "initial-metadata") { b.Metadata.Add(metadata); } } }; var conventions = Array.Empty<Action<EndpointBuilder>>(); var context = new RouteGroupContext { Prefix = prefix, Conventions = conventions, FinallyConventions = finallyConventions, ApplicationServices = applicationServices }; var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context); var receivedContext = Assert.Single(dataSource.ReceivedRouteGroupContexts); Assert.Same(context, receivedContext); Assert.Collection(groupedEndpoints, endpoint1 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint1); Assert.Equal("/prefix/a", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal("initial-metadata", endpoint.Metadata.GetMetadata<string>()); }, endpoint2 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint2); Assert.Equal("/prefix/b", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal("initial-metadata", endpoint.Metadata.GetMetadata<string>()); }); } [Fact] public void GetGroupedEndpoints_GroupFinallyConventionsCanExamineRegularConventions() { var endpoint1 = CreateEndpoint("/a"); var endpoint2 = CreateEndpoint("/b"); var dataSource = new TestGroupDataSource(new RouteEndpoint[] { endpoint1, endpoint2 }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); var prefix = RoutePatternFactory.Parse("/prefix"); var applicationServices = new ServiceCollection().BuildServiceProvider(); var metadata = new EndpointNameMetadata("name"); var conventions = new Action<EndpointBuilder>[] { b => b.Metadata.Add("initial-metadata") }; var finallyConventions = new Action<EndpointBuilder>[] { b => { if (b.Metadata.OfType<string>().SingleOrDefault() == "initial-metadata") { b.Metadata.Add(metadata); } } }; var context = new RouteGroupContext { Prefix = prefix, Conventions = conventions, FinallyConventions = finallyConventions, ApplicationServices = applicationServices }; var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context); var receivedContext = Assert.Single(dataSource.ReceivedRouteGroupContexts); Assert.Same(context, receivedContext); Assert.Collection(groupedEndpoints, endpoint1 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint1); Assert.Equal("/prefix/a", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal("initial-metadata", endpoint.Metadata.GetMetadata<string>()); }, endpoint2 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint2); Assert.Equal("/prefix/b", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal("initial-metadata", endpoint.Metadata.GetMetadata<string>()); }); } [Fact] public void GetGroupedEndpoints_MultipleGroupFinallyConventionsApplyToAllEndpoints() { var endpointMetadata = new EndpointMetadataCollection(new object[] { "initial-metadata" }); var endpoint1 = CreateEndpoint("/a", metadata: endpointMetadata); var endpoint2 = CreateEndpoint("/b", metadata: endpointMetadata); var dataSource = new TestGroupDataSource(new RouteEndpoint[] { endpoint1, endpoint2 }); var compositeDataSource = new CompositeEndpointDataSource(new[] { dataSource }); var prefix = RoutePatternFactory.Parse("/prefix"); var applicationServices = new ServiceCollection().BuildServiceProvider(); var metadata = new EndpointNameMetadata("name"); var finallyConventions = new Action<EndpointBuilder>[] { b => { if (b.Metadata.OfType<string>().SingleOrDefault() == "initial-metadata") { b.Metadata.Add(metadata); } }, b => { if (b.Metadata.OfType<IEndpointNameMetadata>().SingleOrDefault() is not null) { b.Metadata.Add("saw-last-metadata"); } } }; var conventions = Array.Empty<Action<EndpointBuilder>>(); var context = new RouteGroupContext { Prefix = prefix, Conventions = conventions, FinallyConventions = finallyConventions, ApplicationServices = applicationServices }; var groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context); // Call twice to ensure that `GetGroupedEndpoints` is idempotent groupedEndpoints = compositeDataSource.GetGroupedEndpoints(context); Assert.Collection(dataSource.ReceivedRouteGroupContexts, receivedContext => Assert.Same(context, receivedContext), receivedContext => Assert.Same(context, receivedContext)); Assert.Collection(groupedEndpoints, endpoint1 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint1); Assert.Equal("/prefix/a", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal(new[] { "initial-metadata", "saw-last-metadata" }, endpoint.Metadata.GetOrderedMetadata<string>()); }, endpoint2 => { var endpoint = Assert.IsType<RouteEndpoint>(endpoint2); Assert.Equal("/prefix/b", endpoint.RoutePattern.RawText); Assert.NotNull(endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()); Assert.Equal(new[] { "initial-metadata", "saw-last-metadata" }, endpoint.Metadata.GetOrderedMetadata<string>()); }); } private RouteEndpoint CreateEndpoint( string template, object defaults = null, int order = 0, EndpointMetadataCollection metadata = null, string routeName = null) { return new RouteEndpoint( TestConstants.EmptyRequestDelegate, RoutePatternFactory.Parse(template, defaults, parameterPolicies: null), order, metadata ?? EndpointMetadataCollection.Empty, null); } private class TestGroupDataSource : EndpointDataSource { public TestGroupDataSource(params Endpoint[] endpoints) => Endpoints = endpoints; public override IReadOnlyList<Endpoint> Endpoints { get; } public List<RouteGroupContext> ReceivedRouteGroupContexts { get; } = new(); public List<IReadOnlyList<Endpoint>> ResolvedGroupedEndpoints { get; } = new(); public override IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context) { ReceivedRouteGroupContexts.Add(context); var resolved = base.GetGroupedEndpoints(context); ResolvedGroupedEndpoints.Add(resolved); return resolved; } public override IChangeToken GetChangeToken() => NullChangeToken.Singleton; } private class EndpointThrowingDataSource : EndpointDataSource { private readonly Exception _ex; public EndpointThrowingDataSource(Exception ex) { _ex = ex; } public override IReadOnlyList<Endpoint> Endpoints => throw _ex; public override IChangeToken GetChangeToken() => NullChangeToken.Singleton; } }