Integration tests with dependencies on response cookies throwing System.NullReferenceException - c#

I'm trying to find a way to write an integration test for a component which that is dependent on HttpContext and uses cookies.
My problem is an Exception is thrown when it tries to write anything to the response cookies.
Here's some code to reproduce the problem.
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web;
using Moq;
namespace CookieTest
{
[TestClass]
public class CookieTest
{
private Mock<HttpContextBase> _httpContextMock;
[TestInitialize]
public void SetUp()
{
var request = new HttpRequest(null, "http://localhost/", "");
var stream = new MemoryStream();
var sw = new StreamWriter(stream);
var response = new HttpResponse(sw);
_httpContextMock = new Mock<HttpContextBase>();
_httpContextMock.Setup(t => t.Request).Returns(new HttpRequestWrapper(request));
_httpContextMock.Setup(t => t.Response).Returns(new HttpResponseWrapper(response));
}
[TestMethod]
public void TestCookieWrite()
{
var httpContext = _httpContextMock.Object;
var expectedValue = "value";
var cookies = httpContext.Response.Cookies;
var cookieToAdd = new HttpCookie("key", expectedValue);
// to illustrate that these are not null
Assert.IsNotNull(cookies);
Assert.IsNotNull(cookieToAdd);
// System.NullReferenceException: Object reference not set to an instance of an object.
// at System.Web.HttpCookieCollection.Add(HttpCookie cookie)
// at CookieTest.CookieTest.TestCookieWrite() in CookieTest.cs: line 41
cookies.Add(cookieToAdd);
Assert.AreEqual(expectedValue, httpContext.Response.Cookies.Get("key"));
}
}
}

The response contains a private field called _context which requires a reference to both itself and the request, if you set the field like so:
var response = new HttpResponse(sw);
response.GetType()
.GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(response, new HttpContext(request, response));
_httpContextMock = new Mock<HttpContextBase>();
Then the NullReferenceException will no longer be thrown. Additionally, the test was failing as you compare the HttpCookie object with a string, you need to edit the assertion to check against the Value property like so:
Assert.AreEqual(expectedValue, httpContext.Response.Cookies.Get("key").Value);

Related

Getting different HTTP responses in API integration tests based on where I place my breakpoint

I've been getting non-deterministic test results and decided to delve deeper into the issue. I've ended up on two tests behaving differently based on whether I Debug them, or just use Run. I've managed to magically fix one by deleting and renaming it.
I'm testing my API endpoint for updating usernames via WebApplicationFactory(I'm using Mediator and a RavenDb that I recreate between every test if that makes a difference). The problem test case is User_Can_Not_Update_Username_If_It_Is_Already_Taken(). I'm expecting a Conflict response, but most of the time I'm getting OK. The weirdest thing is that the response varies on where I put my breakpoint:
OK
Conflict.
I've tried clearing Rider cache and looking into my FakeApp not disposing correctly. Trying to Debug before the UpdateUsernameCommandHandlerresults in the Conflict response status code, so I'm really at loss right now.
Here is the code of the test case:
[Fact]
public async Task User_Can_Not_Update_Username_If_It_Is_Already_Taken()
{
// Arrange
using var app = new FakeApp(DatabaseFixture.TestDbName);
var registerUserCommand = new RegisterUserCommand
{
Email = "oleksandr.torianyk#gmail.com"
};
var registerUserStringPayload = JsonConvert.SerializeObject(registerUserCommand);
var registerUserHttpContent = new StringContent(registerUserStringPayload, Encoding.UTF8, "application/json");
var registerUserResponse = await app.Client.PostAsync("/user", registerUserHttpContent);
var initialUpdateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var initialUpdateUsernameStringPayload = JsonConvert.SerializeObject(initialUpdateUsernameCommand);
var initialUpdateUsernameHttpContent = new StringContent(initialUpdateUsernameStringPayload, Encoding.UTF8, "application/json");
await app.Client.PutAsync("/user/username", initialUpdateUsernameHttpContent);
var updateUsernameCommand = new UpdateUsernameCommand
{
Id = new Guid(await registerUserResponse.Content.ReadAsStringAsync()),
Username = "All-ToR"
};
var updateUsernameStringPayload = JsonConvert.SerializeObject(updateUsernameCommand);
var updateUsernameHttpContent = new StringContent(updateUsernameStringPayload, Encoding.UTF8, "application/json");
// Act
var response = await app.Client.PutAsync("/user/username", updateUsernameHttpContent);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.Conflict);
}
FakeApp
public class FakeApp : IDisposable
{
private readonly WebApplicationFactory<Startup> _appFactory;
public HttpClient Client { get; }
public FakeApp(string ravenDbName = default)
{
_appFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
webHostBuilder.ConfigureServices(services =>
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.Development.json", true)
.AddEnvironmentVariables()
.Build();
services.AddRavenDb(configuration, ravenDbName);
services.AddDependencies();
services.AddMediatR(Assembly.GetExecutingAssembly());
}));
Client = _appFactory.CreateClient();
}
public void Dispose()
{
Client.Dispose();
_appFactory.Dispose();
}
}

Response object is null when using FeatureCollection on the DefaultHttpContext

I am testing some .net Core middleware and would like to run the middleware with the whole asp.net Core http pipeline instead of mocking it.
The problem is that somehow the Response object is not being set in the httpRequest when I use the Feature Collection and it is read only on the Request itself.
This code throws an exception when it tries to write to the Response Stream.
var fc = new FeatureCollection();
fc.Set<IHttpRequestFeature>(new HttpRequestFeature {
Headers = new HeaderDictionary { { "RandomHeaderName", "123" } }
});
var httpContext = new DefaultHttpContext(fc);
var middleware = new RequestValidationMiddleware(
next: async (innerHttpContext) =>
{
await innerHttpContext.Response.WriteAsync("test writing");
});
middleware.InvokeAsync(httpContext).GetAwaiter().GetResult();
By using a custom feature collection, you are excluding features that would have been added by the default constructor of the DefaultHttpContext
public DefaultHttpContext()
: this(new FeatureCollection())
{
Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
}
public DefaultHttpContext(IFeatureCollection features)
{
_features.Initalize(features);
_request = new DefaultHttpRequest(this);
_response = new DefaultHttpResponse(this);
}
try recreating what was done in the default constructor by also adding the required features needed to exercise your test
var fc = new FeatureCollection();
fc.Set<IHttpRequestFeature>(new HttpRequestFeature {
Headers = new HeaderDictionary { { "RandomHeaderName", "123" } }
});
//Add response features
fc.Set<IHttpResponseFeature>(new HttpResponseFeature());
var responseBodyStream = new MemoryStream();
fc.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(responseBodyStream ));
var httpContext = new DefaultHttpContext(fc);

HttpClient (Windows.Web.Http) working with cookies

I am working on a Windows app and am having some issues with cookies. Please note that I am working with Windows.Web.Http, not the System namespace HttpClient.
The API I'm working with uses an auth-header for authentication. Basically after a POST to login, I need a way to get the cookies returned and then use those cookies to perform the subsequent API calls. I posted an example of what I currently have, which succeeds. I can see the cookies in the result object. I'm just not entirely sure where to go from here / how to proceed. Thanks! Any ideas?
using MyApi.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Web.Http;
using Newtonsoft.Json;
using MyApi.Models.Auth;
using MyApi.Models;
namespace MyApi
{
public class MyService
{
private const string MyBaseUrl = "http://api.my.com:3000";
private readonly HttpClient _httpClient = new HttpClient();
public async Task<SignInResponse> AttemptLogin(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
throw new ArgumentException("Username or password is null or empty");
var uri = new Uri(string.Format("{0}/{1}", MyBaseUrl, "auth/signin"));
var authSignIn = new Models.Auth.SignInRequest();
authSignIn.Email = username;
authSignIn.Password = password;
var myObject = JsonConvert.SerializeObject(authSignIn);
// I see the headers in the result object, but I'm not
// sure the best way to a) get them out and b) shove them into
// all of the next calls
var result = await _httpClient.PostAsync(uri,
new HttpStringContent(myObject.ToString(),
Windows.Storage.Streams.UnicodeEncoding.Utf8,
"application/json"));
var content = await result.Content.ReadAsStringAsync();
var successResponse = new SignInResponse();
try
{
successResponse = JsonConvert.DeserializeObject<SignInResponse>(content);
}
catch (Exception)
{
var failResponse = JsonConvert.DeserializeObject<ErrorResponse>(content);
throw new Exception(failResponse.message);
}
return successResponse;
}
}
}
You can use HttpBaseProtocolFilter.CookieManager, e.g.:
var filter = new HttpBaseProtocolFilter();
var cookieManager = filter.CookieManager;
var uri = new Uri("http://api.my.com:3000");
foreach (var cookie in cookieManager.GetCookies(uri))
{
Debug.WriteLine(cookie.Name);
Debug.WriteLine(cookie.Value);
}
Notice, if the cookies are already in the HttpCookieContainer, the cookies will be automatically added in the next requests to http://api.my.com:3000, and no action is required from your side.
If you want to modify them or delete them, the HttpCookieContainer has methods to do that.
Take a look at Flurl. It presents a fluent interface over the Http bits, so you can say something like this to authenticate and reuse the connection with the cookies:
using (var fc = new FlurlClient().EnableCookies())
{
var url = new Url( "http://api.com/endpoint" ) ;
await url
.AppendPathSegment("login")
.WithClient(fc)
.PostUrlEncodedAsync(new { user = "user", pass = "pass" });
var page = await url
.AppendPathSegment("home")
.WithClient(fc)
.GetStringAsync();
// Need to inspect the cookies? FlurlClient exposes them as a dictionary.
var sessionId = fc.Cookies["session_id"].Value;
}

How to create VMs using google compute engine REST API

I am new to Google Compute Engine. Some one please help me with creating Google Compute Engine VMs programmatically using REST APIs in C#.
Here [1] you can found the API documentation to create an instance and at the bottom of the document the C# examples [2]:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Compute.v1;
using Google.Apis.Services;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
using Data = Google.Apis.Compute.v1.Data;
namespace ComputeSample
{
public class ComputeExample
{
public static void Main(string[] args)
{
ComputeService computeService = new ComputeService(new BaseClientService.Initializer
{
HttpClientInitializer = GetCredential(),
ApplicationName = "Google-ComputeSample/0.1",
});
// Project ID for this request.
string project = "my-project"; // TODO: Update placeholder value.
// The name of the zone for this request.
string zone = "my-zone"; // TODO: Update placeholder value.
// TODO: Assign values to desired properties of `requestBody`:
Data.Instance requestBody = new Data.Instance();
InstancesResource.InsertRequest request = computeService.Instances.Insert(requestBody, project, zone);
// To execute asynchronously in an async method, replace `request.Execute()` as shown:
Data.Operation response = request.Execute();
// Data.Operation response = await request.ExecuteAsync();
// TODO: Change code below to process the `response` object:
Console.WriteLine(JsonConvert.SerializeObject(response));
}
public static GoogleCredential GetCredential()
{
GoogleCredential credential = Task.Run(() => GoogleCredential.GetApplicationDefaultAsync()).Result;
if (credential.IsCreateScopedRequired)
{
credential = credential.CreateScoped("https://www.googleapis.com/auth/cloud-platform");
}
return credential;
}
}
}
[1] https://cloud.google.com/compute/docs/reference/rest/v1/instances/insert
[2] https://cloud.google.com/compute/docs/reference/rest/v1/instances/insert#examples

How do I fake System.Web.HttpClientCertificate?

I want to write unit tests for a WCF web service. The service uses HttpContext.Current. I already managed to Fake it by adding a Fake Assembly to System.Web and some code like:
[Test]
public void TestMyService()
{
using (ShimsContext.Create())
{
HttpRequest httpRequest = new HttpRequest("", "http://tempuri.org", "");
HttpContext httpContext = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
System.Web.Fakes.ShimHttpContext.CurrentGet = () => { return httpContext; };
System.Web.Fakes.ShimHttpClientCertificate.AllInstances.IsPresentGet = (o) => { return true; };
}
}
But my service also needs the ClientCertificate:
if (!HttpContext.Current.Request.ClientCertificate.IsPresent) // <== Exception in unit test!
throw new Exception("ClientCertificate is missing");
_clientCertificate = new X509Certificate2(HttpContext.Current.Request.ClientCertificate.Certificate);
Now in the marked line the unit test throws a NullReferenceException:
Result Message: System.NullReferenceException : Object reference not
set to an instance of an object. Result StackTrace: at
System.Web.HttpClientCertificate..ctor(HttpContext context) at
System.Web.HttpRequest.CreateHttpClientCertificateWithAssert() at
System.Web.HttpRequest.get_ClientCertificate() at (my method) at
TestMyService()
How can I set up the ClientCertificate for the unit tests? I don´t know how the create an HttpClientCertificate object to pass by a Shim, because there is not appropriate constructor.
I found the solution myself.
Because the Exception came from the constructor of HttpClientCertificate I also had to fake it. I simple do nothing in the faked constructor:
System.Web.Fakes.ShimHttpClientCertificate.ConstructorHttpContext = (o, httpCont) => { };
Additionally in order to get a useful client certificate with HttpContext.Current.Request.ClientCertificate.Certificate in my unit test I fake:
byte[] clientCertBytes = {0x30, 0x82, 0x03, ...., 0xd3};
System.Web.Fakes.ShimHttpClientCertificate.AllInstances.CertificateGet = (o) =>
{
return clientCertBytes;
};
The clientCertBytes are the RawData of an X509Certificate2 object which I created in a debug session where I created this object from a file (could also be done from certificate store).

Categories