// 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.Reflection; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.Core { public class DefaultControllerFactoryTest { [Fact] public void CreateController_ThrowsIfActionDescriptorIsNotControllerActionDescriptor() { // Arrange var expected = "The action descriptor must be of type 'Microsoft.AspNet.Mvc.ControllerActionDescriptor'." + Environment.NewLine + "Parameter name: actionContext"; var actionDescriptor = new ActionDescriptor(); var controllerFactory = new DefaultControllerFactory(Mock.Of()); var httpContext = new DefaultHttpContext(); var actionContext = new ActionContext(httpContext, new RouteData(), actionDescriptor); // Act and Assert var ex = Assert.Throws(() => controllerFactory.CreateController(actionContext)); Assert.Equal(expected, ex.Message); Assert.Equal("actionContext", ex.ParamName); } [Fact] public void CreateController_UsesControllerActivatorToInstantiateController() { // Arrange var expected = new MyController(); var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(MyController).GetTypeInfo() }; var httpContext = new DefaultHttpContext(); httpContext.RequestServices = GetServices(); var actionContext = new ActionContext(httpContext, new RouteData(), actionDescriptor); var activator = new Mock(); activator.Setup(a => a.Create(actionContext, typeof(MyController))) .Returns(expected) .Verifiable(); var controllerFactory = new DefaultControllerFactory(activator.Object); // Act var result = controllerFactory.CreateController(actionContext); // Assert var controller = Assert.IsType(result); Assert.Same(expected, controller); activator.Verify(); } [Fact] public void CreateController_SetsPropertiesFromActionContextHierarchy() { // Arrange var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo() }; var services = GetServices(); var httpContext = new DefaultHttpContext { RequestServices = services }; var context = new ActionContext(httpContext, new RouteData(), actionDescriptor); var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache())); // Act var result = factory.CreateController(context); // Assert var controller = Assert.IsType(result); Assert.Same(context, controller.ActionContext); Assert.Same(httpContext, controller.HttpContext); } [Fact] public void CreateController_SetsViewDataDictionary() { // Arrange var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo() }; var services = GetServices(); var httpContext = new DefaultHttpContext { RequestServices = services }; var context = new ActionContext(httpContext, new RouteData(), actionDescriptor); var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache())); // Act var result = factory.CreateController(context); // Assert var controller = Assert.IsType(result); Assert.NotNull(controller.GetViewData()); } [Fact] public void CreateController_SetsBindingContext() { // Arrange var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo() }; var bindingContext = new ActionBindingContext(); var services = GetServices(); services.GetRequiredService>().Value = bindingContext; var httpContext = new DefaultHttpContext { RequestServices = services }; var context = new ActionContext(httpContext, new RouteData(), actionDescriptor); var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache())); // Act var result = factory.CreateController(context); // Assert var controller = Assert.IsType(result); Assert.Same(bindingContext, controller.BindingContext); } [Fact] public void CreateController_IgnoresPropertiesThatAreNotDecoratedWithActivateAttribute() { // Arrange var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo() }; var services = GetServices(); var httpContext = new DefaultHttpContext { RequestServices = services }; var context = new ActionContext(httpContext, new RouteData(), actionDescriptor); var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache())); // Act var result = factory.CreateController(context); // Assert var controller = Assert.IsType(result); Assert.Null(controller.Response); } [Fact] public void CreateController_ThrowsIfPropertyCannotBeActivated() { // Arrange var actionDescriptor = new ControllerActionDescriptor { ControllerTypeInfo = typeof(ControllerThatCannotBeActivated).GetTypeInfo() }; var services = GetServices(); var httpContext = new DefaultHttpContext { RequestServices = services }; var context = new ActionContext(httpContext, new RouteData(), actionDescriptor); var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache())); // Act and Assert var exception = Assert.Throws(() => factory.CreateController(context)); Assert.Equal("The property 'Service' on controller '" + typeof(ControllerThatCannotBeActivated) + "' cannot be activated.", exception.Message); } [Fact] public void DefaultControllerFactory_DisposesIDisposableController() { // Arrange var factory = new DefaultControllerFactory(Mock.Of()); var controller = new MyController(); // Act + Assert Assert.False(controller.Disposed); factory.ReleaseController(controller); Assert.True(controller.Disposed); } [Fact] public void DefaultControllerFactory_ReleasesNonIDisposableController() { // Arrange var factory = new DefaultControllerFactory(Mock.Of()); var controller = new object(); // Act + Assert (does not throw) factory.ReleaseController(controller); } private IServiceProvider GetServices() { var metadataProvider = new EmptyModelMetadataProvider(); var services = new Mock(); services.Setup(s => s.GetService(typeof(IUrlHelper))) .Returns(Mock.Of()); services.Setup(s => s.GetService(typeof(IModelMetadataProvider))) .Returns(metadataProvider); services.Setup(s => s.GetService(typeof(TestService))) .Returns(new TestService()); services.Setup(s => s.GetService(typeof(IObjectModelValidator))) .Returns(new DefaultObjectValidator(Mock.Of(), metadataProvider)); services .Setup(s => s.GetService(typeof(IScopedInstance))) .Returns(new MockScopedInstance()); services.Setup(s => s.GetService(typeof(ITempDataDictionary))) .Returns(new Mock().Object); return services.Object; } private class ControllerWithActivateAndFromServices { [Activate] public ActionContext ActionContext { get; set; } [Activate] public ActionBindingContext BindingContext { get; set; } [Activate] public HttpContext HttpContext { get; set; } [Activate] protected HttpRequest Request { get; set; } [Activate] private ViewDataDictionary ViewData { get; set; } [FromServices] public IUrlHelper Helper { get; set; } [FromServices] public TestService TestService { get; set; } public HttpResponse Response { get; set; } public ViewDataDictionary GetViewData() { return ViewData; } public HttpRequest GetHttpRequest() { return Request; } } private class MyController : Controller { public bool Disposed { get; set; } protected override void Dispose(bool disposing) { Disposed = true; } } private class ControllerThatCannotBeActivated { [Activate] public TestService Service { get; set; } } private class TestService { } } }