diff --git a/DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs b/DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs index dedca82..761bfa0 100644 --- a/DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs +++ b/DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs @@ -1,168 +1,183 @@ using DocumentService.Client.Clients; using DocumentService.Client.Interfaces; -using DocumentService.Client.Models.Requests; using FluentAssertions; using Microsoft.Extensions.Logging.Abstractions; using System.Net; using System.Net.Http; using System.Text.Json; +using Xunit; namespace DocumentService.Tests.Unit.Client; -/// -/// Unit tests for . -/// All tests use a fake — no real HTTP calls are made. -/// public class PdfValidationClientTests { - // ?? helpers ????????????????????????????????????????????????????????????? - private static (PdfValidationClient client, MockHttpMessageHandler handler) Build(T responseBody) { var handler = MockHttpMessageHandler.ReturningJson(responseBody); var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; - var client = new PdfValidationClient(httpClient, NullLogger.Instance); - return (client, handler); + return (new PdfValidationClient(httpClient, NullLogger.Instance), handler); } - private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray(); + private static (PdfValidationClient client, MockHttpMessageHandler handler) BuildError(HttpStatusCode code) + { + var handler = MockHttpMessageHandler.ReturningStatus(code); + var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; + return (new PdfValidationClient(httpClient, NullLogger.Instance), handler); + } - // ?? ValidatePdfAsync (Stream) ???????????????????????????????????????????? + private static byte[] FakePdfBytes() => "fake-pdf"u8.ToArray(); + + // ── ValidatePdfAsync (Stream) ──────────────────────────────────────────── [Fact] public async Task ValidatePdfAsync_Stream_SendsMultipartPost() { - // Arrange - var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048 }; + var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048, IsEncrypted = false }; var (client, handler) = Build(expected); - // Act var result = await client.ValidatePdfAsync(new MemoryStream(FakePdfBytes())); - // Assert result.PageCount.Should().Be(3); result.PdfVersion.Should().Be("1.7"); + result.FileSizeBytes.Should().Be(2048); + result.IsEncrypted.Should().BeFalse(); handler.LastRequest!.Method.Should().Be(HttpMethod.Post); handler.LastRequest.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate"); handler.LastRequest.Content.Should().BeOfType(); } + [Fact] + public async Task ValidatePdfAsync_Stream_EncryptedPdf_DeserializesIsEncryptedTrue() + { + var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = true, PdfVersion = "1.6" }; + var (client, _) = Build(expected); + + var result = await client.ValidatePdfAsync(new MemoryStream(FakePdfBytes())); + + result.IsEncrypted.Should().BeTrue("encrypted PDFs should surface IsEncrypted=true"); + } + [Fact] public async Task ValidatePdfAsync_Stream_ThrowsWhenApiReturnsNull() { - // Arrange — API returns JSON null var handler = MockHttpMessageHandler.ReturningJson(null); var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; var client = new PdfValidationClient(httpClient, NullLogger.Instance); - // Act & Assert await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes()))) .Should().ThrowAsync(); } - // ?? ValidatePdfAsync (byte[]) ???????????????????????????????????????????? + [Fact] + public async Task ValidatePdfAsync_Stream_WhenApiReturns400_ThrowsHttpRequestException() + { + var (client, _) = BuildError(HttpStatusCode.BadRequest); + await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes()))) + .Should().ThrowAsync(); + } + + [Fact] + public async Task ValidatePdfAsync_Stream_WhenApiReturns500_ThrowsHttpRequestException() + { + var (client, _) = BuildError(HttpStatusCode.InternalServerError); + await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes()))) + .Should().ThrowAsync(); + } + + // ── ValidatePdfAsync (byte[]) ──────────────────────────────────────────── [Fact] public async Task ValidatePdfAsync_Bytes_SendsJsonWithBase64() { - // Arrange - var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false }; + var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false, HasAttachments = true, AttachmentCount = 2 }; var (client, handler) = Build(expected); - // Act var result = await client.ValidatePdfAsync(FakePdfBytes()); - // Assert result.PageCount.Should().Be(1); - handler.LastRequest!.Content.Should().NotBeNull(); - handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json"); - + result.HasAttachments.Should().BeTrue(); + result.AttachmentCount.Should().Be(2); + 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(); } - // ?? ValidatePdfAAsync (Stream) ??????????????????????????????????????????? + [Fact] + public async Task ValidatePdfAsync_Bytes_WhenApiReturns400_ThrowsHttpRequestException() + { + var (client, _) = BuildError(HttpStatusCode.BadRequest); + await client.Invoking(c => c.ValidatePdfAsync(FakePdfBytes())) + .Should().ThrowAsync(); + } + + // ── ValidatePdfAAsync (Stream) ─────────────────────────────────────────── [Fact] - public async Task ValidatePdfAAsync_Stream_SendsMultipartPost() + public async Task ValidatePdfAAsync_Stream_DeserializesAllFields() { - // Arrange - var expected = new PdfAValidationResult { IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2 }; + var expected = new PdfAValidationResult + { + IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2, + Errors = [], Warnings = [] + }; var (client, handler) = Build(expected); - // Act var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes())); - // Assert result.IsValid.Should().BeTrue(); result.PdfAVersion.Should().Be("PDF/A-3b"); + result.PageCount.Should().Be(2); + result.Errors.Should().BeEmpty(); handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa"); handler.LastRequest.Content.Should().BeOfType(); } [Fact] - public async Task ValidatePdfAAsync_Stream_WithErrors_ReturnsErrors() + public async Task ValidatePdfAAsync_Stream_NonCompliant_ReturnsErrors() { - // Arrange var expected = new PdfAValidationResult { IsValid = false, - Errors = new List { "Missing embedded font", "Encryption not allowed" } + Errors = ["Missing embedded font", "Encryption not allowed"], + Warnings = ["Manual verification recommended"] }; var (client, _) = Build(expected); - // Act var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes())); - // Assert result.IsValid.Should().BeFalse(); result.Errors.Should().HaveCount(2).And.Contain("Missing embedded font"); + result.Warnings.Should().HaveCount(1); } - // ?? ValidatePdfAAsync (byte[]) ??????????????????????????????????????????? + [Fact] + public async Task ValidatePdfAAsync_Stream_WhenApiReturns400_ThrowsHttpRequestException() + { + var (client, _) = BuildError(HttpStatusCode.BadRequest); + await client.Invoking(c => c.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()))) + .Should().ThrowAsync(); + } + + // ── ValidatePdfAAsync (byte[]) ─────────────────────────────────────────── [Fact] - public async Task ValidatePdfAAsync_Bytes_SendsJson() + public async Task ValidatePdfAAsync_Bytes_SendsJsonToCorrectEndpoint() { - // Arrange var expected = new PdfAValidationResult { IsValid = true }; var (client, handler) = Build(expected); - // Act await client.ValidatePdfAAsync(FakePdfBytes()); - // Assert handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa"); - handler.LastRequest.Content.Should().NotBeNull(); handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json"); } - // ?? HTTP error propagation ??????????????????????????????????????????????? - [Fact] - public async Task ValidatePdfAsync_WhenApiReturns400_ThrowsHttpRequestException() + public async Task ValidatePdfAAsync_Bytes_WhenApiReturns500_ThrowsHttpRequestException() { - // Arrange - var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.BadRequest); - var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; - var client = new PdfValidationClient(httpClient, NullLogger.Instance); - - // Act & Assert - await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes()))) - .Should().ThrowAsync(); - } - - [Fact] - public async Task ValidatePdfAsync_WhenApiReturns500_ThrowsHttpRequestException() - { - // Arrange - var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.InternalServerError); - var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }; - var client = new PdfValidationClient(httpClient, NullLogger.Instance); - - // Act & Assert - await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes()))) + var (client, _) = BuildError(HttpStatusCode.InternalServerError); + await client.Invoking(c => c.ValidatePdfAAsync(FakePdfBytes())) .Should().ThrowAsync(); } }