From 6cb852c16405799cd02ea9e947aacdc285ece751 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 6 Jan 2026 11:33:31 -0500 Subject: [PATCH] test: add FileApiClientTests with route, success, and error tests Add comprehensive unit tests for FileApiClient covering: - Route verification tests for all 4 download endpoints (POST method) - Route verification tests for 2 upload endpoints (POST method) - Success tests for downloads returning byte arrays - Success tests verifying existing data can be passed to downloads - Success tests for uploads returning IReadOnlyList - Multipart content-type and filename verification test - Error tests (404 for download, 400 validation error for upload) --- .../Services/FileApiClientTests.cs | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs diff --git a/NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs b/NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs new file mode 100644 index 0000000..e1631cc --- /dev/null +++ b/NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs @@ -0,0 +1,263 @@ +using System.Net; +using System.Text.Json; +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; + + 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 request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.UploadWorkOrders}") + .Respond("application/json", "[]"); + + 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 request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.FileIO.UploadItems}") + .Respond("application/json", "[]"); + + 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 + { + 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 List + { + new() { WorkOrderNumber = 12345, ItemNumber = "ITEM001" } + }; + _mockHttp.When(HttpMethod.Post, "*") + .Respond("application/json", JsonSerializer.Serialize(workOrders)); + + 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 List + { + new() { ItemNumber = "ITEM1", Description = "Test Item" } + }; + _mockHttp.When(HttpMethod.Post, "*") + .Respond("application/json", JsonSerializer.Serialize(items)); + + 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 + _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", "[]"); + + 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(); + } +}