// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Routing; using Microsoft.Framework.Logging; using Microsoft.Framework.Logging.Testing; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc { public class MvcRouteHandlerTests { [Fact] public async Task RouteAsync_Success_LogsCorrectValues() { // Arrange var sink = new TestSink(); var loggerFactory = new TestLoggerFactory(sink, enabled: true); var displayName = "A.B.C"; var actionDescriptor = new Mock(); actionDescriptor.SetupGet(ad => ad.DisplayName) .Returns(displayName); var context = CreateRouteContext(actionDescriptor: actionDescriptor.Object, loggerFactory: loggerFactory); var handler = new MvcRouteHandler(); var expectedMessage = $"Executing action {displayName}"; // Act await handler.RouteAsync(context); // Assert Assert.Single(sink.Scopes); Assert.StartsWith("ActionId: ", sink.Scopes[0].Scope?.ToString()); Assert.Single(sink.Writes); Assert.Equal(expectedMessage, sink.Writes[0].State?.ToString()); } [Fact] public async Task RouteAsync_FailOnNoAction_LogsCorrectValues() { // Arrange var sink = new TestSink(); var loggerFactory = new TestLoggerFactory(sink, enabled: true); var mockActionSelector = new Mock(); mockActionSelector.Setup(a => a.SelectAsync(It.IsAny())) .Returns(Task.FromResult(null)); var context = CreateRouteContext( actionSelector: mockActionSelector.Object, loggerFactory: loggerFactory); var handler = new MvcRouteHandler(); var expectedMessage = "No actions matched the current request."; // Act await handler.RouteAsync(context); // Assert Assert.Empty(sink.Scopes); Assert.Single(sink.Writes); Assert.Equal(expectedMessage, sink.Writes[0].State?.ToString()); } [Fact] public async Task RouteAsync_CreatesNewRouteData() { // Arrange RouteData actionRouteData = null; var invoker = new Mock(); invoker .Setup(i => i.InvokeAsync()) .Returns(Task.FromResult(true)); var invokerFactory = new Mock(); invokerFactory .Setup(f => f.CreateInvoker(It.IsAny())) .Returns((c) => { actionRouteData = c.RouteData; return invoker.Object; }); var initialRouter = Mock.Of(); var context = CreateRouteContext(invokerFactory: invokerFactory.Object); var handler = new MvcRouteHandler(); var originalRouteData = context.RouteData; originalRouteData.Routers.Add(initialRouter); originalRouteData.Values.Add("action", "Index"); // Act await handler.RouteAsync(context); // Assert Assert.NotSame(originalRouteData, context.RouteData); Assert.NotSame(originalRouteData, actionRouteData); Assert.Same(actionRouteData, context.RouteData); // The new routedata is a copy Assert.Equal("Index", context.RouteData.Values["action"]); Assert.Equal(initialRouter, Assert.Single(context.RouteData.Routers)); } [Fact] public async Task RouteAsync_ResetsRouteDataOnException() { // Arrange RouteData actionRouteData = null; var invoker = new Mock(); invoker .Setup(i => i.InvokeAsync()) .Throws(new Exception()); var invokerFactory = new Mock(); invokerFactory .Setup(f => f.CreateInvoker(It.IsAny())) .Returns((c) => { actionRouteData = c.RouteData; c.RouteData.Values.Add("action", "Index"); return invoker.Object; }); var context = CreateRouteContext(invokerFactory: invokerFactory.Object); var handler = new MvcRouteHandler(); var initialRouter = Mock.Of(); var originalRouteData = context.RouteData; originalRouteData.Routers.Add(initialRouter); // Act await Assert.ThrowsAsync(() => handler.RouteAsync(context)); // Assert Assert.Same(originalRouteData, context.RouteData); Assert.NotSame(originalRouteData, actionRouteData); Assert.NotSame(actionRouteData, context.RouteData); // The new routedata is a copy Assert.Null(context.RouteData.Values["action"]); Assert.Equal("Index", actionRouteData.Values["action"]); Assert.Equal(initialRouter, Assert.Single(actionRouteData.Routers)); } private RouteContext CreateRouteContext( ActionDescriptor actionDescriptor = null, IActionSelector actionSelector = null, IActionInvokerFactory invokerFactory = null, ILoggerFactory loggerFactory = null, IOptions optionsAccessor = null) { var mockContextAccessor = new Mock>(); if (actionDescriptor == null) { var mockAction = new Mock(); actionDescriptor = mockAction.Object; } if (actionSelector == null) { var mockActionSelector = new Mock(); mockActionSelector.Setup(a => a.SelectAsync(It.IsAny())) .Returns(Task.FromResult(actionDescriptor)); actionSelector = mockActionSelector.Object; } if (invokerFactory == null) { var mockInvoker = new Mock(); mockInvoker.Setup(i => i.InvokeAsync()) .Returns(Task.FromResult(true)); var mockInvokerFactory = new Mock(); mockInvokerFactory.Setup(f => f.CreateInvoker(It.IsAny())) .Returns(mockInvoker.Object); invokerFactory = mockInvokerFactory.Object; } if (loggerFactory == null) { loggerFactory = NullLoggerFactory.Instance; } if (optionsAccessor == null) { var options = new Mock>(); options.SetupGet(o => o.Options) .Returns(new MvcOptions()); optionsAccessor = options.Object; } var httpContext = new Mock(); httpContext.Setup(h => h.RequestServices.GetService(typeof(IScopedInstance))) .Returns(mockContextAccessor.Object); httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionSelector))) .Returns(actionSelector); httpContext.Setup(h => h.RequestServices.GetService(typeof(IActionInvokerFactory))) .Returns(invokerFactory); httpContext.Setup(h => h.RequestServices.GetService(typeof(ILoggerFactory))) .Returns(loggerFactory); httpContext.Setup(h => h.RequestServices.GetService(typeof(MvcMarkerService))) .Returns(new MvcMarkerService()); httpContext.Setup(h => h.RequestServices.GetService(typeof(IOptions))) .Returns(optionsAccessor); return new RouteContext(httpContext.Object); } } }