feat: add health check endpoint, file upload result handling, and Playwright E2E tests

- Add /health endpoint with anonymous access for monitoring
- Add FileUploadResult<T> model and PostMultipartForFileResultAsync for proper upload response handling
- Add ApiResult.Success() factory method for interface types
- Refactor Login.razor for cleaner code
- Add comprehensive Playwright E2E test suite with fixtures and helpers
This commit is contained in:
Joseph Doherty
2026-01-30 07:12:20 -05:00
parent ae69a261d6
commit ee044d03e0
59 changed files with 11740 additions and 49 deletions
@@ -1,5 +1,6 @@
using System.Net;
using System.Text.Json;
using JdeScoping.Client.Models;
using JdeScoping.Client.Services;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.ViewModels;
@@ -12,6 +13,7 @@ public class FileApiClientTests
{
private readonly MockHttpMessageHandler _mockHttp;
private readonly FileApiClient _client;
private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
public FileApiClientTests()
{
@@ -84,8 +86,9 @@ public class FileApiClientTests
public async Task UploadWorkOrdersAsync_CallsCorrectRoute_WithPostMethod()
{
// Arrange
var response = new FileUploadResult<WorkOrderViewModel> { WasSuccessful = true, Data = Array.Empty<WorkOrderViewModel>() };
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.UploadWorkOrders}")
.Respond("application/json", "[]");
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
@@ -100,8 +103,9 @@ public class FileApiClientTests
public async Task UploadItemsAsync_CallsCorrectRoute()
{
// Arrange
var response = new FileUploadResult<ItemViewModel> { WasSuccessful = true, Data = Array.Empty<ItemViewModel>() };
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.UploadItems}")
.Respond("application/json", "[]");
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
@@ -156,12 +160,13 @@ public class FileApiClientTests
public async Task UploadWorkOrdersAsync_Success_ReturnsWorkOrderList()
{
// Arrange
var workOrders = new List<WorkOrderViewModel>
var workOrders = new[]
{
new() { WorkOrderNumber = 12345, ItemNumber = "ITEM001" }
new WorkOrderViewModel { WorkOrderNumber = 12345, ItemNumber = "ITEM001" }
};
var response = new FileUploadResult<WorkOrderViewModel> { WasSuccessful = true, Data = workOrders };
_mockHttp.When(HttpMethod.Post, "*")
.Respond("application/json", JsonSerializer.Serialize(workOrders));
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
@@ -178,12 +183,13 @@ public class FileApiClientTests
public async Task UploadItemsAsync_Success_ReturnsItemList()
{
// Arrange
var items = new List<ItemViewModel>
var items = new[]
{
new() { ItemNumber = "ITEM1", Description = "Test Item" }
new ItemViewModel { ItemNumber = "ITEM1", Description = "Test Item" }
};
var response = new FileUploadResult<ItemViewModel> { WasSuccessful = true, Data = items };
_mockHttp.When(HttpMethod.Post, "*")
.Respond("application/json", JsonSerializer.Serialize(items));
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
@@ -215,6 +221,7 @@ public class FileApiClientTests
public async Task UploadWorkOrdersAsync_VerifiesMultipartContentType_AndFilename()
{
// Arrange - verify multipart structure and filename
var response = new FileUploadResult<WorkOrderViewModel> { WasSuccessful = true, Data = Array.Empty<WorkOrderViewModel>() };
_mockHttp.When(HttpMethod.Post, "*")
.With(req =>
{
@@ -229,7 +236,7 @@ public class FileApiClientTests
var contentDisposition = content.First().Headers.ContentDisposition;
return contentDisposition?.FileName?.Contains("test.xlsx") == true;
})
.Respond("application/json", "[]");
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });