// 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.ComponentModel; using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Authorization; using Xunit; [assembly: ProducesErrorResponseType(typeof(InvalidEnumArgumentException))] namespace Microsoft.AspNetCore.Mvc.ApplicationModels { public class ApiConventionApplicationModelConventionTest { [Fact] public void Apply_DoesNotAddConventionItem_IfNoConventionMatches() { // Arrange var actionModel = GetActionModel(nameof(TestController.NoMatch)); var convention = GetConvention(); // Act convention.Apply(actionModel); // Assert Assert.DoesNotContain(typeof(ApiConventionResult), actionModel.Properties.Keys); } [Fact] public void Apply_AddsConventionItem_IfConventionMatches() { // Arrange var actionModel = GetActionModel(nameof(TestController.Delete)); var convention = GetConvention(); // Act convention.Apply(actionModel); // Assert var value = actionModel.Properties[typeof(ApiConventionResult)]; Assert.NotNull(value); } [Fact] public void Apply_AddsConventionItem_IfActionHasNonConventionBasedFilters() { // Arrange var actionModel = GetActionModel(nameof(TestController.Delete)); actionModel.Filters.Add(new AuthorizeFilter()); var convention = GetConvention(); // Act convention.Apply(actionModel); // Assert var value = actionModel.Properties[typeof(ApiConventionResult)]; Assert.NotNull(value); } [Fact] public void Apply_UsesDefaultErrorType_IfActionHasNoAttributes() { // Arrange var expected = typeof(InvalidFilterCriteriaException); var controller = new ControllerModel(typeof(object).GetTypeInfo(), Array.Empty()); var action = new ActionModel(typeof(object).GetMethods()[0], Array.Empty()) { Controller = controller, }; var convention = GetConvention(expected); // Act convention.Apply(action); // Assert var attribute = GetProperty(action); Assert.Equal(expected, attribute.Type); } [Fact] public void Apply_UsesValueFromProducesErrorResponseTypeAttribute_SpecifiedOnControllerAsssembly() { // Arrange var expected = typeof(InvalidEnumArgumentException); var action = GetActionModel(nameof(TestController.Delete)); var convention = GetConvention(); // Act convention.Apply(action); // Assert var attribute = GetProperty(action); Assert.Equal(expected, attribute.Type); } [Fact] public void Apply_UsesValueFromProducesErrorResponseTypeAttribute_SpecifiedOnController() { // Arrange var expected = typeof(InvalidTimeZoneException); var action = GetActionModel( nameof(TestController.Delete), controllerAttributes: new[] { new ProducesErrorResponseTypeAttribute(expected) }); var convention = GetConvention(); // Act convention.Apply(action); // Assert var attribute = GetProperty(action); Assert.Equal(expected, attribute.Type); } [Fact] public void Apply_UsesValueFromProducesErrorResponseTypeAttribute_SpecifiedOnAction() { // Arrange var expected = typeof(InvalidTimeZoneException); var action = GetActionModel( nameof(TestController.Delete), actionAttributes: new[] { new ProducesErrorResponseTypeAttribute(expected) }, controllerAttributes: new[] { new ProducesErrorResponseTypeAttribute(typeof(Guid)) }); var convention = GetConvention(); // Act convention.Apply(action); // Assert var attribute = GetProperty(action); Assert.Equal(expected, attribute.Type); } [Fact] public void Apply_AllowsVoidsErrorType() { // Arrange var expected = typeof(void); var action = GetActionModel(nameof(TestController.Delete), new[] { new ProducesErrorResponseTypeAttribute(expected) }); var convention = GetConvention(); // Act convention.Apply(action); // Assert var attribute = GetProperty(action); Assert.Equal(expected, attribute.Type); } private ApiConventionApplicationModelConvention GetConvention(Type errorType = null) { errorType = errorType ?? typeof(ProblemDetails); return new ApiConventionApplicationModelConvention(new ProducesErrorResponseTypeAttribute(errorType)); } private static TValue GetProperty(ActionModel action) { return Assert.IsType(action.Properties[typeof(TValue)]); } private static ActionModel GetActionModel( string actionName, object[] actionAttributes = null, object[] controllerAttributes = null) { actionAttributes = actionAttributes ?? Array.Empty(); controllerAttributes = controllerAttributes ?? new[] { new ApiConventionTypeAttribute(typeof(DefaultApiConventions)) }; var controllerModel = new ControllerModel(typeof(TestController).GetTypeInfo(), controllerAttributes); var actionModel = new ActionModel(typeof(TestController).GetMethod(actionName), actionAttributes) { Controller = controllerModel, }; controllerModel.Actions.Add(actionModel); return actionModel; } private class TestController { public IActionResult NoMatch() => null; public IActionResult Delete(int id) => null; } } }