715 lines
26 KiB
C#
715 lines
26 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.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using System.Text.Encodings.Web;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using Microsoft.AspNetCore.Mvc.Internal;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
|
using Microsoft.AspNetCore.Mvc.Razor;
|
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
|
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|
{
|
|
public class PageActionInvokerTest
|
|
{
|
|
private readonly DivideByZeroException _pageException = new DivideByZeroException();
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_DoesNotInvokeExceptionFilter_WhenPageDoesNotThrow()
|
|
{
|
|
// Arrange
|
|
var filter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, pageThrows: false);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter.Verify(f => f.OnException(It.IsAny<ExceptionContext>()), Times.Never());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_DoesNotAsyncInvokeExceptionFilter_WhenPageDoesNotThrow()
|
|
{
|
|
// Arrange
|
|
var filter = new Mock<IAsyncExceptionFilter>(MockBehavior.Strict);
|
|
filter
|
|
.Setup(f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()))
|
|
.Returns<ExceptionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, pageThrows: false);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter.Verify(
|
|
f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()),
|
|
Times.Never());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesExceptionFilter_WhenPageThrows()
|
|
{
|
|
// Arrange
|
|
Exception exception = null;
|
|
IActionResult pageAction = null;
|
|
var expected = new Mock<IActionResult>(MockBehavior.Strict);
|
|
expected
|
|
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
|
.Returns(Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filter1 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter1
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Verifiable();
|
|
var filter2 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
exception = context.Exception;
|
|
pageAction = context.Result;
|
|
|
|
// Handle the exception
|
|
context.Result = expected.Object;
|
|
})
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
expected.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
|
filter2.Verify(f => f.OnException(It.IsAny<ExceptionContext>()), Times.Once());
|
|
|
|
Assert.Same(_pageException, exception);
|
|
Assert.Null(pageAction);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAsyncExceptionFilter_WhenPageThrows()
|
|
{
|
|
// Arrange
|
|
Exception exception = null;
|
|
IActionResult pageAction = null;
|
|
var expected = new Mock<IActionResult>(MockBehavior.Strict);
|
|
expected
|
|
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
|
.Returns(Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filter1 = new Mock<IAsyncExceptionFilter>(MockBehavior.Strict);
|
|
filter1
|
|
.Setup(f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()))
|
|
.Returns<ExceptionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
var filter2 = new Mock<IAsyncExceptionFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
exception = context.Exception;
|
|
pageAction = context.Result;
|
|
|
|
// Handle the exception
|
|
context.Result = expected.Object;
|
|
})
|
|
.Returns<ExceptionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
expected.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
|
filter2.Verify(
|
|
f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()),
|
|
Times.Once());
|
|
|
|
Assert.Same(_pageException, exception);
|
|
Assert.Null(pageAction);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesExceptionFilter_ShortCircuit_ExceptionNull()
|
|
{
|
|
// Arrange
|
|
var filter1 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
|
|
var filter2 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
filter2.ToString();
|
|
context.Exception = null;
|
|
})
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter2.Verify(
|
|
f => f.OnException(It.IsAny<ExceptionContext>()),
|
|
Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesExceptionFilter_ShortCircuit_ExceptionHandled()
|
|
{
|
|
// Arrange
|
|
var filter1 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
|
|
var filter2 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
context.ExceptionHandled = true;
|
|
})
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter1.Object, filter2.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter2.Verify(
|
|
f => f.OnException(It.IsAny<ExceptionContext>()),
|
|
Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAsyncExceptionFilter_ShortCircuit_ExceptionNull()
|
|
{
|
|
// Arrange
|
|
var filter1 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
var filter2 = new Mock<IAsyncExceptionFilter>(MockBehavior.Strict);
|
|
|
|
filter2
|
|
.Setup(f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
filter2.ToString();
|
|
context.Exception = null;
|
|
})
|
|
.Returns<ExceptionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filterMetadata = new IFilterMetadata[] { filter1.Object, filter2.Object };
|
|
var invoker = CreateInvoker(filterMetadata, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter2.Verify(
|
|
f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()),
|
|
Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAsyncExceptionFilter_ShortCircuit_ExceptionHandled()
|
|
{
|
|
// Arrange
|
|
var filter1 = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
|
|
var filter2 = new Mock<IAsyncExceptionFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
context.ExceptionHandled = true;
|
|
})
|
|
.Returns<ExceptionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new IFilterMetadata[] { filter1.Object, filter2.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter2.Verify(
|
|
f => f.OnExceptionAsync(It.IsAny<ExceptionContext>()),
|
|
Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesExceptionFilter_UnhandledExceptionIsThrown()
|
|
{
|
|
// Arrange
|
|
var filter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
filter
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object }, pageThrows: true);
|
|
|
|
// Act
|
|
await Assert.ThrowsAsync(_pageException.GetType(), invoker.InvokeAsync);
|
|
|
|
// Assert
|
|
filter.Verify(f => f.OnException(It.IsAny<ExceptionContext>()), Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAuthorizationFilter()
|
|
{
|
|
// Arrange
|
|
var filter = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
filter.Setup(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>())).Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter.Verify(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()), Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAsyncAuthorizationFilter()
|
|
{
|
|
// Arrange
|
|
var filter = new Mock<IAsyncAuthorizationFilter>(MockBehavior.Strict);
|
|
filter
|
|
.Setup(f => f.OnAuthorizationAsync(It.IsAny<AuthorizationFilterContext>()))
|
|
.Returns<AuthorizationFilterContext>(context => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var invoker = CreateInvoker(new[] { filter.Object });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
filter.Verify(
|
|
f => f.OnAuthorizationAsync(It.IsAny<AuthorizationFilterContext>()),
|
|
Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAuthorizationFilter_ShortCircuit()
|
|
{
|
|
// Arrange
|
|
var createCalled = false;
|
|
var challenge = new Mock<IActionResult>(MockBehavior.Strict);
|
|
challenge
|
|
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
|
.Returns(Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filter1 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
filter1
|
|
.Setup(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()))
|
|
.Callback<AuthorizationFilterContext>(c => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filter2 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()))
|
|
.Callback<AuthorizationFilterContext>(c => c.Result = challenge.Object)
|
|
.Verifiable();
|
|
|
|
var filter3 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
|
|
var actionDescriptor = new CompiledPageActionDescriptor()
|
|
{
|
|
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
};
|
|
|
|
var cacheEntry = new PageActionInvokerCacheEntry(
|
|
actionDescriptor,
|
|
null,
|
|
(context, viewContext) => createCalled = true,
|
|
null,
|
|
(context) => null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
new FilterItem[0]);
|
|
var invoker = CreateInvoker(
|
|
new[] { filter1.Object, filter2.Object, filter3.Object },
|
|
actionDescriptor,
|
|
cacheEntry: cacheEntry);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
challenge.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
|
filter1.Verify(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()), Times.Once());
|
|
Assert.False(createCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAsyncAuthorizationFilter_ShortCircuit()
|
|
{
|
|
// Arrange
|
|
var createCalled = false;
|
|
var challenge = new Mock<IActionResult>(MockBehavior.Strict);
|
|
challenge
|
|
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
|
.Returns(Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var filter1 = new Mock<IAsyncAuthorizationFilter>(MockBehavior.Strict);
|
|
filter1
|
|
.Setup(f => f.OnAuthorizationAsync(It.IsAny<AuthorizationFilterContext>()))
|
|
.Returns<AuthorizationFilterContext>((context) =>
|
|
{
|
|
return Task.FromResult(true);
|
|
})
|
|
.Verifiable();
|
|
|
|
var filter2 = new Mock<IAsyncAuthorizationFilter>(MockBehavior.Strict);
|
|
filter2
|
|
.Setup(f => f.OnAuthorizationAsync(It.IsAny<AuthorizationFilterContext>()))
|
|
.Returns<AuthorizationFilterContext>((context) =>
|
|
{
|
|
context.Result = challenge.Object;
|
|
return Task.FromResult(true);
|
|
});
|
|
|
|
var filter3 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
|
|
var actionDescriptor = new CompiledPageActionDescriptor()
|
|
{
|
|
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
};
|
|
|
|
var cacheEntry = new PageActionInvokerCacheEntry(
|
|
actionDescriptor,
|
|
null,
|
|
(context, viewContext) => createCalled = true,
|
|
null,
|
|
(context) => null,
|
|
null,
|
|
null,
|
|
null,
|
|
null,
|
|
new FilterItem[0]);
|
|
var invoker = CreateInvoker(
|
|
new IFilterMetadata[] { filter1.Object, filter2.Object, filter3.Object },
|
|
actionDescriptor,
|
|
cacheEntry: cacheEntry);
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
challenge.Verify(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
|
filter1.Verify(
|
|
f => f.OnAuthorizationAsync(It.IsAny<AuthorizationFilterContext>()),
|
|
Times.Once());
|
|
|
|
Assert.False(createCalled);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_ExceptionInAuthorizationFilter_CannotBeHandledByOtherFilters()
|
|
{
|
|
// Arrange
|
|
var expected = new InvalidCastException();
|
|
|
|
var exceptionFilter = new Mock<IExceptionFilter>(MockBehavior.Strict);
|
|
exceptionFilter
|
|
.Setup(f => f.OnException(It.IsAny<ExceptionContext>()))
|
|
.Callback<ExceptionContext>(context =>
|
|
{
|
|
// Mark as handled
|
|
context.Result = new EmptyResult();
|
|
})
|
|
.Verifiable();
|
|
|
|
var authorizationFilter1 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
authorizationFilter1
|
|
.Setup(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()))
|
|
.Callback<AuthorizationFilterContext>(c => { throw expected; })
|
|
.Verifiable();
|
|
|
|
// None of these filters should run
|
|
var authorizationFilter2 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
var resourceFilter = new Mock<IResourceFilter>(MockBehavior.Strict);
|
|
var actionFilter = new Mock<IActionFilter>(MockBehavior.Strict);
|
|
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
|
|
|
var invoker = CreateInvoker(new IFilterMetadata[]
|
|
{
|
|
exceptionFilter.Object,
|
|
authorizationFilter1.Object,
|
|
authorizationFilter2.Object,
|
|
resourceFilter.Object,
|
|
actionFilter.Object,
|
|
resultFilter.Object,
|
|
});
|
|
|
|
// Act
|
|
var thrown = await Assert.ThrowsAsync<InvalidCastException>(invoker.InvokeAsync);
|
|
|
|
// Assert
|
|
Assert.Same(expected, thrown);
|
|
exceptionFilter.Verify(f => f.OnException(It.IsAny<ExceptionContext>()), Times.Never());
|
|
authorizationFilter1.Verify(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()), Times.Once());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvokeAsync_InvokesAuthorizationFilter_ChallengeNotSeenByResultFilters()
|
|
{
|
|
// Arrange
|
|
var challenge = new Mock<IActionResult>(MockBehavior.Strict);
|
|
challenge
|
|
.Setup(r => r.ExecuteResultAsync(It.IsAny<ActionContext>()))
|
|
.Returns<ActionContext>((context) => Task.FromResult(true))
|
|
.Verifiable();
|
|
|
|
var authorizationFilter = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
|
authorizationFilter
|
|
.Setup(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()))
|
|
.Callback<AuthorizationFilterContext>(c => c.Result = challenge.Object)
|
|
.Verifiable();
|
|
|
|
var resultFilter = new Mock<IResultFilter>(MockBehavior.Strict);
|
|
|
|
var invoker = CreateInvoker(new IFilterMetadata[] { authorizationFilter.Object, resultFilter.Object });
|
|
|
|
// Act
|
|
await invoker.InvokeAsync();
|
|
|
|
// Assert
|
|
authorizationFilter.Verify(f => f.OnAuthorization(It.IsAny<AuthorizationFilterContext>()), Times.Once());
|
|
challenge.Verify(c => c.ExecuteResultAsync(It.IsAny<ActionContext>()), Times.Once());
|
|
}
|
|
|
|
private PageActionInvoker CreateInvoker(
|
|
IFilterMetadata[] filters,
|
|
bool pageThrows = false,
|
|
int maxAllowedErrorsInModelState = 200,
|
|
List<IValueProviderFactory> valueProviderFactories = null)
|
|
{
|
|
Func<PageContext, Task> executeAction;
|
|
if (pageThrows)
|
|
{
|
|
executeAction = _ => { throw _pageException; };
|
|
}
|
|
else
|
|
{
|
|
executeAction = context => context.HttpContext.Response.WriteAsync("Hello");
|
|
}
|
|
var executor = new TestPageResultExecutor(executeAction);
|
|
var actionDescriptor = new CompiledPageActionDescriptor
|
|
{
|
|
ViewEnginePath = "/Index.cshtml",
|
|
RelativePath = "/Index.cshtml",
|
|
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
|
};
|
|
|
|
return CreateInvoker(
|
|
filters,
|
|
actionDescriptor,
|
|
executor);
|
|
}
|
|
|
|
private PageActionInvoker CreateInvoker(
|
|
IFilterMetadata[] filters,
|
|
CompiledPageActionDescriptor actionDescriptor,
|
|
PageResultExecutor executor = null,
|
|
IPageHandlerMethodSelector selector = null,
|
|
PageActionInvokerCacheEntry cacheEntry = null,
|
|
ITempDataDictionaryFactory tempDataFactory = null,
|
|
int maxAllowedErrorsInModelState = 200,
|
|
List<IValueProviderFactory> valueProviderFactories = null,
|
|
RouteData routeData = null,
|
|
ILogger logger = null)
|
|
{
|
|
var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
|
|
|
|
var httpContext = new DefaultHttpContext();
|
|
var serviceCollection = new ServiceCollection();
|
|
if (executor == null)
|
|
{
|
|
executor = new PageResultExecutor(
|
|
Mock.Of<IHttpResponseStreamWriterFactory>(),
|
|
Mock.Of<ICompositeViewEngine>(),
|
|
Mock.Of<IRazorViewEngine>(),
|
|
Mock.Of<IRazorPageActivator>(),
|
|
diagnosticSource,
|
|
HtmlEncoder.Default);
|
|
}
|
|
|
|
serviceCollection.AddSingleton(executor ?? executor);
|
|
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
|
|
|
if (routeData == null)
|
|
{
|
|
routeData = new RouteData();
|
|
}
|
|
|
|
var actionContext = new ActionContext(
|
|
httpContext: httpContext,
|
|
routeData: routeData,
|
|
actionDescriptor: actionDescriptor);
|
|
var pageContext = new PageContext(actionContext)
|
|
{
|
|
ActionDescriptor = actionDescriptor,
|
|
};
|
|
|
|
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(actionDescriptor.ModelTypeInfo);
|
|
pageContext.ViewData = viewDataFactory(new EmptyModelMetadataProvider(), pageContext.ModelState);
|
|
|
|
if (selector == null)
|
|
{
|
|
selector = Mock.Of<IPageHandlerMethodSelector>();
|
|
}
|
|
|
|
if (valueProviderFactories == null)
|
|
{
|
|
valueProviderFactories = new List<IValueProviderFactory>();
|
|
}
|
|
|
|
if (logger == null)
|
|
{
|
|
logger = NullLogger.Instance;
|
|
}
|
|
|
|
if (tempDataFactory == null)
|
|
{
|
|
tempDataFactory = Mock.Of<ITempDataDictionaryFactory>(m => m.GetTempData(It.IsAny<HttpContext>()) == Mock.Of<ITempDataDictionary>());
|
|
}
|
|
|
|
Func<PageContext, ViewContext, object> pageFactory = (context, viewContext) =>
|
|
{
|
|
var instance = (Page)Activator.CreateInstance(actionDescriptor.PageTypeInfo.AsType());
|
|
instance.PageContext = context;
|
|
return instance;
|
|
};
|
|
|
|
cacheEntry = new PageActionInvokerCacheEntry(
|
|
actionDescriptor,
|
|
viewDataFactory,
|
|
pageFactory,
|
|
(c, viewContext, page) => { (page as IDisposable)?.Dispose(); },
|
|
_ => Activator.CreateInstance(actionDescriptor.ModelTypeInfo.AsType()),
|
|
(c, model) => { (model as IDisposable)?.Dispose(); },
|
|
null,
|
|
null,
|
|
null,
|
|
new FilterItem[0]);
|
|
|
|
var invoker = new PageActionInvoker(
|
|
selector,
|
|
diagnosticSource,
|
|
logger,
|
|
pageContext,
|
|
filters,
|
|
valueProviderFactories.AsReadOnly(),
|
|
cacheEntry,
|
|
GetParameterBinder(),
|
|
tempDataFactory,
|
|
new HtmlHelperOptions());
|
|
return invoker;
|
|
}
|
|
|
|
private static ParameterBinder GetParameterBinder(
|
|
IModelBinderFactory factory = null,
|
|
IObjectModelValidator validator = null)
|
|
{
|
|
if (validator == null)
|
|
{
|
|
validator = CreateMockValidator();
|
|
}
|
|
|
|
if (factory == null)
|
|
{
|
|
factory = TestModelBinderFactory.CreateDefault();
|
|
}
|
|
|
|
return new ParameterBinder(
|
|
TestModelMetadataProvider.CreateDefaultProvider(),
|
|
factory,
|
|
validator);
|
|
}
|
|
|
|
private static IObjectModelValidator CreateMockValidator()
|
|
{
|
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
|
mockValidator
|
|
.Setup(o => o.Validate(
|
|
It.IsAny<ActionContext>(),
|
|
It.IsAny<ValidationStateDictionary>(),
|
|
It.IsAny<string>(),
|
|
It.IsAny<object>()));
|
|
return mockValidator.Object;
|
|
}
|
|
|
|
private class TestPageResultExecutor : PageResultExecutor
|
|
{
|
|
private readonly Func<PageContext, Task> _executeAction;
|
|
|
|
public TestPageResultExecutor(Func<PageContext, Task> executeAction)
|
|
: base(
|
|
Mock.Of<IHttpResponseStreamWriterFactory>(),
|
|
Mock.Of<ICompositeViewEngine>(),
|
|
Mock.Of<IRazorViewEngine>(),
|
|
Mock.Of<IRazorPageActivator>(),
|
|
new DiagnosticListener("Microsoft.AspNetCore"),
|
|
HtmlEncoder.Default)
|
|
{
|
|
_executeAction = executeAction;
|
|
}
|
|
|
|
public override Task ExecuteAsync(PageContext pageContext, PageResult result)
|
|
=> _executeAction(pageContext);
|
|
}
|
|
|
|
private class TestPage : Page
|
|
{
|
|
public override Task ExecuteAsync()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
}
|