1187 lines
46 KiB
C#
1187 lines
46 KiB
C#
// 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.Buffers;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Internal;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Logging.Testing;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
|
{
|
|
public class ControllerActionInvokerTest : CommonResourceInvokerTest
|
|
{
|
|
[Fact]
|
|
public async Task Invoke_Success_LogsCorrectValues()
|
|
{
|
|
// Arrange
|
|
var sink = new TestSink();
|
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
|
var logger = loggerFactory.CreateLogger<ControllerActionInvoker>();
|
|
|
|
var displayName = "A.B.C";
|
|
var actionDescriptor = Mock.Of<ControllerActionDescriptor>(a => a.DisplayName == displayName);
|
|
actionDescriptor.MethodInfo = typeof(TestController).GetMethod(nameof(TestController.ActionMethod));
|
|
actionDescriptor.ControllerTypeInfo = typeof(TestController).GetTypeInfo();
|
|
actionDescriptor.FilterDescriptors = new List<FilterDescriptor>();
|
|
actionDescriptor.Parameters = new List<ParameterDescriptor>();
|
|
actionDescriptor.BoundProperties = new List<ParameterDescriptor>();
|
|
|
|
var filter = Mock.Of<IFilterMetadata>();
|
|
var invoker = CreateInvoker(
|
|
new[] { filter },
|
|
actionDescriptor,
|
|
controller: new TestController(),
|
|
logger: logger);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.Single(sink.Scopes);
|
|
Assert.Equal(displayName, sink.Scopes[0].Scope?.ToString());
|
|
|
|
Assert.Equal(4, sink.Writes.Count);
|
|
Assert.Equal($"Executing action {displayName}", sink.Writes[0].State?.ToString());
|
|
Assert.Equal($"Executing action method {displayName} with arguments ((null)) - ModelState is Valid", sink.Writes[1].State?.ToString());
|
|
Assert.Equal($"Executed action method {displayName}, returned result Microsoft.AspNetCore.Mvc.ContentResult.", sink.Writes[2].State?.ToString());
|
|
// This message has the execution time embedded, which we don't want to verify.
|
|
Assert.StartsWith($"Executed action {displayName} ", sink.Writes[3].State?.ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Invoke_WritesDiagnostic_ActionSelected()
|
|
{
|
|
// Arrange
|
|
var actionDescriptor = new ControllerActionDescriptor()
|
|
{
|
|
FilterDescriptors = new List<FilterDescriptor>(),
|
|
Parameters = new List<ParameterDescriptor>(),
|
|
BoundProperties = new List<ParameterDescriptor>(),
|
|
};
|
|
|
|
actionDescriptor.MethodInfo = typeof(TestController).GetMethod(nameof(TestController.ActionMethod));
|
|
actionDescriptor.ControllerTypeInfo = typeof(TestController).GetTypeInfo();
|
|
|
|
var listener = new TestDiagnosticListener();
|
|
|
|
var routeData = new RouteData();
|
|
routeData.Values.Add("tag", "value");
|
|
|
|
var filter = Mock.Of<IFilterMetadata>();
|
|
var invoker = CreateInvoker(
|
|
new[] { filter },
|
|
actionDescriptor,
|
|
controller: new TestController(),
|
|
diagnosticListener: listener,
|
|
routeData: routeData);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.NotNull(listener.BeforeAction?.ActionDescriptor);
|
|
Assert.NotNull(listener.BeforeAction?.HttpContext);
|
|
|
|
var routeValues = listener.BeforeAction?.RouteData?.Values;
|
|
Assert.NotNull(routeValues);
|
|
|
|
Assert.Equal(1, routeValues.Count);
|
|
Assert.Contains(routeValues, kvp => kvp.Key == "tag" && string.Equals(kvp.Value, "value"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Invoke_WritesDiagnostic_ActionInvoked()
|
|
{
|
|
// Arrange
|
|
var actionDescriptor = new ControllerActionDescriptor()
|
|
{
|
|
FilterDescriptors = new List<FilterDescriptor>(),
|
|
Parameters = new List<ParameterDescriptor>(),
|
|
BoundProperties = new List<ParameterDescriptor>(),
|
|
};
|
|
|
|
actionDescriptor.MethodInfo = typeof(TestController).GetMethod(nameof(TestController.ActionMethod));
|
|
actionDescriptor.ControllerTypeInfo = typeof(TestController).GetTypeInfo();
|
|
|
|
var listener = new TestDiagnosticListener();
|
|
|
|
var filter = Mock.Of<IFilterMetadata>();
|
|
var invoker = CreateInvoker(
|
|
new[] { filter },
|
|
actionDescriptor,
|
|
controller: new TestController(),
|
|
diagnosticListener: listener);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.NotNull(listener.AfterAction?.ActionDescriptor);
|
|
Assert.NotNull(listener.AfterAction?.HttpContext);
|
|
}
|
|
|
|
|
|
[Fact]
|
|
public async Task AddingValueProviderFactory_AtResourceFilter_IsAvailableInControllerContext()
|
|
{
|
|
// Arrange
|
|
var valueProviderFactory2 = Mock.Of<IValueProviderFactory>();
|
|
var resourceFilter = new Mock<IResourceFilter>();
|
|
resourceFilter
|
|
.Setup(f => f.OnResourceExecuting(It.IsAny<ResourceExecutingContext>()))
|
|
.Callback<ResourceExecutingContext>((resourceExecutingContext) =>
|
|
{
|
|
resourceExecutingContext.ValueProviderFactories.Add(valueProviderFactory2);
|
|
});
|
|
var valueProviderFactory1 = Mock.Of<IValueProviderFactory>();
|
|
var valueProviderFactories = new List<IValueProviderFactory>();
|
|
valueProviderFactories.Add(valueProviderFactory1);
|
|
|
|
var invoker = CreateInvoker(
|
|
new IFilterMetadata[] { resourceFilter.Object }, valueProviderFactories: valueProviderFactories);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var controllerContext = Assert.IsType<ControllerActionInvoker>(invoker).ControllerContext;
|
|
Assert.NotNull(controllerContext);
|
|
Assert.Equal(2, controllerContext.ValueProviderFactories.Count);
|
|
Assert.Same(valueProviderFactory1, controllerContext.ValueProviderFactories[0]);
|
|
Assert.Same(valueProviderFactory2, controllerContext.ValueProviderFactories[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeletingValueProviderFactory_AtResourceFilter_IsNotAvailableInControllerContext()
|
|
{
|
|
// Arrange
|
|
var resourceFilter = new Mock<IResourceFilter>();
|
|
resourceFilter
|
|
.Setup(f => f.OnResourceExecuting(It.IsAny<ResourceExecutingContext>()))
|
|
.Callback<ResourceExecutingContext>((resourceExecutingContext) =>
|
|
{
|
|
resourceExecutingContext.ValueProviderFactories.RemoveAt(0);
|
|
});
|
|
|
|
var valueProviderFactory1 = Mock.Of<IValueProviderFactory>();
|
|
var valueProviderFactory2 = Mock.Of<IValueProviderFactory>();
|
|
var valueProviderFactories = new List<IValueProviderFactory>();
|
|
valueProviderFactories.Add(valueProviderFactory1);
|
|
valueProviderFactories.Add(valueProviderFactory2);
|
|
|
|
var invoker = CreateInvoker(
|
|
new IFilterMetadata[] { resourceFilter.Object }, valueProviderFactories: valueProviderFactories);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var controllerContext = Assert.IsType<ControllerActionInvoker>(invoker).ControllerContext;
|
|
Assert.NotNull(controllerContext);
|
|
Assert.Equal(1, controllerContext.ValueProviderFactories.Count);
|
|
Assert.Same(valueProviderFactory2, controllerContext.ValueProviderFactories[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_TaskReturnType()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.TaskAction), actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<EmptyResult>(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_TaskOfValueReturnType()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.TaskValueTypeAction), actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(inputParam1, contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_WithAsyncKeywordThrows()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.TaskActionWithException), actionParameters);
|
|
|
|
// Act and Assert
|
|
await Assert.ThrowsAsync<NotImplementedException>(
|
|
() => invoker.InvokeAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_WithoutAsyncThrows()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.TaskActionWithExceptionWithoutAsync), actionParameters);
|
|
|
|
// Act and Assert
|
|
await Assert.ThrowsAsync<NotImplementedException>(
|
|
() => invoker.InvokeAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_WithExceptionsAfterAwait()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.TaskActionThrowAfterAwait), actionParameters);
|
|
var expectedException = "Argument Exception";
|
|
|
|
// Act and Assert
|
|
var ex = await Assert.ThrowsAsync<ArgumentException>(
|
|
() => invoker.InvokeAsync());
|
|
Assert.Equal(expectedException, ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncAction()
|
|
{
|
|
// Arrange
|
|
var inputString = "hello";
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.Echo), new Dictionary<string, object>() { { "input", inputString } });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(inputString, contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncAction_WithException()
|
|
{
|
|
// Arrange
|
|
var inputString = "hello";
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.EchoWithException),
|
|
new Dictionary<string, object>() { { "input", inputString } });
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<NotImplementedException>(
|
|
() => invoker.InvokeAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncMethod_WithArgumentDictionary_DefaultValueAttributeUsed()
|
|
{
|
|
// Arrange
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.EchoWithDefaultValue),
|
|
new Dictionary<string, object>());
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal("hello", contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncMethod_WithArgumentArray_DefaultValueAttributeIgnored()
|
|
{
|
|
// Arrange
|
|
var inputString = "test";
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.EchoWithDefaultValue),
|
|
new Dictionary<string, object>() { { "input", inputString } });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(inputString, contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncMethod_WithArgumentDictionary_DefaultParameterValueUsed()
|
|
{
|
|
// Arrange
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.EchoWithDefaultValueAndAttribute),
|
|
new Dictionary<string, object>());
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal("world", contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_SyncMethod_WithArgumentDictionary_AnyValue_HasPrecedenceOverDefaults()
|
|
{
|
|
// Arrange
|
|
var inputString = "test";
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.EchoWithDefaultValueAndAttribute),
|
|
new Dictionary<string, object>() { { "input", inputString } });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(inputString, contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_WithCustomTaskReturnType()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.TaskActionWithCustomTaskReturnType),
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.IsType(typeof(EmptyResult), result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_WithCustomTaskOfTReturnType()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.TaskActionWithCustomTaskOfTReturnType),
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.IsType(typeof(ObjectResult), result);
|
|
Assert.IsType(typeof(int), ((ObjectResult)result).Value);
|
|
Assert.Equal(1, ((ObjectResult)result).Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncAction_ReturningUnwrappedTask()
|
|
{
|
|
// Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
var actionParameters = new Dictionary<string, object> { { "i", inputParam1 }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, nameof(TestController.UnwrappedTask), actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.IsType<EmptyResult>(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncActionWithTaskOfObjectReturnType_AndReturningTaskOfActionResult()
|
|
{
|
|
// Arrange
|
|
var actionParameters = new Dictionary<string, object> { ["value"] = 3 };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result);
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.AsyncActionMethodReturningActionResultWithTaskOfObjectAsReturnType),
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var testResult = Assert.IsType<TestActionResult>(result);
|
|
Assert.Equal(3, testResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_ActionWithObjectReturnType_AndReturningActionResult()
|
|
{
|
|
// Arrange
|
|
var actionParameters = new Dictionary<string, object> { ["value"] = 3 };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result);
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.ActionMethodReturningActionResultWithObjectAsReturnType),
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var testResult = Assert.IsType<TestActionResult>(result);
|
|
Assert.Equal(3, testResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncMethod_ParametersInRandomOrder()
|
|
{
|
|
//Arrange
|
|
var inputParam1 = 1;
|
|
var inputParam2 = "Second Parameter";
|
|
|
|
// Note that the order of parameters is reversed
|
|
var actionParameters = new Dictionary<string, object> { { "s", inputParam2 }, { "i", inputParam1 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.TaskValueTypeAction),
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<ObjectResult>(result);
|
|
Assert.Equal(inputParam1, contentResult.Value);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(nameof(TestController.AsynActionMethodWithTestActionResult))]
|
|
[InlineData(nameof(TestController.ActionMethodWithTestActionResult))]
|
|
public async Task InvokeAction_ReturnTypeAsIActionResult_ReturnsExpected(string methodName)
|
|
{
|
|
//Arrange
|
|
var inputParam = 1;
|
|
var actionParameters = new Dictionary<string, object> { { "value", inputParam } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
methodName,
|
|
actionParameters);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
var contentResult = Assert.IsType<TestActionResult>(result);
|
|
Assert.Equal(inputParam, contentResult.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAction_AsyncMethod_InvalidParameterValueThrows()
|
|
{
|
|
//Arrange
|
|
var inputParam2 = "Second Parameter";
|
|
|
|
var actionParameters = new Dictionary<string, object> { { "i", "Some Invalid Value" }, { "s", inputParam2 } };
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
nameof(TestController.TaskValueTypeAction),
|
|
actionParameters);
|
|
|
|
// Act & Assert
|
|
await Assert.ThrowsAsync<InvalidCastException>(
|
|
() => invoker.InvokeAsync());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(nameof(TestController.ActionMethodWithNullActionResult), typeof(IActionResult))]
|
|
[InlineData(nameof(TestController.TestActionMethodWithNullActionResult), typeof(TestActionResult))]
|
|
[InlineData(nameof(TestController.AsyncActionMethodWithNullActionResult), typeof(IActionResult))]
|
|
[InlineData(nameof(TestController.AsyncActionMethodWithNullTestActionResult), typeof(TestActionResult))]
|
|
[ReplaceCulture]
|
|
public async Task InvokeAction_WithNullActionResultThrows(string methodName, Type resultType)
|
|
{
|
|
// Arrange
|
|
IActionResult result = null;
|
|
|
|
var filter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnActionExecuting(It.IsAny<ActionExecutingContext>())).Verifiable();
|
|
filter
|
|
.Setup(f => f.OnActionExecuted(It.IsAny<ActionExecutedContext>()))
|
|
.Callback<ActionExecutedContext>(c => result = c.Result)
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(
|
|
new[] { filter.Object },
|
|
methodName,
|
|
new Dictionary<string, object>());
|
|
|
|
// Act & Assert
|
|
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
|
|
() => invoker.InvokeAsync(),
|
|
$"Cannot return null from an action method with a return type of '{resultType}'.");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Invoke_UsesDefaultValuesIfNotBound()
|
|
{
|
|
// Arrange
|
|
var actionDescriptor = new ControllerActionDescriptor
|
|
{
|
|
ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
|
|
BoundProperties = new List<ParameterDescriptor>(),
|
|
MethodInfo = typeof(TestController).GetTypeInfo()
|
|
.DeclaredMethods
|
|
.First(m => m.Name.Equals("ActionMethodWithDefaultValues", StringComparison.Ordinal)),
|
|
|
|
Parameters = new List<ParameterDescriptor>
|
|
{
|
|
new ParameterDescriptor
|
|
{
|
|
Name = "value",
|
|
ParameterType = typeof(int),
|
|
BindingInfo = new BindingInfo(),
|
|
}
|
|
},
|
|
FilterDescriptors = new List<FilterDescriptor>()
|
|
};
|
|
|
|
var context = new Mock<HttpContext>();
|
|
context.SetupGet(c => c.Items)
|
|
.Returns(new Dictionary<object, object>());
|
|
context.Setup(c => c.RequestServices.GetService(typeof(ILoggerFactory)))
|
|
.Returns(new NullLoggerFactory());
|
|
|
|
var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor);
|
|
|
|
var controllerContext = new ControllerContext(actionContext)
|
|
{
|
|
ValueProviderFactories = new IValueProviderFactory[0]
|
|
};
|
|
controllerContext.ModelState.MaxAllowedErrors = 200;
|
|
var objectMethodExecutor = ObjectMethodExecutor.Create(
|
|
actionDescriptor.MethodInfo,
|
|
actionDescriptor.ControllerTypeInfo,
|
|
ParameterDefaultValues.GetParameterDefaultValues(actionDescriptor.MethodInfo));
|
|
|
|
var cacheEntry = new ControllerActionInvokerCacheEntry(
|
|
new FilterItem[0],
|
|
_ => new TestController(),
|
|
(_, __) => { },
|
|
(_, __, ___) => Task.CompletedTask,
|
|
actionMethodExecutor: objectMethodExecutor);
|
|
|
|
var invoker = new ControllerActionInvoker(
|
|
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
|
new DiagnosticListener("Microsoft.AspNetCore"),
|
|
controllerContext,
|
|
cacheEntry,
|
|
new IFilterMetadata[0]);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
Assert.Equal(5, context.Object.Items["Result"]);
|
|
}
|
|
|
|
protected override ResourceInvoker CreateInvoker(
|
|
IFilterMetadata[] filters,
|
|
Exception exception = null,
|
|
List<IValueProviderFactory> valueProviderFactories = null)
|
|
{
|
|
var actionDescriptor = new ControllerActionDescriptor()
|
|
{
|
|
ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
|
|
FilterDescriptors = new List<FilterDescriptor>(),
|
|
Parameters = new List<ParameterDescriptor>(),
|
|
BoundProperties = new List<ParameterDescriptor>(),
|
|
};
|
|
|
|
if (exception == Exception)
|
|
{
|
|
actionDescriptor.MethodInfo = typeof(TestController).GetMethod(nameof(TestController.ThrowingActionMethod));
|
|
}
|
|
else if (exception != null)
|
|
{
|
|
throw new InvalidOperationException($"Unexpected exception {exception}.");
|
|
}
|
|
else
|
|
{
|
|
actionDescriptor.MethodInfo = typeof(TestController).GetMethod(nameof(TestController.ActionMethod));
|
|
}
|
|
|
|
return CreateInvoker(
|
|
filters,
|
|
actionDescriptor,
|
|
new TestController(),
|
|
valueProviderFactories: valueProviderFactories);
|
|
}
|
|
|
|
// Used by tests which directly test different types of signatures for controller methods.
|
|
private ControllerActionInvoker CreateInvoker(
|
|
IFilterMetadata[] filters,
|
|
string methodName,
|
|
IDictionary<string, object> arguments)
|
|
{
|
|
var actionDescriptor = new ControllerActionDescriptor()
|
|
{
|
|
ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
|
|
FilterDescriptors = new List<FilterDescriptor>(),
|
|
Parameters = new List<ParameterDescriptor>(),
|
|
BoundProperties = new List<ParameterDescriptor>(),
|
|
};
|
|
|
|
var method = typeof(TestController).GetTypeInfo().GetMethod(methodName);
|
|
Assert.NotNull(method);
|
|
actionDescriptor.MethodInfo = method;
|
|
|
|
foreach (var kvp in arguments)
|
|
{
|
|
actionDescriptor.Parameters.Add(new ControllerParameterDescriptor()
|
|
{
|
|
Name = kvp.Key,
|
|
ParameterInfo = method.GetParameters().Where(p => p.Name == kvp.Key).Single(),
|
|
});
|
|
}
|
|
|
|
return CreateInvoker(filters, actionDescriptor, new TestController(), arguments);
|
|
}
|
|
|
|
private ControllerActionInvoker CreateInvoker(
|
|
IFilterMetadata[] filters,
|
|
ControllerActionDescriptor actionDescriptor,
|
|
object controller,
|
|
IDictionary<string, object> arguments = null,
|
|
IList<IValueProviderFactory> valueProviderFactories = null,
|
|
RouteData routeData = null,
|
|
ILogger logger = null,
|
|
object diagnosticListener = null)
|
|
{
|
|
Assert.NotNull(actionDescriptor.MethodInfo);
|
|
|
|
if (arguments == null)
|
|
{
|
|
arguments = new Dictionary<string, object>();
|
|
}
|
|
|
|
if (valueProviderFactories == null)
|
|
{
|
|
valueProviderFactories = new List<IValueProviderFactory>();
|
|
}
|
|
|
|
if (routeData == null)
|
|
{
|
|
routeData = new RouteData();
|
|
}
|
|
|
|
if (logger == null)
|
|
{
|
|
logger = new NullLoggerFactory().CreateLogger<ControllerActionInvoker>();
|
|
}
|
|
|
|
var httpContext = new DefaultHttpContext();
|
|
var options = new MvcOptions();
|
|
var mvcOptionsAccessor = new TestOptionsManager<MvcOptions>(options);
|
|
|
|
var services = new ServiceCollection();
|
|
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
|
|
services.AddSingleton<IOptions<MvcOptions>>(mvcOptionsAccessor);
|
|
services.AddSingleton(new ObjectResultExecutor(
|
|
mvcOptionsAccessor,
|
|
new TestHttpResponseStreamWriterFactory(),
|
|
NullLoggerFactory.Instance));
|
|
|
|
services.AddSingleton(new ContentResultExecutor(
|
|
NullLogger<ContentResultExecutor>.Instance,
|
|
new MemoryPoolHttpResponseStreamWriterFactory(ArrayPool<byte>.Shared, ArrayPool<char>.Shared)));
|
|
|
|
httpContext.Response.Body = new MemoryStream();
|
|
httpContext.RequestServices = services.BuildServiceProvider();
|
|
|
|
var formatter = new Mock<IOutputFormatter>();
|
|
formatter
|
|
.Setup(f => f.CanWriteResult(It.IsAny<OutputFormatterCanWriteContext>()))
|
|
.Returns(true);
|
|
|
|
formatter
|
|
.Setup(f => f.WriteAsync(It.IsAny<OutputFormatterWriteContext>()))
|
|
.Returns<OutputFormatterWriteContext>(async c =>
|
|
{
|
|
await c.HttpContext.Response.WriteAsync(c.Object.ToString());
|
|
});
|
|
|
|
options.OutputFormatters.Add(formatter.Object);
|
|
|
|
var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
|
|
if (diagnosticListener != null)
|
|
{
|
|
diagnosticSource.SubscribeWithAdapter(diagnosticListener);
|
|
}
|
|
|
|
var objectMethodExecutor = ObjectMethodExecutor.Create(
|
|
actionDescriptor.MethodInfo,
|
|
actionDescriptor.ControllerTypeInfo,
|
|
ParameterDefaultValues.GetParameterDefaultValues(actionDescriptor.MethodInfo));
|
|
|
|
var cacheEntry = new ControllerActionInvokerCacheEntry(
|
|
new FilterItem[0],
|
|
(c) => controller,
|
|
null,
|
|
(_, __, args) =>
|
|
{
|
|
foreach (var item in arguments)
|
|
{
|
|
args[item.Key] = item.Value;
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
},
|
|
objectMethodExecutor);
|
|
|
|
var actionContext = new ActionContext(httpContext, routeData, actionDescriptor);
|
|
var controllerContext = new ControllerContext(actionContext)
|
|
{
|
|
ValueProviderFactories = valueProviderFactories,
|
|
};
|
|
|
|
var invoker = new ControllerActionInvoker(
|
|
logger,
|
|
diagnosticSource,
|
|
controllerContext,
|
|
cacheEntry,
|
|
filters);
|
|
return invoker;
|
|
}
|
|
|
|
public sealed class TestController
|
|
{
|
|
public IActionResult ActionMethod()
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
public ObjectResult ThrowingActionMethod()
|
|
{
|
|
throw Exception;
|
|
}
|
|
|
|
public IActionResult ActionMethodWithDefaultValues(int value = 5)
|
|
{
|
|
return new TestActionResult { Value = value };
|
|
}
|
|
|
|
public TestActionResult ActionMethodWithTestActionResult(int value)
|
|
{
|
|
return new TestActionResult { Value = value };
|
|
}
|
|
|
|
public async Task<TestActionResult> AsynActionMethodWithTestActionResult(int value)
|
|
{
|
|
return await Task.FromResult<TestActionResult>(new TestActionResult { Value = value });
|
|
}
|
|
|
|
public IActionResult ActionMethodWithNullActionResult()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public object ActionMethodReturningActionResultWithObjectAsReturnType(int value = 5)
|
|
{
|
|
return new TestActionResult { Value = value };
|
|
}
|
|
|
|
public async Task<object> AsyncActionMethodReturningActionResultWithTaskOfObjectAsReturnType(int value = 5)
|
|
{
|
|
return await Task.FromResult(new TestActionResult { Value = value });
|
|
}
|
|
|
|
public TestActionResult TestActionMethodWithNullActionResult()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public async Task<IActionResult> AsyncActionMethodWithNullActionResult()
|
|
{
|
|
return await Task.FromResult<IActionResult>(null);
|
|
}
|
|
|
|
public async Task<TestActionResult> AsyncActionMethodWithNullTestActionResult()
|
|
{
|
|
return await Task.FromResult<TestActionResult>(null);
|
|
}
|
|
#pragma warning disable 1998
|
|
public async Task TaskAction(int i, string s)
|
|
{
|
|
return;
|
|
}
|
|
#pragma warning restore 1998
|
|
|
|
#pragma warning disable 1998
|
|
public async Task<int> TaskValueTypeAction(int i, string s)
|
|
{
|
|
return i;
|
|
}
|
|
#pragma warning restore 1998
|
|
|
|
#pragma warning disable 1998
|
|
public async Task<Task<int>> TaskOfTaskAction(int i, string s)
|
|
{
|
|
return TaskValueTypeAction(i, s);
|
|
}
|
|
#pragma warning restore 1998
|
|
|
|
public Task<int> TaskValueTypeActionWithoutAsync(int i, string s)
|
|
{
|
|
return TaskValueTypeAction(i, s);
|
|
}
|
|
|
|
#pragma warning disable 1998
|
|
public async Task<int> TaskActionWithException(int i, string s)
|
|
{
|
|
throw new NotImplementedException("Not Implemented Exception");
|
|
}
|
|
#pragma warning restore 1998
|
|
|
|
public Task<int> TaskActionWithExceptionWithoutAsync(int i, string s)
|
|
{
|
|
throw new NotImplementedException("Not Implemented Exception");
|
|
}
|
|
|
|
public async Task<int> TaskActionThrowAfterAwait(int i, string s)
|
|
{
|
|
await Task.Delay(500);
|
|
throw new ArgumentException("Argument Exception");
|
|
}
|
|
|
|
public TaskDerivedType TaskActionWithCustomTaskReturnType(int i, string s)
|
|
{
|
|
var task = new TaskDerivedType();
|
|
task.Start();
|
|
return task;
|
|
}
|
|
|
|
public TaskOfTDerivedType<int> TaskActionWithCustomTaskOfTReturnType(int i, string s)
|
|
{
|
|
var task = new TaskOfTDerivedType<int>(1);
|
|
task.Start();
|
|
return task;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a <see cref="Task{TResult}"/> instead of a <see cref="Task"/>.
|
|
/// </summary>
|
|
public Task UnwrappedTask(int i, string s)
|
|
{
|
|
return Task.Factory.StartNew(async () => await Task.Factory.StartNew(() => i));
|
|
}
|
|
|
|
public string Echo(string input)
|
|
{
|
|
return input;
|
|
}
|
|
|
|
public string EchoWithException(string input)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string EchoWithDefaultValue([DefaultValue("hello")] string input)
|
|
{
|
|
return input;
|
|
}
|
|
|
|
public string EchoWithDefaultValueAndAttribute([DefaultValue("hello")] string input = "world")
|
|
{
|
|
return input;
|
|
}
|
|
|
|
public class TaskDerivedType : Task
|
|
{
|
|
public TaskDerivedType()
|
|
: base(() => Console.WriteLine("In The Constructor"))
|
|
{
|
|
}
|
|
}
|
|
|
|
public class TaskOfTDerivedType<T> : Task<T>
|
|
{
|
|
public TaskOfTDerivedType(T input)
|
|
: base(() => input)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class TestActionResult : IActionResult
|
|
{
|
|
public int Value { get; set; }
|
|
|
|
public Task ExecuteResultAsync(ActionContext context)
|
|
{
|
|
context.HttpContext.Items["Result"] = Value;
|
|
return Task.FromResult(0);
|
|
}
|
|
}
|
|
|
|
private static ObjectMethodExecutor CreateExecutor(ControllerActionDescriptor actionDescriptor)
|
|
{
|
|
return ObjectMethodExecutor.Create(
|
|
actionDescriptor.MethodInfo,
|
|
actionDescriptor.ControllerTypeInfo,
|
|
ParameterDefaultValues.GetParameterDefaultValues(actionDescriptor.MethodInfo));
|
|
}
|
|
|
|
private class MockAuthorizationFilter : IAuthorizationFilter
|
|
{
|
|
int _expectedMaxAllowedErrors;
|
|
|
|
public MockAuthorizationFilter(int maxAllowedErrors)
|
|
{
|
|
_expectedMaxAllowedErrors = maxAllowedErrors;
|
|
}
|
|
|
|
public void OnAuthorization(AuthorizationFilterContext context)
|
|
{
|
|
Assert.Equal(_expectedMaxAllowedErrors, context.ModelState.MaxAllowedErrors);
|
|
}
|
|
}
|
|
|
|
private class TestParameterBinder : ParameterBinder
|
|
{
|
|
private readonly IDictionary<string, object> _actionParameters;
|
|
public TestParameterBinder(IDictionary<string, object> actionParameters)
|
|
: base(
|
|
new EmptyModelMetadataProvider(),
|
|
TestModelBinderFactory.CreateDefault(),
|
|
Mock.Of<IObjectModelValidator>())
|
|
{
|
|
_actionParameters = actionParameters;
|
|
}
|
|
|
|
public override Task<ModelBindingResult> BindModelAsync(
|
|
ActionContext actionContext,
|
|
IValueProvider valueProvider,
|
|
ParameterDescriptor parameter,
|
|
object value)
|
|
{
|
|
if (_actionParameters.TryGetValue(parameter.Name, out var result))
|
|
{
|
|
return Task.FromResult(ModelBindingResult.Success(result));
|
|
}
|
|
|
|
return Task.FromResult(ModelBindingResult.Failed());
|
|
}
|
|
}
|
|
}
|
|
}
|