Files
jdescopingtool/NEW/tests/JdeScoping.Client.Tests/Services/FileApiClientTests.cs
T
Joseph Doherty 6cb852c164 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<T>
- Multipart content-type and filename verification test
- Error tests (404 for download, 400 validation error for upload)
2026-01-06 11:33:31 -05:00

264 lines
8.5 KiB
C#

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<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 List<WorkOrderViewModel>
{
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<ItemViewModel>
{
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();
}
}