// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNetCore.Mvc { public class AcceptedAtActionResultTests { [Theory] [InlineData(null)] [InlineData("")] [InlineData("actionName")] public void Constructor_InitializesActionName(string actionName) { // Act var result = new AcceptedAtActionResult(actionName: actionName, controllerName: null, routeValues: null, value: null); // Assert Assert.Equal(actionName, result.ActionName); } [Theory] [InlineData(null)] [InlineData("")] [InlineData("controllerName")] public void Constructor_InitializesControllerName(string controllerName) { // Act var result = new AcceptedAtActionResult(actionName: null, controllerName: controllerName, routeValues: null, value: null); // Assert Assert.Equal(controllerName, result.ControllerName); } public static TheoryData RouteValuesData() { return new TheoryData() { { null, -1 }, { "value", 1 }, { new object(), 0 } }; } [Theory] [MemberData(nameof(RouteValuesData))] public void Constructor_InitializesRouteValues(object routeValues, int expectedRouteValuesCount) { // Act var result = new AcceptedAtActionResult(actionName: null, controllerName: null, routeValues: routeValues, value: null); // Assert if (expectedRouteValuesCount == -1) { Assert.Null(result.RouteValues); } else { Assert.Equal(expectedRouteValuesCount, result.RouteValues.Count); } } public static TheoryData ValuesData { get { return new TheoryData { null, "Test string", new object(), }; } } [Theory] [MemberData(nameof(ValuesData))] public void Constructor_InitializesStatusCodeAndValue(object value) { // Arrange var url = "testAction"; // Act var result = new AcceptedAtActionResult( actionName: url, controllerName: null, routeValues: null, value: value); // Assert Assert.Equal(StatusCodes.Status202Accepted, result.StatusCode); Assert.Same(value, result.Value); } [Fact] public void UrlHelper_Get_ReturnsNull() { // Act var result = new AcceptedAtActionResult(actionName: null, controllerName: null, routeValues: null, value: null); // Assert Assert.Null(result.UrlHelper); } [Theory] [MemberData(nameof(ValuesData))] public async Task ExecuteResultAsync_SetsObjectValueOfFormatter(object value) { // Arrange var url = "testAction"; var formatter = CreateMockFormatter(); var httpContext = GetHttpContext(formatter); object actual = null; formatter.Setup(f => f.WriteAsync(It.IsAny())) .Callback((OutputFormatterWriteContext context) => actual = context.Object) .Returns(Task.FromResult(0)); var actionContext = GetActionContext(httpContext); var urlHelper = GetMockUrlHelper(url); // Act var result = new AcceptedAtActionResult( actionName: url, controllerName: null, routeValues: null, value: value); result.UrlHelper = urlHelper; await result.ExecuteResultAsync(actionContext); // Assert Assert.Same(value, actual); } [Fact] public async Task ExecuteResultAsync_SetsStatusCodeAndLocationHeader() { // Arrange var expectedUrl = "testAction"; var formatter = CreateMockFormatter(); var httpContext = GetHttpContext(formatter); var actionContext = GetActionContext(httpContext); var urlHelper = GetMockUrlHelper(expectedUrl); // Act var result = new AcceptedAtActionResult( actionName: expectedUrl, controllerName: null, routeValues: null, value: null); result.UrlHelper = urlHelper; await result.ExecuteResultAsync(actionContext); // Assert Assert.Equal(StatusCodes.Status202Accepted, httpContext.Response.StatusCode); Assert.Equal(expectedUrl, httpContext.Response.Headers["Location"]); } [Theory] [InlineData(null)] [InlineData("")] public async Task ExecuteResultAsync_ThrowsIfActionUrlIsNullOrEmpty(string returnValue) { // Arrange var formatter = CreateMockFormatter(); var httpContext = GetHttpContext(formatter); var actionContext = GetActionContext(httpContext); var urlHelper = GetMockUrlHelper(returnValue); // Act var result = new AcceptedAtActionResult( actionName: null, controllerName: null, routeValues: null, value: null); result.UrlHelper = urlHelper; // Assert await ExceptionAssert.ThrowsAsync(() => result.ExecuteResultAsync(actionContext), "No route matches the supplied values."); } [Fact] public void OnFormatting_NullUrlHelperContextHasRequestServices_ReturnsRequestServicesAction() { // Arrange var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); context.HttpContext.RequestServices = new ForwardingServiceProvider(); // Act var result = new AcceptedAtActionResult(actionName: null, controllerName: null, routeValues: null, value: null); result.OnFormatting(context); // Assert var header = context.HttpContext.Response.Headers.Last(); Assert.Equal("Location", header.Key); Assert.Equal("abc", header.Value); } [Fact] public void OnFormatting_NullUrlHelperContextNoRequestServices_ThrowsArgumentNullException() { // Arrange var context = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); // Act var result = new AcceptedAtActionResult(actionName: null, controllerName: null, routeValues: null, value: null); // Assert Assert.Throws("provider", () => result.OnFormatting(context)); } [Fact] public void OnFormatting_NullContext_ThrowsArgumentNullException() { // Act var result = new AcceptedAtActionResult("actionName", "controllerName", "routeValues", "value"); // Assert Assert.Throws("context", () => result.OnFormatting(null)); } private static ActionContext GetActionContext(HttpContext httpContext) { var routeData = new RouteData(); routeData.Routers.Add(Mock.Of()); return new ActionContext( httpContext, routeData, new ActionDescriptor()); } private static HttpContext GetHttpContext(Mock formatter) { var httpContext = new DefaultHttpContext(); httpContext.RequestServices = CreateServices(formatter); return httpContext; } private static Mock CreateMockFormatter() { var formatter = new Mock { CallBase = true }; formatter.Setup(f => f.CanWriteResult(It.IsAny())).Returns(true); return formatter; } private static IServiceProvider CreateServices(Mock formatter) { var options = Options.Create(new MvcOptions()); options.Value.OutputFormatters.Add(formatter.Object); var services = new ServiceCollection(); services.AddSingleton>(new ObjectResultExecutor( new DefaultOutputFormatterSelector(options, NullLoggerFactory.Instance), new TestHttpResponseStreamWriterFactory(), NullLoggerFactory.Instance)); return services.BuildServiceProvider(); } private static IUrlHelper GetMockUrlHelper(string returnValue) { var urlHelper = new Mock(); urlHelper.Setup(o => o.Action(It.IsAny())).Returns(returnValue); return urlHelper.Object; } private class ForwardingServiceProvider : IServiceProvider { public object GetService(Type serviceType) => new ForwardingUrlHelperFactory(); } private class ForwardingUrlHelperFactory : IUrlHelperFactory { public IUrlHelper GetUrlHelper(ActionContext context) => new ForwardingUrlHelper() { ActionValue = "abc" }; } private class ForwardingUrlHelper : IUrlHelper { public string ActionValue { get; set; } public ActionContext ActionContext => null; public string Action(UrlActionContext actionContext) => ActionValue; public string Content(string contentPath) => null; public bool IsLocalUrl(string url) => false; public string Link(string routeName, object values) => null; public string RouteUrl(UrlRouteContext routeContext) => null; } } }