Files
jdescopingtool/NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs
T
Joseph Doherty ee044d03e0 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
2026-01-30 07:12:20 -05:00

271 lines
9.4 KiB
C#

using System.Net;
using System.Text.Json;
using JdeScoping.Client.Models;
using JdeScoping.Client.Services;
using JdeScoping.Core.ApiContracts;
using JdeScoping.Core.ViewModels;
using RichardSzalay.MockHttp;
using Shouldly;
namespace JdeScoping.Client.Tests.Services;
public class FileApiClientTests
{
private readonly MockHttpMessageHandler _mockHttp;
private readonly FileApiClient _client;
private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
public FileApiClientTests()
{
_mockHttp = new MockHttpMessageHandler();
var httpClient = new HttpClient(_mockHttp) { BaseAddress = new Uri("http://localhost/") };
_client = new FileApiClient(httpClient);
}
// Route verification tests - Downloads
[Fact]
public async Task DownloadWorkOrdersTemplateAsync_CallsCorrectRoute_WithPostMethod()
{
// Arrange
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.DownloadWorkOrders}")
.Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 }));
// Act
await _client.DownloadWorkOrdersTemplateAsync();
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
[Fact]
public async Task DownloadItemsTemplateAsync_CallsCorrectRoute()
{
// Arrange
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.DownloadItems}")
.Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 }));
// Act
await _client.DownloadItemsTemplateAsync();
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
[Fact]
public async Task DownloadComponentLotsTemplateAsync_CallsCorrectRoute()
{
// Arrange
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.DownloadComponentLots}")
.Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 }));
// Act
await _client.DownloadComponentLotsTemplateAsync();
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
[Fact]
public async Task DownloadPartOperationsTemplateAsync_CallsCorrectRoute()
{
// Arrange
var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.DownloadPartOperations}")
.Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 }));
// Act
await _client.DownloadPartOperationsTemplateAsync();
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
// Route verification tests - Uploads
[Fact]
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", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
await _client.UploadWorkOrdersAsync(stream, "test.xlsx");
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
[Fact]
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", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
await _client.UploadItemsAsync(stream, "test.xlsx");
// Assert
_mockHttp.GetMatchCount(request).ShouldBe(1);
}
// Success tests - Downloads
[Fact]
public async Task DownloadWorkOrdersTemplateAsync_Success_ReturnsBytes()
{
// Arrange
var expectedBytes = new byte[] { 0x50, 0x4B, 0x03, 0x04 }; // ZIP header
_mockHttp.When(HttpMethod.Post, "*")
.Respond("application/octet-stream", new MemoryStream(expectedBytes));
// Act
var result = await _client.DownloadWorkOrdersTemplateAsync();
// Assert
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(expectedBytes);
}
[Fact]
public async Task DownloadWorkOrdersTemplateAsync_WithExistingData_SendsData()
{
// Arrange
var existingData = new List<WorkOrderViewModel>
{
new() { WorkOrderNumber = 12345 }
};
_mockHttp.When(HttpMethod.Post, "*")
.With(req => req.Content != null) // Verify body is sent
.Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 }));
// Act
var result = await _client.DownloadWorkOrdersTemplateAsync(existingData);
// Assert
result.IsSuccess.ShouldBeTrue();
}
// Success tests - Uploads
[Fact]
public async Task UploadWorkOrdersAsync_Success_ReturnsWorkOrderList()
{
// Arrange
var workOrders = new[]
{
new WorkOrderViewModel { WorkOrderNumber = 12345, ItemNumber = "ITEM001" }
};
var response = new FileUploadResult<WorkOrderViewModel> { WasSuccessful = true, Data = workOrders };
_mockHttp.When(HttpMethod.Post, "*")
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
var result = await _client.UploadWorkOrdersAsync(stream, "test.xlsx");
// Assert
result.IsSuccess.ShouldBeTrue();
result.Value.Count.ShouldBe(1);
result.Value[0].WorkOrderNumber.ShouldBe(12345);
}
[Fact]
public async Task UploadItemsAsync_Success_ReturnsItemList()
{
// Arrange
var items = new[]
{
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(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
var result = await _client.UploadItemsAsync(stream, "test.xlsx");
// Assert
result.IsSuccess.ShouldBeTrue();
result.Value.Count.ShouldBe(1);
}
// Error tests
[Fact]
public async Task DownloadWorkOrdersTemplateAsync_404_ReturnsNotFound()
{
// Arrange
_mockHttp.When(HttpMethod.Post, "*")
.Respond(HttpStatusCode.NotFound);
// Act
var result = await _client.DownloadWorkOrdersTemplateAsync();
// Assert
result.IsNotFound.ShouldBeTrue();
}
[Fact]
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 =>
{
var content = req.Content as MultipartFormDataContent;
if (content == null) return false;
// Check content type is multipart/form-data
var contentType = req.Content?.Headers.ContentType?.MediaType;
if (contentType != "multipart/form-data") return false;
// Check that filename is passed correctly
var contentDisposition = content.First().Headers.ContentDisposition;
return contentDisposition?.FileName?.Contains("test.xlsx") == true;
})
.Respond("application/json", JsonSerializer.Serialize(response, _jsonOptions));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
var result = await _client.UploadWorkOrdersAsync(stream, "test.xlsx");
// Assert
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public async Task UploadWorkOrdersAsync_400_ReturnsValidationError()
{
// Arrange - use actual ValidationProblemDetails structure
var validationProblem = new Microsoft.AspNetCore.Mvc.ValidationProblemDetails
{
Errors = { ["File"] = new[] { "Invalid file format" } }
};
_mockHttp.When(HttpMethod.Post, "*")
.Respond(HttpStatusCode.BadRequest, "application/json",
JsonSerializer.Serialize(validationProblem, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
using var stream = new MemoryStream(new byte[] { 1, 2, 3 });
// Act
var result = await _client.UploadWorkOrdersAsync(stream, "test.xlsx");
// Assert
result.IsValidationError.ShouldBeTrue();
}
}