Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first pass full MVC pipeline #58

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Settings.StyleCop
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<StyleCopSettings Version="105">
<GlobalSettings>
<BooleanProperty Name="RulesEnabledByDefault">False</BooleanProperty>
</GlobalSettings>
</StyleCopSettings>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Web.Mvc;
using NUnit.Framework;
using TestStack.FluentMVCTesting.MvcPipeline;
using TestStack.FluentMVCTesting.Tests.TestControllers;

namespace TestStack.FluentMVCTesting.Tests.MvcPipeline
{
[TestFixture]
class PipelineExecutorTests
{
[Test]
public void should_call_filters()
{
var controller = new ControllerExtensionsController();
GlobalFilters.Filters.Add(new DummyFilter());
var executor = new PipelineActionExecutor();

var result = executor.Execute(controller, c => c.SomeAction());

Assert.That(DummyFilter.LoggingFilterWasCalled);
}

private class DummyFilter : ActionFilterAttribute, IActionFilter
{
public static bool LoggingFilterWasCalled { get; set; }

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
LoggingFilterWasCalled = true;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AsyncControllerTests.cs" />
<Compile Include="MvcPipeline\PipelineExecutorTests.cs" />
<Compile Include="ControllerResultTestTests\ShouldGiveHttpStatusTests.cs" />
<Compile Include="ControllerResultTestTests\ShouldRedirectToTests.cs" />
<Compile Include="ControllerResultTestTests\ShouldRenderFileTests.cs" />
Expand Down
16 changes: 5 additions & 11 deletions TestStack.FluentMvcTesting/ControllerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace TestStack.FluentMVCTesting
{
public static class ControllerExtensions
{

public static T WithModelErrors<T>(this T controller) where T : Controller
{
controller.ModelState.AddModelError("Key", "Value");
Expand All @@ -18,22 +17,17 @@ public static ControllerResultTest<T> WithCallTo<T, TAction>(this T controller,
where T : Controller
where TAction : ActionResult
{
var actionName = ((MethodCallExpression)actionCall.Body).Method.Name;

var actionResult = actionCall.Compile().Invoke(controller);

return new ControllerResultTest<T>(controller, actionName, actionResult);
//var actionExecutor = new PipelineActionExecutor();
var actionExecutor = new SimpleActionExecutor();
return actionExecutor.Execute(controller, actionCall);
}

public static ControllerResultTest<T> WithCallTo<T, TAction>(this T controller, Expression<Func<T, Task<TAction>>> actionCall)
where T : Controller
where TAction : ActionResult
{
var actionName = ((MethodCallExpression)actionCall.Body).Method.Name;

var actionResult = actionCall.Compile().Invoke(controller).Result;

return new ControllerResultTest<T>(controller, actionName, actionResult);
var actionExecutor = new SimpleActionExecutor();
return actionExecutor.Execute(controller, actionCall);
}

public static ControllerResultTest<T> WithCallToChild<T, TAction>(this T controller, Expression<Func<T, TAction>> actionCall)
Expand Down
18 changes: 18 additions & 0 deletions TestStack.FluentMvcTesting/IActionExecutor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace TestStack.FluentMVCTesting
{
public interface IActionExecutor
{
ControllerResultTest<TController> Execute<TController, TAction>(TController controller, Expression<Func<TController, TAction>> actionCall)
where TController : Controller
where TAction : ActionResult;

ControllerResultTest<TController> Execute<TController, TAction>(TController controller, Expression<Func<TController, Task<TAction>>> actionCall)
where TController : Controller
where TAction : ActionResult;
}
}
10 changes: 10 additions & 0 deletions TestStack.FluentMvcTesting/MvcPipeline/ActionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Web.Mvc;

namespace TestStack.FluentMVCTesting.MvcPipeline
{
public class ActionContext
{
public ControllerContext ControllerContext { get; set; }
public ActionDescriptor ActionDescriptor { get; set; }
}
}
103 changes: 103 additions & 0 deletions TestStack.FluentMvcTesting/MvcPipeline/ActionRequestWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using System.Web;

namespace TestStack.FluentMVCTesting.MvcPipeline
{
internal class ActionRequestWrapper : HttpWorkerRequest
{
private readonly SimpleHttpRequest _httpRequest;

public ActionRequestWrapper(SimpleHttpRequest httpRequest)
{
this._httpRequest = httpRequest;
}

public override string GetUriPath()
{
var path = this._httpRequest.UriPath;
if (path.StartsWith("~"))
path = path.Substring(1);
return path;
}

public override string GetQueryString()
{
return String.Empty;
}

public override string GetRawUrl()
{
return this.GetUriPath();
}

public override string GetHttpVerbName()
{
return this._httpRequest.HttpMethod;
}

public override string GetHttpVersion()
{
return "HTTP/1.1";
}

public override string GetRemoteAddress()
{
return "localhost";
}

public override int GetRemotePort()
{
throw new NotImplementedException();
}

public override string GetLocalAddress()
{
return "127.0.0.1";
}

public override int GetLocalPort()
{
return 80;
}

public override void SendStatus(int statusCode, string statusDescription)
{
throw new NotImplementedException();
}

public override void SendKnownResponseHeader(int index, string value)
{
throw new NotImplementedException();
}

public override void SendUnknownResponseHeader(string name, string value)
{
throw new NotImplementedException();
}

public override void SendResponseFromMemory(byte[] data, int length)
{
throw new NotImplementedException();
}

public override void SendResponseFromFile(string filename, long offset, long length)
{
throw new NotImplementedException();
}

public override void SendResponseFromFile(IntPtr handle, long offset, long length)
{
throw new NotImplementedException();
}

public override void FlushResponse(bool finalFlush)
{
throw new NotImplementedException();
}

public override void EndOfRequest()
{
throw new NotImplementedException();
}
}
}
107 changes: 107 additions & 0 deletions TestStack.FluentMvcTesting/MvcPipeline/ControllerActionBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;

namespace TestStack.FluentMVCTesting.MvcPipeline
{
public abstract class ControllerActionBase
{
protected ControllerActionBase()
{
FilterProviders = new FilterProviderCollection(System.Web.Mvc.FilterProviders.Providers);
Cookies = new Collection<HttpCookie>();
Session = new Dictionary<string, object>();
Files = new Dictionary<string, Stream>();
Resolve = DependencyResolver.Current.GetService;
}

public FilterProviderCollection FilterProviders { get; private set; }

public IPrincipal User { get; set; }

public IValueProvider ValueProvider { get; set; }

public ICollection<HttpCookie> Cookies { get; private set; }
public IDictionary<string, object> Session { get; private set; }
public IDictionary<string, Stream> Files { get; private set; }
public IDictionary<string, string> Form { get; private set; }
public Func<Type, object> Resolve { get; set; }

public string HttpMethod { get; set; }

public string UriPath { get; set; }

public abstract ActionContext GetActionContext(HttpContextBase httpContext = null);

public virtual ControllerActionResult Execute(HttpContextBase httpContext = null)
{
var actionContext = GetActionContext(httpContext ?? CreateHttpContext());
return Execute(actionContext.ControllerContext, actionContext.ActionDescriptor);
}

protected virtual ControllerActionResult Execute(ControllerContext controllerContext, System.Web.Mvc.ActionDescriptor actionDescriptor)
{
var invoker = GetActionInvoker(controllerContext, actionDescriptor);
return new ControllerActionResult
{
ControllerContext = controllerContext,
ActionResult = invoker.InvokeAction()
};
}

internal virtual MvcActionInvoker GetActionInvoker(ControllerContext controllerContext,
System.Web.Mvc.ActionDescriptor actionDescriptor)
{
var filters = FilterProviders.GetFilters(controllerContext, actionDescriptor).Select(BuildUp);

return new MvcActionInvoker(controllerContext, actionDescriptor, filters);
}

protected virtual Filter BuildUp(Filter filter)
{
if (Resolve == null)
return filter;

foreach (PropertyDescriptor propertyDesc in TypeDescriptor.GetProperties(filter.Instance))
{
var typeNames = propertyDesc.Attributes.OfType<Attribute>().Select(e => e.GetType().ToString());
if (typeNames.Contains("Microsoft.Practices.Unity.DependencyAttribute"))
{
var service = Resolve(propertyDesc.PropertyType);
if (service != null)
propertyDesc.SetValue(filter.Instance, service);
}
}

return filter;
}

public virtual ActionResult Authorize()
{
var actionContext = GetActionContext(CreateHttpContext());
return Authorize(actionContext.ControllerContext, actionContext.ActionDescriptor);
}

protected virtual ActionResult Authorize(ControllerContext controllerContext, System.Web.Mvc.ActionDescriptor actionDescriptor)
{
return GetActionInvoker(controllerContext, actionDescriptor).AuthorizeAction();
}

public abstract HttpContextBase CreateHttpContext();
}

[Serializable]
public class ControllerActionException : Exception
{
public ControllerActionException(string message)
: base(message)
{
}
}
}
46 changes: 46 additions & 0 deletions TestStack.FluentMvcTesting/MvcPipeline/ControllerActionResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Web;
using System.Web.Mvc;

namespace TestStack.FluentMVCTesting.MvcPipeline
{
public class ControllerActionResult
{
public ControllerContext ControllerContext { get; set; }
public ActionResult ActionResult { get; set; }

public dynamic ViewBag
{
get { return ControllerContext.Controller.ViewBag; }
}

public ViewDataDictionary ViewData
{
get { return ControllerContext.Controller.ViewData; }
}

public ModelStateDictionary ModelState
{
get { return ControllerContext.Controller.ViewData.ModelState; }
}

public HttpResponseBase Response
{
get { return ControllerContext.HttpContext.Response; }
}

public ControllerBase Controller
{
get { return ControllerContext.Controller; }
}

public HttpRequestBase Request
{
get { return ControllerContext.HttpContext.Request; }
}

public void ExecuteResult()
{
ActionResult.ExecuteResult(ControllerContext);
}
}
}
Loading