Renamed methods across multiple client classes for consistency, shortening and aligning naming conventions (e.g., `CheckAttachmentsAsync` → `CheckAsync`, `ValidatePdfAsync` → `ValidateAsync`). Updated corresponding interfaces, unit tests, and documentation to reflect the new method names. Standardized method signatures to support both `Stream` and `byte[]` overloads consistently. Improved error handling in tests and updated examples in the README for batch processing and validation scenarios. Enhanced API endpoint overview for clarity.
223 lines
7.9 KiB
C#
223 lines
7.9 KiB
C#
using DocumentService.Application.Common.DTOs;
|
||
using DocumentService.Client.Clients;
|
||
using DocumentService.Client.Interfaces;
|
||
using DocumentService.Application.Common.DTOs.Requests;
|
||
using FluentAssertions;
|
||
using Microsoft.Extensions.Logging.Abstractions;
|
||
using System.IO.Compression;
|
||
using System.Net;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
|
||
namespace DocumentService.Tests.Unit.Client;
|
||
|
||
/// <summary>
|
||
/// Unit tests for <see cref="PdfAttachmentClient"/>.
|
||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> <20> no real HTTP calls are made.
|
||
/// </summary>
|
||
public class PdfAttachmentClientTests
|
||
{
|
||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||
|
||
private static (PdfAttachmentClient client, MockHttpMessageHandler handler) BuildJson<T>(T body)
|
||
{
|
||
var handler = MockHttpMessageHandler.ReturningJson(body);
|
||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||
return (client, handler);
|
||
}
|
||
|
||
private static (PdfAttachmentClient client, MockHttpMessageHandler handler) BuildBytes(byte[] bytes, string mediaType = "application/zip")
|
||
{
|
||
var handler = MockHttpMessageHandler.ReturningBytes(bytes, mediaType);
|
||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||
return (client, handler);
|
||
}
|
||
|
||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||
|
||
/// <summary>Builds a minimal valid ZIP containing the given entries.</summary>
|
||
private static byte[] BuildZip(Dictionary<string, string> entries)
|
||
{
|
||
using var ms = new MemoryStream();
|
||
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
|
||
{
|
||
foreach (var (name, content) in entries)
|
||
{
|
||
var entry = archive.CreateEntry(name);
|
||
using var writer = new StreamWriter(entry.Open());
|
||
writer.Write(content);
|
||
}
|
||
}
|
||
return ms.ToArray();
|
||
}
|
||
|
||
// ?? CheckAsync (Stream) ???????????????????????????????????????
|
||
|
||
[Fact]
|
||
public async Task CheckAsync_Stream_SendsMultipartPost()
|
||
{
|
||
// Arrange
|
||
var expected = new AttachmentCheckResult
|
||
{
|
||
HasAttachments = true,
|
||
AttachmentCount = 1,
|
||
Attachments = new List<AttachmentDto>
|
||
{
|
||
new() { FileName = "factur-x.xml", MimeType = "application/xml", Size = 512 }
|
||
}
|
||
};
|
||
var (client, handler) = BuildJson(expected);
|
||
|
||
// Act
|
||
var result = await client.CheckAsync(new MemoryStream(FakePdfBytes()));
|
||
|
||
// Assert
|
||
result.HasAttachments.Should().BeTrue();
|
||
result.AttachmentCount.Should().Be(1);
|
||
result.Attachments[0].FileName.Should().Be("factur-x.xml");
|
||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/attachments/check");
|
||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task CheckAsync_Stream_PdfWithNoAttachments_ReturnsFalse()
|
||
{
|
||
// Arrange
|
||
var expected = new AttachmentCheckResult { HasAttachments = false, AttachmentCount = 0, Attachments = [] };
|
||
var (client, _) = BuildJson(expected);
|
||
|
||
// Act
|
||
var result = await client.CheckAsync(new MemoryStream(FakePdfBytes()));
|
||
|
||
// Assert
|
||
result.HasAttachments.Should().BeFalse();
|
||
result.AttachmentCount.Should().Be(0);
|
||
result.Attachments.Should().BeEmpty();
|
||
}
|
||
|
||
// ?? CheckAsync (byte[]) ???????????????????????????????????????
|
||
|
||
[Fact]
|
||
public async Task CheckAsync_Bytes_SendsJsonWithBase64()
|
||
{
|
||
// Arrange
|
||
var expected = new AttachmentCheckResult { HasAttachments = false };
|
||
var (client, handler) = BuildJson(expected);
|
||
|
||
// Act
|
||
await client.CheckAsync(FakePdfBytes());
|
||
|
||
// Assert
|
||
handler.LastRequest!.Content.Should().NotBeNull();
|
||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||
var doc = JsonDocument.Parse(body);
|
||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||
}
|
||
|
||
// ?? ExtractAsync (Stream) <20> ZIP unzip ?????????????????????????
|
||
|
||
[Fact]
|
||
public async Task ExtractAsync_Stream_UnzipsAndReturnsDictionary()
|
||
{
|
||
// Arrange
|
||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||
{
|
||
["factur-x.xml"] = "<invoice>test</invoice>",
|
||
["readme.txt"] = "Hello World"
|
||
});
|
||
var (client, handler) = BuildBytes(zipBytes);
|
||
|
||
// Act
|
||
var result = await client.ExtractAsync(new MemoryStream(FakePdfBytes()));
|
||
|
||
// Assert
|
||
result.Should().HaveCount(2);
|
||
result.Should().ContainKey("factur-x.xml");
|
||
result.Should().ContainKey("readme.txt");
|
||
|
||
using var xmlStream = result["factur-x.xml"];
|
||
var xmlContent = await new StreamReader(xmlStream).ReadToEndAsync();
|
||
xmlContent.Should().Be("<invoice>test</invoice>");
|
||
|
||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/attachments/extract");
|
||
|
||
// Cleanup
|
||
foreach (var s in result.Values) s.Dispose();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task ExtractAsync_Stream_WithSingleEntry_ReturnsOneItem()
|
||
{
|
||
// Arrange
|
||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||
{
|
||
["data.xml"] = "<root/>"
|
||
});
|
||
var (client, _) = BuildBytes(zipBytes);
|
||
|
||
// Act
|
||
var result = await client.ExtractAsync(new MemoryStream(FakePdfBytes()));
|
||
|
||
// Assert
|
||
result.Should().HaveCount(1);
|
||
result.Should().ContainKey("data.xml");
|
||
|
||
foreach (var s in result.Values) s.Dispose();
|
||
}
|
||
|
||
// ?? ExtractAsync (byte[]) <20> ZIP unzip ?????????????????????????
|
||
|
||
[Fact]
|
||
public async Task ExtractAsync_Bytes_SendsJsonAndUnzips()
|
||
{
|
||
// Arrange
|
||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||
{
|
||
["invoice.xml"] = "<invoice/>"
|
||
});
|
||
var (client, handler) = BuildBytes(zipBytes);
|
||
|
||
// Act
|
||
var result = await client.ExtractAsync(FakePdfBytes());
|
||
|
||
// Assert
|
||
result.Should().ContainKey("invoice.xml");
|
||
handler.LastRequest!.Content.Should().NotBeNull();
|
||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||
|
||
foreach (var s in result.Values) s.Dispose();
|
||
}
|
||
|
||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||
|
||
[Fact]
|
||
public async Task CheckAsync_WhenApiReturns404_ThrowsHttpRequestException()
|
||
{
|
||
// Arrange
|
||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.NotFound);
|
||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||
|
||
// Act & Assert
|
||
await client.Invoking(c => c.CheckAsync(new MemoryStream(FakePdfBytes())))
|
||
.Should().ThrowAsync<HttpRequestException>();
|
||
}
|
||
|
||
[Fact]
|
||
public async Task ExtractAsync_WhenApiReturns500_ThrowsHttpRequestException()
|
||
{
|
||
// Arrange
|
||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.InternalServerError);
|
||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||
|
||
// Act & Assert
|
||
await client.Invoking(c => c.ExtractAsync(new MemoryStream(FakePdfBytes())))
|
||
.Should().ThrowAsync<HttpRequestException>();
|
||
}
|
||
}
|