Init
This commit is contained in:
130
Prefab.Tests/Unit/Web/Shared/ExceptionHandlerShould.cs
Normal file
130
Prefab.Tests/Unit/Web/Shared/ExceptionHandlerShould.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Prefab.Handler;
|
||||
using Prefab.Catalog.Domain.Exceptions;
|
||||
using Prefab.Web.Shared;
|
||||
using Shouldly;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Prefab.Tests.Unit.Web.Shared;
|
||||
|
||||
public sealed class ExceptionHandlerShould
|
||||
{
|
||||
[Fact]
|
||||
public async Task ReturnBadRequestForValidationFailures()
|
||||
{
|
||||
var failure = new ValidationFailure("productId", "Either productId or sku must be provided.");
|
||||
var exception = new ValidationException(new[] { failure });
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new MemoryStream();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IHandlerContextAccessor, HandlerContextAccessor>();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
context.RequestServices = provider;
|
||||
|
||||
var middleware = new ExceptionHandler(
|
||||
_ => throw exception,
|
||||
NullLogger<ExceptionHandler>.Instance,
|
||||
Options.Create(new JsonOptions()));
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
context.Response.StatusCode.ShouldBe(StatusCodes.Status400BadRequest);
|
||||
|
||||
context.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(context.Response.Body);
|
||||
var payload = await reader.ReadToEndAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(payload);
|
||||
var root = document.RootElement;
|
||||
|
||||
var messages = new List<string>();
|
||||
if (root.TryGetProperty("errors", out var errorsElement))
|
||||
{
|
||||
messages.AddRange(
|
||||
errorsElement
|
||||
.EnumerateObject()
|
||||
.SelectMany(property => property.Value.EnumerateArray())
|
||||
.Select(element => element.GetString())
|
||||
.Where(message => !string.IsNullOrWhiteSpace(message))!
|
||||
.Cast<string>());
|
||||
}
|
||||
|
||||
if (messages.Count == 0 && root.TryGetProperty("detail", out var detailElement))
|
||||
{
|
||||
messages.Add(detailElement.GetString() ?? string.Empty);
|
||||
}
|
||||
|
||||
messages.ShouldNotBeEmpty(payload);
|
||||
messages.ShouldContain(message => message.Equals("Either productId or sku must be provided.", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnBadRequestForDomainExceptions()
|
||||
{
|
||||
var exception = new DomainValidationException("Demo domain failure.");
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new MemoryStream();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IHandlerContextAccessor, HandlerContextAccessor>();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
context.RequestServices = provider;
|
||||
|
||||
var middleware = new ExceptionHandler(
|
||||
_ => throw exception,
|
||||
NullLogger<ExceptionHandler>.Instance,
|
||||
Options.Create(new JsonOptions()));
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
context.Response.StatusCode.ShouldBe(StatusCodes.Status400BadRequest);
|
||||
context.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(context.Response.Body);
|
||||
var payload = await reader.ReadToEndAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(payload);
|
||||
var detail = document.RootElement.GetProperty("detail").GetString();
|
||||
document.RootElement.GetProperty("type").GetString().ShouldBe("https://prefab.dev/problems/domain-error");
|
||||
detail.ShouldNotBeNull();
|
||||
detail!.ShouldContain("Demo domain failure.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReturnConflictForConcurrencyExceptions()
|
||||
{
|
||||
var exception = new DbUpdateConcurrencyException("Concurrency conflict");
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new MemoryStream();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IHandlerContextAccessor, HandlerContextAccessor>();
|
||||
using var provider = services.BuildServiceProvider();
|
||||
context.RequestServices = provider;
|
||||
|
||||
var middleware = new ExceptionHandler(
|
||||
_ => throw exception,
|
||||
NullLogger<ExceptionHandler>.Instance,
|
||||
Options.Create(new JsonOptions()));
|
||||
|
||||
await middleware.InvokeAsync(context);
|
||||
|
||||
context.Response.StatusCode.ShouldBe(StatusCodes.Status409Conflict);
|
||||
context.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(context.Response.Body);
|
||||
var payload = await reader.ReadToEndAsync(TestContext.Current.CancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(payload);
|
||||
document.RootElement.GetProperty("type").GetString().ShouldBe("https://prefab.dev/problems/concurrency-conflict");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user