Changed to use Authenticate asp.net event instead of Authorize
[castle.git] / MonoRail / Castle.MonoRail.TestSupport / AbstractMRTestCase.cs
blob2177efd7791a4cba4358d92f5e75599b0e1d7814
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.TestSupport
17 using System;
18 using System.IO;
19 using System.Net;
20 using System.Text;
21 using System.Text.RegularExpressions;
22 using System.Web.Hosting;
23 using System.Configuration;
24 using System.Collections.Specialized;
25 using System.Runtime.Serialization.Formatters.Binary;
26 using System.Xml;
28 using NUnit.Framework;
30 /// <summary>
31 /// Base class for tests cases using the ASP.Net Runtime
32 /// to run the web project. Deprecated.
33 /// </summary>
34 [Obsolete("Prefer the BaseControllerTest")]
35 public abstract class AbstractMRTestCase
37 private static readonly String PhysicalWebDirConfigKey = "web.physical.dir";
38 private static readonly String VirtualWebDirConfigKey = "web.virtual.dir";
40 private WebAppHost host;
41 private TestRequest request;
42 private TestResponse response;
43 private StringBuilder outputBuffer = new StringBuilder();
45 #region Public Methods
47 /// <summary>
48 /// Reinstates the request.
49 /// </summary>
50 /// <param name="serializedRequest">The serialized request.</param>
51 public void ReinstateRequest(byte[] serializedRequest)
53 BinaryFormatter bf;
54 MemoryStream ms = null;
55 try
57 ms = new MemoryStream(serializedRequest);
59 bf = new BinaryFormatter();
60 Request.Headers = (NameValueCollection) bf.Deserialize(ms);
63 finally
65 if (ms != null) ms.Close();
69 /// <summary>
70 /// Gets the serialized request.
71 /// </summary>
72 /// <returns></returns>
73 public byte[] GetSerializedRequest()
75 MemoryStream objMS = new MemoryStream();
76 BinaryFormatter objBinaryFormatter = new BinaryFormatter();
78 try
80 objBinaryFormatter.Serialize(objMS, Request.Headers);
82 catch
86 return objMS.GetBuffer();
89 #endregion
91 #region Test Lifecycle
93 [TestFixtureSetUp]
94 public virtual void FixtureInitialize()
96 String virDir = GetVirtualDir();
97 String physicalDir = GetPhysicalDir();
99 if (!Path.IsPathRooted(physicalDir))
101 DirectoryInfo dinfo = new DirectoryInfo(
102 Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, physicalDir));
104 physicalDir = dinfo.FullName;
107 if (!Directory.Exists(Path.Combine(physicalDir, "bin")) ||
108 !File.Exists(Path.Combine(physicalDir, "web.config")))
110 String message = String.Format("The path specified for the " +
111 "web project doesnt look as a web project dir (bin directory or web.config missing): {0}",
112 physicalDir);
113 throw new ConfigurationErrorsException(message);
116 host = (WebAppHost) ApplicationHost.CreateApplicationHost(
117 typeof(WebAppHost), virDir, physicalDir);
119 host.Configure(virDir, physicalDir);
122 [SetUp]
123 public virtual void Initialize()
125 request = new TestRequest();
128 [TearDown]
129 public virtual void Terminate()
133 [TestFixtureTearDown]
134 public virtual void FixtureTerminate()
136 if (host != null) host.Dispose();
139 #endregion
141 #region Actions
143 /// <summary>
144 /// Performs a GET operation on the specified path.
145 /// </summary>
146 /// <example>
147 /// <code>
148 /// DoGet("home/index.rails");
149 /// </code>
150 /// </example>
151 /// <param name="path">The resource being request, for example <c>home/index.rails</c></param>
152 /// <param name="queryStringParams">A list of key/value pair, for example <c>name=johndoe</c></param>
153 public void DoGet(String path, params String[] queryStringParams)
155 DoGet(path, true, queryStringParams);
158 /// <summary>
159 /// Performs a GET operation on the specified path.
160 /// </summary>
161 /// <example><code>
162 /// DoGet("home/index.rails");</code>
163 /// </example>
164 /// <param name="path">The resource being request, for example <c>home/index.rails</c></param>
165 /// <param name="resendCookies">if set to <c>true</c> [resend cookies].</param>
166 /// <param name="queryStringParams">A list of key/value pair, for example <c>name=johndoe</c></param>
167 public void DoGet(String path, bool resendCookies, params String[] queryStringParams)
169 AssertPathIsValid(path);
171 if (queryStringParams.Length != 0)
173 Request.QueryStringParams = queryStringParams;
175 else
177 Request.QueryStringParams = null;
180 if (resendCookies)
182 ResendCookies();
185 Request.Url = path;
186 Request.Verb = "GET";
187 Request.PostParams = null;
189 SendRequest();
192 /// <summary>
193 /// Performs a POST operation on the specified path.
194 /// </summary>
195 /// <example>
196 /// <code>
197 /// DoPost("producto/search.rails", "name=mac", "page=1");
198 /// </code>
199 /// </example>
200 /// <param name="path">The resource being request, for example <c>home/index.rails</c></param>
201 /// <param name="postStringParams">A list of key/value pair, for example <c>name=johndoe</c></param>
202 public void DoPost(String path, params String[] postStringParams)
204 DoPost(path, true, postStringParams);
207 /// <summary>
208 /// Performs a POST operation on the specified path.
209 /// </summary>
210 /// <example>
211 /// <code>
212 /// DoPost("producto/search.rails", "name=mac", "page=1");
213 /// </code>
214 /// </example>
215 /// <param name="path">The resource being request, for example <c>home/index.rails</c></param>
216 /// <param name="resendCookies">if set to <c>true</c> [resend cookies].</param>
217 /// <param name="postStringParams">A list of key/value pair, for example <c>name=johndoe</c></param>
218 public void DoPost(String path, bool resendCookies, params String[] postStringParams)
220 if (postStringParams.Length != 0)
222 Request.PostParams = postStringParams;
224 else
226 Request.PostParams = null;
229 outputBuffer.Length = 0;
231 int pos = path.IndexOf('?');
233 if (pos > -1)
235 string qs = path.Substring(pos + 1);
236 path = path.Substring(0, pos);
237 Request.QueryStringParams = qs.Split('&');
239 else
241 Request.QueryStringParams = null;
244 // Set the content type so that the post data comes through.
245 Request.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
247 Request.Url = path;
248 Request.Verb = "POST";
250 if (resendCookies)
252 ResendCookies();
255 SendRequest();
258 /// <summary>
259 /// Performs a HEAD operation on the specified path.
260 /// </summary>
261 /// <example>
262 /// <code>
263 /// DoHead("producto/search.rails", "name=mac", "page=1");
264 /// </code>
265 /// </example>
266 /// <param name="path">The resource being request, for example <c>home/index.rails</c></param>
267 /// <param name="postStringParams">A list of key/value pair, for example <c>name=johndoe</c></param>
268 public void DoHead(String path, params String[] postStringParams)
270 if (postStringParams.Length != 0)
272 Request.PostParams = postStringParams;
274 else
276 Request.PostParams = null;
279 outputBuffer.Length = 0;
281 int pos = path.IndexOf('?');
282 if (pos > -1)
284 string qs = path.Substring(pos + 1);
285 path = path.Substring(0, pos);
286 Request.QueryStringParams = qs.Split('&');
288 else
290 Request.QueryStringParams = null;
293 Request.Url = path;
294 Request.Verb = "HEAD";
296 SendRequest();
299 #endregion
301 #region Properties
303 /// <summary>
304 /// Gets the <c>TestRequest</c>
305 /// </summary>
306 public TestRequest Request
308 get { return request; }
311 /// <summary>
312 /// Gets the <c>TestResponse</c>
313 /// </summary>
314 public TestResponse Response
316 get { return response; }
319 /// <summary>
320 /// Gets the request response
321 /// </summary>
322 public String Output
324 get { return outputBuffer.ToString(); }
327 /// <summary>
328 /// Returns the sessionId related to the current session
329 /// </summary>
330 public string SessionId
334 string sessionId = string.Empty;
338 sessionId =
339 Request.Cookies.GetCookies(new Uri("http://localhost"))["ASP.NET_SessionId"].ToString().Split("=".ToCharArray())[1
342 catch
346 return sessionId;
350 #endregion
352 #region Available Asserts
354 /// <summary>
355 /// Asserts that the response contains a number of nodes matching an XPath expression.
356 /// </summary>
357 /// <param name="xpathExpression">The xpath expression to match against.</param>
358 /// <param name="numberOfExpectedNodes">The number of expected nodes.</param>
359 protected void AssertResponseNodeCount(String xpathExpression, int numberOfExpectedNodes)
361 XmlDocument xml = new XmlDocument();
362 xml.LoadXml(Output);
364 Assert.AreEqual(numberOfExpectedNodes, xml.SelectNodes(xpathExpression).Count);
367 /// <summary>
368 /// Asserts that the response was NOT a redirect to the specified
369 /// <c>url</c> - for example check that your request was not sent to a login screen.
370 /// </summary>
371 protected void AssertNotRedirectedTo(String url)
373 // Location header always starts with a leading / so
374 // if this is not present on the specified url, add it
376 if (url.IndexOf("/") != 0)
378 url = "/" + url;
381 Assert.IsFalse(url == (String) Response.Headers["Location"]);
384 protected void AssertStatusCode(int expectedCode)
386 Assert.IsNotNull(response, "No requests performed with DoGet or DoPost (?)");
387 Assert.IsTrue(response.StatusCode == expectedCode, "Expecting status code {0} when it was in fact {1} {2}",
388 expectedCode, response.StatusCode, response.StatusDescription);
391 /// <summary>
392 /// Asserts the return status code is less than 400
393 /// </summary>
394 protected void AssertSuccess()
396 Assert.IsNotNull(response, "No requests performed with DoGet or DoPost (?)");
397 Assert.IsTrue(response.StatusCode < 400, "Expecting status code < 400 when it was in fact {0} - {1}",
398 response.StatusCode, response.StatusDescription);
401 /// <summary>
402 /// Asserts that reply has exactly the samme
403 /// content of <c>expectedContents</c>
404 /// </summary>
405 /// <param name="expectedContents"></param>
406 protected void AssertReplyEqualTo(String expectedContents)
408 Assert.AreEqual(expectedContents, Output, "Reply differs. Expecting {0} but was {1}", expectedContents, Output);
411 /// <summary>
412 /// Asserts that reply starts with
413 /// <c>expectedContents</c>
414 /// </summary>
415 protected void AssertReplyStartsWith(String contents)
417 String buffer = Output;
419 Assert.IsTrue(buffer.StartsWith(contents),
420 String.Format("Reply string did not start with '{0}'. It was '{1}'", Prepare(contents),
421 Prepare(buffer.Substring(0, Math.Min(contents.Length, buffer.Length)))));
424 /// <summary>
425 /// Asserts that reply ends with
426 /// <c>expectedContents</c>
427 /// </summary>
428 protected void AssertReplyEndsWith(String contents)
430 String buffer = Output;
432 Assert.IsTrue(buffer.EndsWith(contents),
433 "Reply string did not end with '{0}'. It was '{1}'", contents,
434 buffer.Substring(0, Math.Min(contents.Length, buffer.Length)));
437 /// <summary>
438 /// Asserts that reply contains the specified
439 /// <c>expectedContents</c>
440 /// </summary>
441 protected void AssertReplyContains(String contents)
443 Assert.IsTrue(Output.IndexOf(contents) != -1,
444 "AssertReplyContains did not find the content '{0}'. Raw content '{1}'", contents, Output);
447 /// <summary>
448 /// Asserts that reply have only whitespace characters
449 /// </summary>
450 protected void AssertReplyIsBlank()
452 string contents = Output.Trim();
454 Assert.IsTrue(contents == String.Empty,
455 "AssertReplyIsBlank found not whitespace characters '{0}'", contents);
458 /// <summary>
459 /// Asserts that reply contents match the specified pattern, ignoring any whitespaces
460 /// <c>pattern</c>
461 /// </summary>
462 protected void AssertReplyMatch(String pattern)
464 AssertReplyMatch(pattern, true, RegexOptions.None);
467 /// <summary>
468 /// Asserts that reply contents match the specified pattern
469 /// <c>pattern</c>
470 /// </summary>
471 protected void AssertReplyMatch(String pattern, bool ignoreSpaces)
473 AssertReplyMatch(pattern, ignoreSpaces, RegexOptions.None);
476 /// <summary>
477 /// Asserts that reply contents match the specified pattern
478 /// <c>pattern</c>
479 /// </summary>
480 protected void AssertReplyMatch(String pattern, bool ignoreSpaces, RegexOptions options)
482 string contents = Output;
484 if (ignoreSpaces)
486 contents = Regex.Replace(contents, @"\s+", "");
487 pattern = Regex.Replace(pattern, @"\s+", "");
490 Regex re = new Regex(pattern, options);
492 Assert.IsTrue(re.IsMatch(contents),
493 "AssertReplyMatch did not match pattern '{0}'. Raw content {1}", pattern, contents);
496 /// <summary>
497 /// Asserts that reply does not contain
498 /// <c>expectedContents</c>
499 /// </summary>
500 protected void AssertReplyDoesNotContain(String contents)
502 Assert.IsTrue(Output.IndexOf(contents) == -1,
503 "AssertReplyDoNotContain found the content '{0}'", contents);
506 /// <summary>
507 /// Asserts that the response was a redirect to the specified
508 /// <c>url</c>
509 /// </summary>
510 protected void AssertRedirectedTo(String url)
512 Assert.AreEqual(302, Response.StatusCode, "Redirect status not used");
513 AssertHasHeader("Location");
514 Assert.AreEqual(url, Response.Headers["Location"]);
517 /// <summary>
518 /// Asserts that the content-type header is equals to the specified
519 /// value
520 /// </summary>
521 /// <param name="expectedContentType">value to assert to</param>
522 protected void AssertContentTypeEqualsTo(String expectedContentType)
524 AssertHasHeader("Content-Type");
525 Assert.AreEqual(expectedContentType, Response.Headers["Content-Type"]);
528 /// <summary>
529 /// Asserts that the content-type header starts with to the specified
530 /// value
531 /// </summary>
532 /// <param name="expectedContentType">value to assert to</param>
533 protected void AssertContentTypeStartsWith(String expectedContentType)
535 AssertHasHeader("Content-Type");
536 Assert.IsTrue(Response.Headers["Content-Type"].ToString().StartsWith(expectedContentType));
539 /// <summary>
540 /// Asserts that the content-type header ends with the specified
541 /// value
542 /// </summary>
543 /// <param name="expectedContentType">value to assert to</param>
544 protected void AssertContentTypeEndsWith(String expectedContentType)
546 AssertHasHeader("Content-Type");
547 Assert.IsTrue(Response.Headers["Content-Type"].ToString().EndsWith(expectedContentType));
550 /// <summary>
551 /// Asserts that response contains the specified header.
552 /// </summary>
553 /// <param name="headerName">value to assert to</param>
554 protected void AssertHasHeader(String headerName)
556 Assert.IsTrue(Response.Headers[headerName] != null,
557 "Header '{0}' was not found", headerName);
560 /// <summary>
561 /// Asserts that PropertyBag contains the specified key.
562 /// </summary>
563 /// <param name="entryKey">key name</param>
564 protected void AssertPropertyBagContains(String entryKey)
566 Assert.IsNotNull(response.PropertyBag,
567 "PropertyBag could not be used. Are you using a testing enable version of MonoRail Engine and Framework?");
568 Assert.IsTrue(response.PropertyBag.Contains(entryKey), "Entry {0} was not on PropertyBag", entryKey);
571 /// <summary>
572 /// Asserts that PropertyBag's entry value equals to the specified value.
573 /// </summary>
574 /// <param name="entryKey">key name</param>
575 /// <param name="expectedValue">value to assert to</param>
576 protected void AssertPropertyBagEntryEquals(String entryKey, object expectedValue)
578 AssertPropertyBagContains(entryKey);
579 Assert.AreEqual(expectedValue, response.PropertyBag[entryKey], "PropertyBag entry differs from the expected");
582 /// <summary>
583 /// Asserts that Flash contains the specified key.
584 /// </summary>
585 /// <param name="entryKey">key name</param>
586 protected void AssertFlashContains(String entryKey)
588 Assert.IsNotNull(response.Flash,
589 "Flash could not be used. Are you using a testing enable version of MonoRail Engine and Framework?");
590 Assert.IsTrue(response.Flash.Contains(entryKey), "Entry {0} was not on Flash", entryKey);
593 /// <summary>
594 /// Asserts that Flash does not contains the specified key.
595 /// </summary>
596 /// <param name="entryKey">key name</param>
597 protected void AssertFlashDoesNotContain(String entryKey)
599 Assert.IsNotNull(response.Flash,
600 "Flash could not be used. Are you using a testing enable version of MonoRail Engine and Framework?");
601 Assert.IsFalse(response.Flash.Contains(entryKey), "Entry {0} was on Flash", entryKey);
604 /// <summary>
605 /// Asserts that Flash's entry value equals to the specified value.
606 /// </summary>
607 /// <param name="entryKey">key name</param>
608 /// <param name="expectedValue">value to assert to</param>
609 protected void AssertFlashEntryEquals(String entryKey, object expectedValue)
611 AssertFlashContains(entryKey);
612 Assert.AreEqual(expectedValue, response.Flash[entryKey], "Flash entry differs from the expected");
615 /// <summary>
616 /// Asserts that Session contains the specified key.
617 /// </summary>
618 /// <param name="entryKey">key name</param>
619 protected void AssertSessionContains(String entryKey)
621 Assert.IsNotNull(response.Session,
622 "Session could not be used. Are you using a testing enable version of MonoRail Engine and Framework?");
623 Assert.IsTrue(response.Session.Contains(entryKey), "Entry {0} was not on Session", entryKey);
626 /// <summary>
627 /// Asserts that Session does not contains the specified key.
628 /// </summary>
629 /// <param name="entryKey">key name</param>
630 protected void AssertSessionDoesNotContain(String entryKey)
632 Assert.IsNotNull(response.Session,
633 "Session could not be used. Are you using a testing enable version of MonoRail Engine and Framework?");
634 Assert.IsFalse(response.Session.Contains(entryKey), "Entry {0} was on Session", entryKey);
637 /// <summary>
638 /// Asserts that Session's entry value equals to the specified value.
639 /// </summary>
640 /// <param name="entryKey">key name</param>
641 /// <param name="expectedValue">value to assert to</param>
642 protected void AssertSessionEntryEqualsTo(String entryKey, object expectedValue)
644 AssertSessionContains(entryKey);
645 Assert.AreEqual(expectedValue, response.Session[entryKey], "Session entry differs from the expected");
648 /// <summary>
649 /// Asserts that the response contains the specified cookie.
650 /// </summary>
651 /// <param name="cookieName">cookie name</param>
652 protected void AssertHasCookie(String cookieName)
654 CookieCollection cookies = Response.Cookies.GetCookies(new Uri("http://localhost"));
656 foreach(Cookie cookie in cookies)
658 if (cookie.Name.Equals(cookieName)) return;
661 Assert.Fail("Cookie '{0}' was not found", cookieName);
664 /// <summary>
665 /// Asserts that Response cookie entry value equals to the specified value.
666 /// </summary>
667 /// <param name="cookieName">cookie name</param>
668 /// <param name="expectedValue">value to assert to</param>
669 protected void AssertCookieValueEqualsTo(String cookieName, String expectedValue)
671 AssertHasCookie(cookieName);
673 CookieCollection cookies = Response.Cookies.GetCookies(new Uri("http://localhost"));
675 foreach(Cookie cookie in cookies)
677 if (cookie.Name.Equals(cookieName))
679 Assert.AreEqual(expectedValue, cookie.Value);
680 break;
685 /// <summary>
686 /// Asserts that the response cookie has the specified expiration.
687 /// </summary>
688 /// <param name="cookieName">cookie name</param>
689 /// <param name="expectedExpiration">value to assert to</param>
690 protected void AssertCookieExpirationEqualsTo(String cookieName, DateTime expectedExpiration)
692 AssertHasCookie(cookieName);
694 CookieCollection cookies = Response.Cookies.GetCookies(new Uri("http://localhost"));
696 foreach(Cookie cookie in cookies)
698 if (cookie.Name.Equals(cookieName))
700 Assert.AreEqual(expectedExpiration.Day, cookie.Expires.Day, "Expiration day differs. Expecting {0} but was {1}",
701 expectedExpiration.Day, cookie.Expires.Day);
702 Assert.AreEqual(expectedExpiration.Month, cookie.Expires.Month,
703 "Expiration month differs. Expecting {0} but was {1}", expectedExpiration.Month,
704 cookie.Expires.Month);
705 Assert.AreEqual(expectedExpiration.Year, cookie.Expires.Year, "Expiration year differs. Expecting {0} but was {1}",
706 expectedExpiration.Year, cookie.Expires.Year);
707 Assert.AreEqual(expectedExpiration.Hour, cookie.Expires.Hour, "Expiration hour differs. Expecting {0} but was {1}",
708 expectedExpiration.Hour, cookie.Expires.Hour);
709 Assert.AreEqual(expectedExpiration.Minute, cookie.Expires.Minute,
710 "Expiration minute differs. Expecting {0} but was {1}", expectedExpiration.Minute,
711 cookie.Expires.Minute);
712 // Assert.AreEqual(expectedExpiration.Second, cookie.Expires.Second, "Expiration second differs. Expecting {0} but was {1}", expectedExpiration.Second, cookie.Expires.Second);
713 break;
718 #endregion
720 #region Overridables
722 protected virtual string GetPhysicalDir()
724 String dir = ConfigurationManager.AppSettings[PhysicalWebDirConfigKey];
726 if (dir == null)
728 String message = String.Format("Could not find a configuration key " +
729 "defining the web application physical directory. You must create " +
730 "a key ('{0}') on your configuration file or override the method " +
731 "AbstractMRTestCase.GetPhysicalDir", PhysicalWebDirConfigKey);
733 throw new ConfigurationErrorsException(message);
736 return dir;
739 protected virtual string GetVirtualDir()
741 String dir = ConfigurationManager.AppSettings[VirtualWebDirConfigKey];
743 if (dir == null)
745 dir = "/";
748 return dir;
751 #endregion
753 private String Prepare(String content)
755 if (content == null || content.Length == 0) return String.Empty;
757 return content.Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t");
760 /// <summary>
761 /// Ensures that cookies (and therefore the session) will persist between requests,
762 /// emulating the behaviour of a genuine web client.
763 /// </summary>
764 private void ResendCookies()
766 // If no initial request has been made then there will be no response
767 // Therefore we do not need to resendcookies as none have been received
768 if (response != null)
770 Uri uri = new Uri("http://localhost");
772 // We have a cookies container which is used to persist the cookies between requests,
773 // emulating the cookie cache on a web browser
775 // Here we take the cookies from the response and append them to any existing cookies
776 request.Cookies.Add(uri, response.Cookies.GetCookies(uri));
778 // Clear all of the cookie headers to prepare for them to be resent on the next request
779 request.Headers.Remove("Cookie");
781 // Form a new cookie header from the cookies in the persistant request cookie container
782 request.Headers.Add("Cookie", request.Cookies.GetCookieHeader(uri));
786 private void SendRequest()
788 outputBuffer = new StringBuilder();
790 StringWriter writer = new StringWriter(outputBuffer);
792 if (Request.Headers["IsTestWorkerRequest"] == null)
794 Request.Headers.Add("IsTestWorkerRequest", "true");
797 response = host.Process(Request, writer);
799 writer.Close();
802 private void AssertPathIsValid(string path)
804 if (path == null)
805 throw new ArgumentNullException("path", "Can't test a null path");
806 if (path.Length == 0)
807 throw new ArgumentException("Can't test an empty path", "path");
808 if (path[0] == '/')
809 throw new ArgumentException("Path cannot start with a '/'");