Update of /cvsroot/mavnet/Maverick.net/tests/src/Maverick/Util.Test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20418/src/Maverick/Util.Test
Added Files:
MockTest.cs
Log Message:
add test that creates a mock request for testing controllers from unit tests
--- NEW FILE: MockTest.cs ---
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Hosting;
using Maverick.Ctl;
using Maverick.Flow;
using NUnit.Framework;
namespace Maverick.src.Maverick.Util.Test {
/// <summary>
/// Summary description for MockTest.
/// </summary>
public class MockTest {
[Test]
public void MockRequest() {
HttpWorkerRequest wr = new MockHttpWorkerRequest();
HttpContext context = new HttpContext(wr);
Assert.AreEqual(2, context.Request.Form.Keys.Count);
context.Request.Cookies.Add(new HttpCookie("abc", "123"));
Assert.AreEqual(1, context.Request.Cookies.Count);
//context.Request.Form = new NameValueCollection();
//context.Request.Form.Add("abc", "123");
Dispatcher dispatcher = new Dispatcher();
MaverickContext mctx = new MaverickContext(dispatcher, context);
context.Items.Add(Dispatcher.MAVERICK_CONTEXT_KEY, mctx);
TestController controller = new TestController();
String result = controller.Go(mctx);
Assert.AreEqual("success", result);
Assert.AreEqual(1, context.Response.Cookies.Count);
Assert.AreEqual("bar123", context.Response.Cookies["Test"]["Foo"]);
Assert.AreEqual("bar", ((TestController)mctx.Model).Foo);
Assert.AreEqual(typeof(TestController), mctx.Model.GetType());
}
}
public class TestController : MockController {
private String foo = null;
public String Foo {
get { return foo; }
set { this.foo = value; }
}
public override string Perform() {
HttpCookie cookie = GetCookie("Test");
cookie.Values["Foo"] = Foo + Request.Cookies["abc"].Value;
Response.Cookies.Add(cookie);
return SUCCESS;
}
}
public class MockController : Throwaway {
private ArrayList properties = new ArrayList();
public HttpResponse Response {
get { return this.ControllerContext.HttpContext.Response; }
}
public HttpRequest Request {
get { return this.ControllerContext.HttpContext.Request; }
}
public HttpContext Context {
get { return this.ControllerContext.HttpContext; }
}
public HttpServerUtility Server {
get { return this.ControllerContext.HttpContext.Server; }
}
/// <summary>
/// This is the method you should override to implement application logic.
/// Default implementation just returns "success".
/// </summary>
/// <returns></returns>
public virtual string Perform() {
return SUCCESS;
}
public override sealed string Go() {
Populate(this, this.ControllerContext.HttpContext.Request.Params);
this.ControllerContext.Model = this;
return Perform();
}
public void Populate(Object target, NameValueCollection data) {
if (data == null) return;
// TODO: this seems like it is backwards in that the controller properties should be reflected and then check
// for values in the various scopes in a priority order.
foreach (string key in data) {
this.Populate(target, key, data[key]);
}
// if the ControllerContext params collection exists and has anything in it, it was should be
// mapped to controller properties. If a forward is used, what used to be passed along in the query string
// could be here.
if (this.ControllerContext != null && this.ControllerContext.Params != null && this.ControllerContext.Params.Count > 0) {
foreach(String key in this.ControllerContext.Params.Keys) {
this.Populate(target, key, this.ControllerContext.Params[key].ToString());
}
}
// // check for required properties
// PropertyInfo[] pis = target.GetType().GetProperties();
// foreach(PropertyInfo property in pis) {
// Boolean required = property.GetCustomAttributes(typeof(RequiredAttribute), true).Length == 1;
// if (required && !properties.Contains(property)) {
// HandleMissingRequiredProperty(property);
// }
// }
}
/// <summary>
/// Sets the property specified by name to the value val on
/// the object target.
/// </summary>
/// <param name="target">The instance to set the property on.</param>
/// <param name="name">The name of the property to set.</param>
/// <param name="val">The new value for the property.</param>
protected void Populate(object target, string name, string val) {
// only process the value of the name is not null
if (name!=null) {
string firstChar = name.Substring(0, 1);
firstChar = firstChar.ToUpper();
string propertyName = firstChar + name.Substring(1);
try {
Type t = target.GetType();
PropertyInfo property = t.GetProperty(propertyName);
properties.Add(property);
SetValue(target, property, val);
} catch (Exception) {
//log.Warn("Unable to populate property \"" + propertyName + "\" in: " + target.GetType().FullName, ex);
}
}
}
/// <summary>
/// Sets the value of property in the object target to the value val.
/// </summary>
/// <param name="target">The instance to set the property on.</param>
/// <param name="property">The property to set.</param>
/// <param name="val">The new value for the property.</param>
protected void SetValue(object target, PropertyInfo property, string val) {
if (property != null) {
Type ptype = property.PropertyType;
// Boolean required = property.GetCustomAttributes(typeof(RequiredAttribute), true).Length == 1;
// if (required && String.Empty.Equals(val)) {
// HandleMissingRequiredProperty(property);
// } else {
try {
Object o = ParseValue(val, ptype);
property.SetValue(target, o, null);
} catch (FormatException) {
HandleParseException(property, val);
} catch (ArgumentException) {
// do nothing, this happens because a property is not mapped to the request value
}
// }
}
}
/// <summary>
/// Attempts to parse the given string as one of the standard
/// value types: bool, decimal, sbyte, byte, short, ushort,
/// int, uint, long, ulong, char, float, or double.
/// Simply returns val on failure.
/// </summary>
/// <param name="val">The string containing the value to be parsed.</param>
/// <param name="type">The target type of val after parsing.</param>
/// <returns>
/// A new object representing the string val parsed as the
/// appropriate type or val if type is not one of the standard
/// value types.
/// </returns>
protected object ParseValue(string val, Type type) {
// allow for other types not specified here to be handled
Object o = ParseCustomValue(val, type);
if (o != null) {
return o;
}
// check for .Net types
if (typeof(bool).Equals(type)) {
return bool.Parse(val);
} else if (typeof(decimal).Equals(type)) {
return decimal.Parse(val);
} else if (typeof(sbyte).Equals(type)) {
return sbyte.Parse(val);
} else if (typeof(byte).Equals(type)) {
return byte.Parse(val);
} else if (typeof(short).Equals(type)) {
return short.Parse(val);
} else if (typeof(ushort).Equals(type)) {
return ushort.Parse(val);
} else if (typeof(int).Equals(type)) {
return int.Parse(val);
} else if (typeof(uint).Equals(type)) {
return uint.Parse(val);
} else if (typeof(long).Equals(type)) {
return long.Parse(val);
} else if (typeof(ulong).Equals(type)) {
return ulong.Parse(val);
} else if (typeof(char).Equals(type)) {
return char.Parse(val);
} else if (typeof(float).Equals(type)) {
return float.Parse(val);
} else if (typeof(double).Equals(type)) {
return double.Parse(val);
}
return val;
}
/// <summary>
/// Extendeds types that ParseValue can parse
/// </summary>
/// <param name="val" type="string"></param>
/// <param name="type" type="System.Type"></param>
/// <returns>The parsed value, or null indicating that this type was not parsable by this method</returns>
protected virtual object ParseCustomValue(String val, Type type) {
// not one of the known types, return null
return null;
}
protected object InvokeMethod(Type type, string methodName, Type[] types, object[] values){
if(types.Length != values.Length){
throw new ArgumentException("The parameters types and values must correspond. The lengths of the arrays do not match.");
}
MethodInfo method = type.GetMethod(methodName, types);
if(null == method) {
return null;
}
object instance = null;
if(!method.IsStatic) {
instance = Activator.CreateInstance(type);
}
// If the object supports IDisposable, then we'll dispose of it after method invocation.
IDisposable disposable = instance as IDisposable;
try {
return method.Invoke(instance, values);
}
finally {
if(null != disposable) {
disposable.Dispose();
}
}
}
/// <summary>
/// Handle parsing exceptions
/// </summary>
protected virtual void HandleParseException(PropertyInfo property, object val) {
}
/// <summary>
/// Handle parsing exceptions
/// </summary>
protected virtual void HandleMissingRequiredProperty(PropertyInfo property) {
}
/// <summary>
/// Get a cookie for getting data.
/// </summary>
/// <param name="cookieName"></param>
/// <returns></returns>
public HttpCookie GetCookie(String cookieName) {
HttpCookie returnCookie = ControllerContext.HttpContext.Request.Cookies[cookieName];
if (returnCookie == null) {
returnCookie = new HttpCookie(cookieName);
}
return returnCookie;
}
/// <summary>
/// Persists a cookie. This can only be expected to work once for each cookie between post.
/// </summary>
/// <param name="cookie"></param>
public void UpdateCookie(HttpCookie cookie) {
//cookie.Path = controllerContext.HttpContext.Request.ApplicationPath;
// TODO: set explicitly to / for problems with enrollment process
cookie.Path = "/";
ControllerContext.HttpContext.Response.Cookies.Add(cookie);
}
}
public class MockHttpWorkerRequest : HttpWorkerRequest {
public override string GetUriPath() {
return "/localhost/";
}
public override string GetQueryString() {
return "foo=bar";
}
public override string GetRawUrl() {
throw new NotImplementedException();
}
public override string GetHttpVerbName() {
return "GET";
}
public override string GetHttpVersion() {
return "HTTP 1.0";
}
public override string GetRemoteAddress() {
return "remoteaddress";
}
public override int GetRemotePort() {
throw new NotImplementedException();
}
public override string GetLocalAddress() {
return "host";
}
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();
}
// This string is 9 bytes in length. That's 2 more than
// the Content-Length header says it should be.
string data = "foox=bar&foo2=bar2\r\n";
public override string GetKnownRequestHeader(int index) {
switch (index) {
case HttpWorkerRequest.HeaderContentLength:
return (data.Length - 2).ToString();
case HttpWorkerRequest.HeaderContentType:
return "application/x-www-form-urlencoded";
}
return String.Empty;
}
public override byte[] GetPreloadedEntityBody() {
return Encoding.ASCII.GetBytes(data);
}
}
}
|