Unit Testing ASP.NET Web API

This is one of those things that I encounter now and then and always forgot how to do. I have to search the web and most of the results are for ASP.NET MVC, not for Web API. This time I found the solution on http://www.peterprovost.org/blog/2012/06/16/unit-testing-asp-dot-net-web-api/ and decided to document it.

var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Get, "http://host/api/my");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "my" } });
var controller = new MyController();
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;

I’m not sure you need the route stuff, but the config is needed.

Another variant of this is constructing an HttpRequestMessage that be passed to the UrlHelpeconstructor:

var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Get, "http://host/2.2/api/se/15/controller");
var route = config.Routes.MapHttpRoute(
            name: RoutesConstants.IncludeResourceSpecificOptionsRouteName,
            routeTemplate: "api/{country}/{deviceVersion}/{controller}/{id}",
            defaults:
                new
                {
                    id = RouteParameter.Optional,
                    country = RouteParameter.Optional,
                    deviceVersion = RouteParameter.Optional
                });
request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
var urlHelper = new UrlHelper(request);

Another related problem is if the controller accesses HttpContext.Current.Request. That you can just assign:

HttpContext.Current = new HttpContext(new HttpRequest(null, "http://someurl", null), new HttpResponse(new StringWriter()));
Advertisements

Uninstalling NuGet Packages the Hard Way

We were recently in a situation where TeamCity had “cleaned” some NuGet packages that some of our projects depended on. Now they didn’t build in TeamCity. I thought this would be easy to fix: Just upgrade them to the latest version, or uninstall the old and install the new one. But bot of these methods failed, because the package manager failed to build the dependency graph, because it didn’t find the packages. So this was a catch 22!

It turned out that it was possible to manually remove references to these packages by editing the packages.config files, and then run Install-Package. I spent time to automate this, and came up with the following PowerShell script:

$pattern = 'id=”Neonstingray.Nettv4.DataTransfer” | id="Neonstingray.Nettv4.Domain" | id="Neonstingray.Nettv4.Configuration"’
Get-ChildItem -Filter packages.config -Recurse | Select-String -Pattern $pattern | Group Path | ForEach-Object { (Get-Content -Path $_.Name) | where { $_ -NotMatch $pattern } | Set-Content -Encoding UTF8 -Path $_.Name }
$source = "https://<myhost>/httpAuth/app/nuget/v1/FeedService.svc"
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.UnitTests -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.Ooyala -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.Ooyala.IntegrationsTests -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.Ooyala.UnitTests -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.PartnerApi -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.PartnerApi.IntegrationTests -source $source
install-package Neonstingray.Nettv4.DataTransfer -projectname Valtech.Ready4Air.Ingest.PartnerApi.UnitTests -source $source

Patching Assembly Version in TeamCity

We use TeamCity for building our .NET solutions and Octopus Deploy for deployment. We use semantic versioning using the <major>.<minor>.<patch>.<build> pattern, and I wanted to automatically set AssemblyInformationalVersion (a.k.a. product version) in all built assemblies. This was set to 2.0.2 in AssemblyInfo.cs, so I had to add the build number.

This was fairly easy using a build feature in TeamCity. Select the desired build configuration (I called it Build and Publish), and instead of going to build steps, click on build features in the left menu, and select the File Content Replacer type and begin by loading the AssemblyInformationalVersion in AssemblyInfo (C#) template. I then modified the search pattern to:

(^\s*\[\s*assembly\s*:\s*((System\s*\.)?\s*Reflection\s*\.)?\s*AssemblyInformationalVersion(Attribute)?\s*\(\s*@?\")(([0-9\*]+\.?)+)(\"\s*\)\s*\])

This will capture the following groups:

1: [assembly: AssemblyInformationalVersion(“
5: 2.0.2
6: 2
7: “)]

so the replacement is:

$1$5.\%build.number%$7

As AssemblyVersion, I wanted to stick with Microsoft’s standard <major>.<minor>.<build>.<revision>, and in the projects, this was:

[assembly: AssemblyVersion("2.0.*")]

I wanted to change that to

[assembly: AssemblyVersion("2.0.nnn.*")]

The search pattern in case is

(^\s*\[\s*assembly\s*:\s*((System\s*\.)?\s*Reflection\s*\.)?\s*AssemblyVersion(Attribute)?\s*\(\s*@?\")(([0-9\*]+\.)+)[0-9\*]+(\"\s*\)\s*\])

and the replacement

$1$5\%build.number%.*$7

Batch Resizing and Date Stamping Photos

I have a photo frame that displays pictures from a USB stick. It is fairly low resolution (1024*600), and in order to pack as many pictures as possible on a low-capacity USB stick, I resize them. I also like to have the photo date and time displayed in a corner. Since I have thousands of photos, I cannot do this manually on at a time.

I found that ImageMagick could be used to accomplish this. It works from the command line with a lot of parameters. I created a Windows batch file to iterate through some folders and in one step resize and annotate with date and time and write the result to the USB stick. Here it is. (It iterates through subfolders 2012, 2013, 2014, 2015.)

@ECHO OFF
SET convert=C:\Program Files\ImageMagick-6.9.3-Q16\convert.exe
SET destination=G:\Documents\Pictures
IF NOT EXIST %destination% MKDIR %destination%
FOR /D %%d IN (2012 2013 2014 2015) DO (
    PUSHD %%d
    ECHO *** Processing folder %%d ***
    FOR /R %%a in (*.jpg) DO (
		ECHO %%a | FINDSTR /I ".picasaoriginals" > NUL
		IF errorlevel 1 (
			ECHO Processing file: %%a to %destination%\%%~nxa
			"%convert%" "%%a" -adaptive-resize 1024x600 - | "%convert%" - -pointsize 16 -fill white -undercolor "#00000080" -gravity Southeast -annotate +0+0 " %%[exif:DateTimeOriginal] " "%destination%\%%~nxa"
		) ELSE (
			ECHO Skipped %%a
		)
    )
    POPD
)
PAUSE

Monitoring My Home IP Phone Connection

A couple of years ago, I got fibre to my house and switched to IP telephony for my home number. The primary reason is cost – it is must cheaper that traditional copper. But unfortunately, it also less reliable. With irregular intervals, it simply stops working and we cannot call out and, which is more problematic, others cannot call us. So I thought about how to set up some kind of automatic monitoring, and finally found a solution.

I have a computer at home which is always on, and in my technology archive, I found a classic analogue modem. Problem one was how to connect them, since the modem of course has a serial RS232 port but the computer only has USB ports. That part I solved by buying an adapter/converter (I found this one (EAN 4040849954351) at my local dealer (kjell.com).) It had a 9-pin connector and my modem a 25 pin, but luckily my archive also contained an adapter for that.

When connected to the computer, it showed up as COM3. The next part of the solution was to write a script to send a command to the modem to dial my mobile phone and collect the result. If the IP telephony is down, the modem will not get a dial-tone and answer “NO DIALTONE”. The script, which I wrote in PowerShell, detects this and sends an e-mail in this case. Here it is:

param([string] $logfile)

$phonenumber = "a telephone number"
$comport = "COM3"
$smtphost = "smtp.live.com"
$smtpport = 587
$smtpuser = "user@domain"
$smtppassword = "password"
$emailsubject = "Telefonövervakaren"

function InitLog($logfile)
{
    if ($logfile -eq $null -or $logfile -eq "") 
    {
        $logfile = ($MyInvocation.ScriptName) + ".log"
    }
    Write-Host "Logfile: $logfile"
    if ([System.IO.File]::Exists($logfile)) { Remove-Item $logfile }
    return $logfile
}

function SendCommand ($port, $cmd, $logfile)
{
    $cmd = "AT" + $cmd
    Write-Host "> $cmd"
    "> $cmd" >> $logfile
    $port.WriteLine($cmd)
    $response = ""
    for ($i = 0; ($response -eq $null -or $response -eq "" -or $response -eq $cmd) -and ($i -lt 100); $i++)
    {
        Write-Host "." -NoNewline
        Start-Sleep -Seconds 1
        $response = $port.ReadExisting().Trim("`r", "`n");
    }
    Write-Host
    Write-Host $response
    $response >> $logfile
    return $response
}

try
{
    $logfile = (InitLog $logfile)
    #[System.IO.Ports.SerialPort]::GetPortNames()
    $port = new-Object System.IO.Ports.SerialPort $comport,9600,None,8,one
    $port.NewLine = "`r"
    $port.open()
    $response = (SendCommand $port "DT$phonenumber" $logfile)
    $port.Close()
}
catch
{
    Write-Host $_ -ForegroundColor Red
    $_ >> $logfile
}
$success = $response -eq "BUSY" -or $response -eq "NO ANSWER" -or $response -eq "NO CARRIER" -or $response -eq "VOICE"
Write-Host "Success: $success"
if (-not $success)
{
    try {
        Write-Host "Sending e-mail"
        $body = (Get-Content $logfile | Out-String)
        $smtpclient = New-Object Net.Mail.SmtpClient($smtphost, $smtpport) 
        $smtpclient.EnableSsl = $true 
        $smtpclient.Credentials = New-Object System.Net.NetworkCredential($smtpuser, $smtppassword); 
        $smtpclient.Send($smtpuser, $smtpuser, $emailsubject, $body)
    }
    catch
    {
        Write-Host $_ -ForegroundColor Red
        Write-Host $_.Exception -ForegroundColor Red
        $_ >> $logfile
        $_.Exception >> $logfile
    }
}

I saved this script as PhoneMonitor.ps1 and scheduled it using Windows task scheduler. On the action tab, I entered:

  • Program/script: PowerShell.exe
  • Arguments: C:\Users\HTPC\Documents\PhoneMonitor.ps1

Backing up Multiple PCs in a Home Environment

Backup of PCs

I have used the excellent Windows Home Server operating system to backup all my PCs at home. It can automatically take file and image backups, and in case of a disaster you can boot from a CD and perform a restore of the exact state of the last (or earlier) backup. Some time ago, however, my home server began corrupting the backup disks. (Maybe it had something to do with the fact that I was running it as a virtual machine or that I was using storage spaces.)

In addition to these problems, Windows Home Server is no longer being developed. (Which is sadly true for more excellent Microsoft products, e.g. Windows Media Center and Live Writer.) It was time to look another solution.

I tried Macrium Reflect Free, and found it simple and effective. It can do both file and image backups, so I went ahead and payed for the non-free version, which can do incremental backups (the free version only differential). An explanation of these terms are perhaps in order. From Macrium’s web site:

When Reflect creates an incremental image it only backs up the parts of your diskthat contain data that is different from the last backup you made. The advantage of this is that the resultant image file is both muchsmaller and much quicker to create than a full image. The only slight disadvantage is that when you restore your data, Reflect needs toaccess all the image files in the backup set to reconstruct the disk you want to restore. However, if the image files are stored in alocal or network directory then this operation is automatic and completely transparent.

A differential backup is similar to an incremental backup. However, rather thanbacking up the changes since the most recent backup, a differential backup will save changes made since the first/full backup.

Backup of the Backup Server

The next challange was how to backup the backup server. If that breaks, all backup breaks and no further backups or restores can be made.

The recent announcement of Azure File Storage provided me with the answer. With Azure File Storage, you can create an ordinary file share in Azure storage and use standard backup programs like e.g. Macrium Reflect (Free) or Cobian Backup. I do this to backup the system disk of the backup server, not the data disk with backups of my PC’s, because the risk of simultaneous failure of one PC and the data disk is very small.

First, you go to the new Azure portal. Then you browse your storage account and create a new file service and file share. Call it e.g. backup. The endpoint will be https://your-account-name.file.core.windows.net.

You can then mount using the following two commands:

cmdkey /add:your-account-name.file.core.windows.net /user:your-account-name /pass:your-access-key
net use Z: \\your-account-name.file.core.windows.net\backup

For more information, see https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-how-to-use-files/.