// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.Http.HttpResults; public partial class CreatedAtRouteResultTests { public static IEnumerable CreatedAtRouteData { get { yield return new object[] { null }; yield return new object[] { new Dictionary() { { "hello", "world" } } }; yield return new object[] { new RouteValueDictionary(new Dictionary() { { "test", "case" }, { "sample", "route" } }) }; } } [Theory] [MemberData(nameof(CreatedAtRouteData))] public async Task CreatedAtRouteResult_ReturnsStatusCode_SetsLocationHeader(object values) { // Arrange var expectedUrl = "testAction"; var httpContext = GetHttpContext(expectedUrl); // Act var result = new CreatedAtRoute(routeName: null, routeValues: values); await result.ExecuteAsync(httpContext); // Assert Assert.Equal(StatusCodes.Status201Created, httpContext.Response.StatusCode); Assert.Equal(expectedUrl, httpContext.Response.Headers["Location"]); } [Fact] public async Task CreatedAtRouteResult_ThrowsOnNullUrl() { // Arrange var httpContext = GetHttpContext(expectedUrl: null); var result = new CreatedAtRoute( routeName: null, routeValues: new Dictionary()); // Act & Assert await ExceptionAssert.ThrowsAsync( async () => await result.ExecuteAsync(httpContext), "No route matches the supplied values."); } [Fact] public void PopulateMetadata_AddsResponseTypeMetadata() { // Arrange CreatedAtRoute MyApi() { throw new NotImplementedException(); } var metadata = new List(); var builder = new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/"), order: 0); // Act PopulateMetadata(((Delegate)MyApi).GetMethodInfo(), builder); // Assert var producesResponseTypeMetadata = builder.Metadata.OfType().Last(); Assert.Equal(StatusCodes.Status201Created, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); } [Fact] public void ExecuteAsync_ThrowsArgumentNullException_WhenHttpContextIsNull() { // Arrange var result = new CreatedAtRoute(null); HttpContext httpContext = null; // Act & Assert Assert.ThrowsAsync("httpContext", () => result.ExecuteAsync(httpContext)); } [Fact] public void PopulateMetadata_ThrowsArgumentNullException_WhenMethodOrBuilderAreNull() { // Act & Assert Assert.Throws("method", () => PopulateMetadata(null, new RouteEndpointBuilder(requestDelegate: null, RoutePatternFactory.Parse("/"), order: 0))); Assert.Throws("builder", () => PopulateMetadata(((Delegate)PopulateMetadata_ThrowsArgumentNullException_WhenMethodOrBuilderAreNull).GetMethodInfo(), null)); } [Fact] public void CreatedAtRouteResult_Implements_IValueHttpResult_Correctly() { // Arrange & Act var rawResult = new CreatedAtRoute( routeName: null, routeValues: new Dictionary()); // Assert var result = Assert.IsAssignableFrom(rawResult); Assert.Equal(StatusCodes.Status201Created, result.StatusCode); } private static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) where TResult : IEndpointMetadataProvider => TResult.PopulateMetadata(method, builder); private static HttpContext GetHttpContext(string expectedUrl) { var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = new PathString(""); httpContext.Response.Body = new MemoryStream(); httpContext.RequestServices = CreateServices(expectedUrl); return httpContext; } private static IServiceProvider CreateServices(string expectedUrl) { var services = new ServiceCollection(); services.AddSingleton(); services.AddSingleton(new TestLinkGenerator { Url = expectedUrl }); return services.BuildServiceProvider(); } }