Unit Testing ASP.Net MVC 5 Controllers using Rhino Mocks

February 12, 2016

Unit Testing ASP.Net MVC Controllers is not as straight-forward as you may thing, because as you get started, you face questions such as: how to create an HttpContext, an HttpContextBase, should you prefer mocking, and which mocking framework, etc.

After a day of googling and tinkering, I finally came up with a working solution using Rhino.Mocks and MvcContrib, both downloaded via nuget. To analyze Owin black magic, I used the source code repositories of Katana on Codeplex and symbolsource.

[TestClass()]
public class HomeIndexControllerTests
{
  [TestMethod()]
  public void HomeIndexTest()
  {

The authentication routine uses async calls, such as FindByNameAsync(). I tried to declare the test method as public async Task, but after starting the test, the IDE claimed it’s busy, but did not execute the test.

Without Task.Run(), the async method would fail throwing an AggregateException.

I found the work-around to encapsulate the whole test inside a Task.Run():

    Task.Run(() =>
    {
      var builder = new TestControllerBuilder();

First, we mock the ApplicationUserManager

      var us = MockRepository.GenerateStub<IUserStore<AppUser>>();
      var aum = MockRepository.GenerateStub<ApplicationUserManager>(us);

My class for storing users in the user database is called AppUser. The variable appuser is set to null for unauthenticated requests, and contains a valid object if authenticated:

      AppUser appuser = null;
      //appuser = new AppUser { .... values .... };  // uncomment for authenticated request

These stubs return the defined user object:

      us.Stub(u => u.FindByNameAsync(""))
        .IgnoreArguments()
        .Return(Task.FromResult(appuser));
      aum.Stub(m => m.FindByNameAsync((ClaimsIdentity)null, ""))
        .IgnoreArguments()
        .Return(Task.FromResult(appuser));

Create the Owin context and register the user manager:

      var owin = new OwinContext();
      owin.Set(aum);

Use the builder to create the controller under test:

      var controller = builder.CreateController<C.Home.IndexController>();

To test unauthenticated requests, we check whether the class or the method have an [Authorize] attribute

      if (appuser == null)
      {
        var type = controller.GetType();
        var attributes = type.GetCustomAttributes(typeof(AuthorizeAttribute), true);
        var methodInfo = type.GetMethod("Execute" 
          /*, new Type[] { ....parameter types.... } */
        );
        var methodAttributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
        Assert.IsFalse(attributes.Any() || methodAttributes.Any(), "Unauthorized request not allowed");
      }

Next, we wire up the HttpContext and Owin, and set the request’s cookies:

      var context = controller.HttpContext; 
      // the context is already mocked, so we can stub() it

      var dict = new Dictionary<object, object>();
      dict.Add("owin.Environment", owin.Environment);
      context.Stub(c => c.Items).Return(dict);
      var request = controller.Request;
      request.Stub(r => r.Cookies).Return(new HttpCookieCollection());

Next, we create a principal from the user object (see here for setting the name claim)

      var user = new GenericPrincipal(
        new ClaimsIdentity(
          new Claim[] { 
            new Claim(ClaimTypes.Name, appuser == null ? "" : appuser.UserName) }),
        new string[0]);
      context.User = user;

Then we create a fake HttpContext which matches the mocked HttpContextBase object created above:

      HttpContext.Current = new HttpContext(
        new HttpRequest("", "http://tempuri.org", ""),
        new HttpResponse(new StringWriter())
      );
      HttpContext.Current.Items["owin.Environment"] = owin.Environment;
      if (appuser != null)
        HttpContext.Current.User = user;

We are now ready to execute the controller method

      builder.InitializeController(controller);
      var result = controller.Execute();

And check the results: if the request is successful, the result is typically a ViewResult.

      Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
      Assert.IsInstanceOfType(result, typeof(ViewResult));

We can now test the result values, and end the Task definition:

    }).GetAwaiter().GetResult();
  }
}

 


Finding Spammers in hMailServer Log Files

February 4, 2016

hMailServer has a couple of spam protection measures built in, such as DNS blacklists and SURBL support. Among other features, you can also ban single IP adresses or IP ranges from connecting to your mail server.

While recently browsing through the log files, I noticed a couple of IP addresses which repeatedly connected to the mail server to log in, but kept their rate over the default 30 minutes auto-ban timer.

Interestingly those addresses chose to authenticate via AUTH LOGIN, but failed every time to provide a valid password. This results in a

535 Authentication failed

answer by the server, thus closing the conversation.

In the log file, the status code 535 looks like this

"SMTPD" 3228 21101 "2016-02-03 00:05:19.743" "xxx.xx.xx.xxx" 
  "SENT: 535 Authentication failed. Too many invalid logon attempts."

To find the conversations ending in status code 535, we can simply grep or findstr the relevant log files

grep "SENT: 535" *.log

In the log files, IP address is logged in the sixth column, so we can iterate over the resulting lines with the shell’s for command with option /f “tokens=6”.

Then we sort and count

(for /f "tokens=6" %i in ('grep "SENT: 535" *.log') do @echo %i) 
  | sort | uniq -c

To count the resulting IP addresses, I use my tool uniq, implemented after the Unix command uniq.

Similarly, one could also search for “550 Unknown user”.


NHibernate SELECT MAX()

February 3, 2016

A form used to edit or add records was to set a number field to the highest assigned integer value plus one.

This is typically achieved by writing

SELECT Max(NumberField)+1 FROM [Table]

and in NHibernate you write something like

result.Number = session.Query<Table>()
    .Max(t => t.Number) + 1;

where Number is defined as int.

While this solution is principally correct, it fails if the table has no records:

Server Error in '/' Application.
Value cannot be null.
Parameter name: item
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: item
Stack Trace:
[ArgumentNullException: Value cannot be null.
Parameter name: item]
 System.ThrowHelper.IfNullAndNullsAreIllegalThenThrow(Object value, ExceptionArgument argName) +4195637
 System.Collections.Generic.List`1.System.Collections.IList.Add(Object item) +32
 NHibernate.Util.<>c__DisplayClass4.<AddAll>b__2() +13
 NHibernate.Util.ArrayHelper.AddAll(IList to, IList from) +445
 NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results) +573
 NHibernate.Impl.StatelessSessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results) +329
[GenericADOException: Could not execute query[SQL: SQL not available]]
 NHibernate.Impl.StatelessSessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results) +379
 NHibernate.Impl.AbstractSessionImpl.List(IQueryExpression queryExpression, QueryParameters parameters) +145
 NHibernate.Impl.AbstractQueryImpl2.List() +117
 NHibernate.Linq.DefaultQueryProvider.ExecuteQuery(NhLinqExpression nhLinqExpression, IQuery query, NhLinqExpression nhQuery) +36
 NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +50
 NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression) +11
 System.Linq.Queryable.Max(IQueryable`1 source, Expression`1 selector) +283

The problem is, of course, that SELECT MAX(NumberField) on an empty table results in a single record containing a NULL value for the computed column, and the NHibernate mapper throws an exception trying to assign the NULL value to the int property.

Additionally, the SELECT statement above should also be written as

SELECT ISNULL(MAX(Number), 0) + 1

to generate 1 for the first record, which already points in the right direction.

The correct NHibernate statement is therefore

result.Number = session.Query<Table>()
    .Max(t => (int?) t.Number) ?? 0 + 1;

casting the C# int property to a Nullable<int>, which accepts the NULL value resulting from MAX(). The ?? operator is the equivalent of T-SQL’s ISNULL() function.

Fundamentally, the behavior is caused be the definition of the Queryable.Max() method:

public static TResult Max<TSource, TResult>(
  this IQueryable<TSource> source, 
  Expression<Func<TSource, TResult>> selector);

If the selector returns int, then the method also returns int. Could the result type not have been declare as Nullable<TResult>?

What does Linq to Objects do? This simple code

IEnumerable<int> values = new int[0];
Console.WriteLine(values.Max());

throws an InvalidOperationException with the message

Sequence contains no elements

as the corresponding Max() method also does not handle Null values. The correct way in Linq to Objects is to call DefaultIfEmpty() to replace an empty enumerable with one containing a single-element default value:

IEnumerable<int> values = new int[0];
values = values.DefaultIfEmpty();
Console.WriteLine(values.Max());

So, I experimented a bit to find a meaningful solution

public static T? max<T>(IEnumerable<T> values) 
  where T: struct, IComparable<T>
{
  T? result = null;
  foreach (var v in values)
    if (!result.HasValue || (v.CompareTo(result.Value) > 0))
      result = v;
  return result;
}
public static T max<T>(IEnumerable<T> values) 
  where T : class, IComparable<T>
{
  T result = null;
  foreach (var v in values)
   if (result==null || (v.CompareTo(result) > 0))
     result = v;
  return result;
}

only to find that

  • C# does not include generic constraints into method signatures, causing the code to not compile
  • Enumerable.Max() and Queryable.Max() are separate extension methods

In fact, Enumerable.Max() (which handles arrays, Lists, and everything IEnumerable) defines various overloads for base types, and one generic overload, whereas Queryable.Max() only defines the generic method. So the code above would have to be changed to base type-specific methods and one generic method.

Of course, the above code for IEnumerable would have no effect on how NHibernate handles the IQueryable.Max() method. This would have to be dealt with using NHibernate extensions.


Retrieving Length of Varbinary Field in NHibernate Linq

January 20, 2016

I wanted to SELECT the length of a field declared as VARBINARY(MAX) and mapped to a C# byte[] property. So naïvely I wrote the query in NHibernate Linq as

session.Query<MyTable>().Where(...).Select(t => new { t.ID, t.Pdf.Length })

Rather unexpectedly, NHibernate decided to translate this Linq query into a sub-select in the form

select t0_.ID as col_0_0_,
  (select cast(count(*) as INT) from myTable t0_) as col_1_0_
from myTable t0_

clearly mistaking the .Length as a COUNT(), rather than a LEN().

RegisterGenerator to the rescue!

Having used NH generators already once or twice, I wrote an extension method

public static int? GetBytesLen(this byte[] array)
{
  return null;
}

a generator class

public class BinaryLengthMethodsHqlGenerator : BaseHqlGeneratorForMethod
{
  public BinaryLengthMethodsHqlGenerator()
  {
    SupportedMethods = new[] {
      ReflectionHelper.GetMethodDefinition((byte[] x) => x.GetBytesLen())
    };
  }
  public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, 
    ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, 
    IHqlExpressionVisitor visitor)
  {
    return treeBuilder.MethodCall("bytes_length", 
      visitor.Visit(arguments[0]).AsExpression());
  }
}

wired the new method into my registry class

internal class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
  public LinqToHqlGeneratorsRegistry()
  {
    RegisterGenerator(
      ReflectionHelper.GetMethodDefinition(
        (byte[] x) => x.GetBytesLen()), 
        new BinaryLengthMethodsHqlGenerator());
  }
}

which is itself registered in NH’s configuration

configuration.LinqToHqlGeneratorsRegistry<LinqToHqlGeneratorsRegistry>();

and registered the SQL equivalent of the declared pseudo-function “bytes_length” in my NH Dialect

internal class MsSql2008DialectFixed : MsSql2008Dialect
{
  protected override void RegisterFunctions()
  {
    base.RegisterFunctions();
    RegisterFunction("bytes_length", 
      new SQLFunctionTemplate(NHibernateUtil.Int32, "len(?1)"));
  }
}

Next, I modified the above Linq query to

session.Query<MyTable>().Where(...).Select(t => new { t.ID, t.Pdf.GetBytesLen() })

and NHibernate now generates the desired SQL query

select t0_.ID as col_0_0_,
  len(t0_.Pdf) as col_1_0_
from myTable t0_

As I wrote this article, I found that RegisterGenerator() not only supports methods, but also properties. But when I tried and adapted the code to use the byte array’s Length property, RegisterGenerator() threw the exception

Unable to cast object of type ‘System.Linq.Expressions.UnaryExpression’ to type ‘System.Linq.Expressions.MemberExpression’.

and it turned out that .Length is translated into a UnaryExpression of type ArrayLength, rather than a MemberExpression. This phenomenon is covered by at least 2 SO questions, here and here.


Unknown Class “ProfileCommon”

January 19, 2016

I was asked to have a look at a Web Site project, and whether it could be converted into a Web Application project. So I created a web application project, copied and added all project files, and after a bit of editing (adding namespaces, etc.) I came across the error:

The type or namespace name ‘ProfileCommon’ could not be found (are you missing a using directive or an assembly reference?)

I soon found out that the class ProfileCommon is being automatically generated during the build process of a Web Site project, and in the meantime a VS BuildTask and a couple lines of code can be found on the internetz which replicate to original code generator.

To understand what it going on, I opened the original web site project, searched for a reference to ProfileCommon, and hit Go To Definition to retrieve the generated code.

As it turns out, the generator simply parses the configuration/system.web/profile/properties section of the web site’s web.config file.

Every item in the section contains a property definition in the form

<add name="[PropertyName]" defaultValue="[value]" 
    type="[.Net Datatype]" />

From this information, the generator creates a public class like this:

using System;
using System.Web;
using System.Web.Profile;
public class ProfileCommon : System.Web.Profile.ProfileBase {

Each declared property is generated as

  public virtual [Datatype] [PropertyName] {
    get {
      return (([Datatype)(this.GetPropertyValue("[PropertyName]")));
    }
    set {
      this.SetPropertyValue("[PropertyName]", value);
    }
  }

and finally

  public virtual ProfileCommon GetProfile(string username) {
    return ((ProfileCommon)(ProfileBase.Create(username)));
  }
}

My guess is that the ProfileBase.GetPropertyValue() method also evaluates the defaultValue clause somehow, but MSDN does not mention this question.


ASP.Net MVC: HTTP 401 in /Reports Controller

January 15, 2016

A customer reported that an MVC application I develop fails when trying to access a page called “Reports”, which unsurprisingly resides under the URL /Reports below application root, implemented by the class ReportsController.

On a different installation, the error does not occur, but that installation is not installed in web site root – rather, the installation which allows /Reports to be executed is a web application inside a web site.

A web search led me to a comment on Stack Overflow, where the same problem had been reported 3 years ago:

Do you by any chance have SSRS installed/hosted on your QA server?

And as it happens, the machine in question had SSRS installed ;)

Time for some background information: When an HTTP request hits a Windows machine, IIS is not the first program to process it. Rather, the HTTP request is first handled by HTTP Server in http.sys. Applications register with the HTTP Server API if they process HTTP requests outside of IIS.

And so does SSRS!

http.sys stores a table mapping URL paths and their registered applications. Entering on the command line

netsh http show urlacl

results in a list of URLs registered with http.sys. The /Reports URL shows up both for ports 80 and 443:

    Reservierte URL            : http://+:80/Reports/
        Benutzer: NT SERVICE\ReportServer
            Abhören: Yes
            Delegieren: No
    Reservierte URL            : https://SomeServer:443/Reports/
        Benutzer: NT SERVICE\ReportServer
            Abhören: Yes
            Delegieren: No

See here for documentation of the netsh http command.

So there we have it: the web root directory /Reports is mapped to SSRS, for all IP addresses (and host names) on port 80 (http), and for a specific host name on port 443 (https).

The solution is now to run Reporting Services Configuration Manager and connect to the Reporting Services

Reporting Services Configuration Manager

Reporting Services Configuration Manager

Click Advanced, and find the IP Address filter set to “all assigned”

IP Address and Port filter rules

IP Address and Port filter rules

Click Edit and restrict the filter to either IP Address or Host Header Name

Edit HTTP Filter for Reporting Service

Edit HTTP Filter for Reporting Service

Note that you can also change the settings from the command line, using the netsh http commands delete urlacl and add urlacl.


Retrieving Chrome tabs suspended by The Great Suspender

January 10, 2016

After a recent Windows crash, restarting Chrome would not open all the windows and tabs I had collected, despite its nice “Restore previous session” question.

None of the tips I found on the net helped recovering those tabs:

Anyway, I found that the tabs suspended by The Great Suspender were most likely not contained in the Tabs file, so where is that information stored?

By looking at the source it turns out that TGS stores its data in an IndexedDB.

To access this database, you need to open TGS’s extension URL chrome-extension://klbibkeccnjlkjkiokjodocebajanakg/suspended.html. Using F12 (Developer Tools) and selecting Resources, you find “tgs” under IndexedDB, containing the tables gsCurrentSessions, gsSuspendedTabInfo, gsPreviews, and gsSavedSessions.

Unfortunately, currently there is no way to export this data directly from the DevTools, so we need a little bit of scripting magic.

After copying and pasting from MDN’s IndexedDB API documentation and sample code and some trial and error, I finally completed a small Javascript script which, when entered in the Developer Tools’ Console, results in a JSON-formatted list of all suspended tabs:

var db; var result=""; var record=0;
var dbor = window.indexedDB.open("tgs");
dbor.onsuccess = function(event) { 
  db = dbor.result;
  db.transaction(["gsSuspendedTabInfo"], "readonly")
    .objectStore("gsSuspendedTabInfo")
    .openCursor()
    .onsuccess= function(event) {
      var c=event.target.result;
      if (c){
        result+=JSON.stringify(c.value)+"\r\n";
        console.log(record++);
        c.continue();
      } else {
        console.log(result);
      }
    };
};

A single JSON record of a suspended tab looks like this:

{
  "date":"2015-12-14T18:40:55.563Z",
  "title":".validate() | jQuery Validation Plugin",
  "url":"http://jqueryvalidation.org/validate#toptions",
  "favicon":"chrome://favicon/http://jqueryvalidation.org/validate#toptions",
  "pinned":false,
  "index":3,
  "windowId":2898,
  "id":1281
}

Of course, you can process each tab’s record (c.value in the code above) any way you need.


Follow

Get every new post delivered to your Inbox.

Join 77 other followers