-
Notifications
You must be signed in to change notification settings - Fork 10
/
CustomExceptionHandlerTests.cs
122 lines (103 loc) · 5.21 KB
/
CustomExceptionHandlerTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
namespace Todo.WebApi.ExceptionHandling
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Security.Principal;
using System.Text.Json;
using System.Threading.Tasks;
using ApplicationFlows.Security;
using Autofac;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Models;
using NUnit.Framework;
using Services.Security;
using TestInfrastructure;
/// <summary>
/// Contains integration tests targeting <see cref="CustomExceptionHandler"/> class.
/// </summary>
[TestFixture]
public class CustomExceptionHandlerTests
{
/// <summary>
/// Ensures <see cref="CustomExceptionHandler.HandleException"/> method successfully converts an API exception
/// to an instance of the <see cref="ProblemDetails"/> class.
/// </summary>
[Test]
public async Task HandleException_WhenApiThrowsException_MustConvertExceptionToInstanceOfProblemDetailsClass()
{
// Arrange
var generateJwtModel = new GenerateJwtModel
{
UserName = $"test-user--{Guid.NewGuid():N}",
Password = $"test-password--{Guid.NewGuid():N}",
};
using var testWebApplicationFactory =
new TestWebApplicationFactory(MethodBase.GetCurrentMethod()?.DeclaringType?.Name)
.WithMockServices(containerBuilder =>
{
// Ensure a mock implementation will be injected whenever a service requires an instance of the
// IGenerateJwtFlow interface.
containerBuilder
.RegisterType<GenerateJwtFlowWhichThrowsException>()
.As<IGenerateJwtFlow>()
.InstancePerLifetimeScope();
})
.WithWebHostBuilder(webHostBuilder =>
{
webHostBuilder.ConfigureAppConfiguration(configurationBuilder =>
{
configurationBuilder.AddInMemoryCollection(new[]
{
// Ensure database is not migrated, since having an up-to-date RDBMS will just complicate
// this test method.
new KeyValuePair<string, string>("MigrateDatabase", bool.FalseString)
});
});
});
using HttpClient httpClient = testWebApplicationFactory.CreateClient();
// Act
HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("api/jwt", generateJwtModel);
// Assert
using (new AssertionScope())
{
const HttpStatusCode expectedStatusCode = HttpStatusCode.InternalServerError;
httpResponseMessage.IsSuccessStatusCode
.Should().BeFalse("the endpoint was supposed to throw a hard-coded exception");
httpResponseMessage.StatusCode.Should().Be(expectedStatusCode,
$"the hard-coded exception was mapped to HTTP status {expectedStatusCode}");
byte[] problemDetailsAsBytes = await httpResponseMessage.Content.ReadAsByteArrayAsync();
await using var memoryStream = new MemoryStream(problemDetailsAsBytes);
// Must use System.Text.Json.JsonSerializer instead of Newtonsoft.Json.JsonSerializer to ensure
// ProblemDetails.Extensions property is correctly deserialized and does not end up as an empty
// dictionary.
ProblemDetails problemDetails = await JsonSerializer.DeserializeAsync<ProblemDetails>(memoryStream);
problemDetails.Should().NotBeNull("application must handle any exception");
// ReSharper disable once PossibleNullReferenceException
problemDetails.Extensions.Should().NotBeNull("problem details must contain extra info");
problemDetails.Extensions.Should().NotContainKey("errorData", "error data must not be present");
problemDetails.Extensions.Should().ContainKey("errorKey", "error key must be present");
problemDetails.Extensions.Should().ContainKey("errorId", "error id must be present");
Guid.TryParse(problemDetails.Extensions["errorId"].ToString(), out Guid _)
.Should().BeTrue("error id is a GUID");
}
}
/// <summary>
/// An <see cref="IGenerateJwtFlow"/> implementation which throws an exception whenever its method is called.
/// </summary>
private class GenerateJwtFlowWhichThrowsException : IGenerateJwtFlow
{
public Task<JwtInfo> ExecuteAsync(GenerateJwtInfo input, IPrincipal flowInitiator)
{
throw new InvalidOperationException("This method must fail for testing purposes");
}
}
}
}