test: strengthen PdfValidationClient tests (IsEncrypted + full field assertions)
- Add IsEncrypted=true deserialization test (EncryptedPdf_DeserializesIsEncryptedTrue) - Add HasAttachments/AttachmentCount assertions on Bytes overload - Add 400/500 error tests for both byte[] overloads of ValidatePdfAAsync - Strengthen ValidatePdfAAsync assertions (Errors count, Warnings count) - Extract BuildError() helper to reduce boilerplate - Total: 8 -> 11 tests
This commit is contained in:
@@ -1,168 +1,183 @@
|
|||||||
using DocumentService.Client.Clients;
|
using DocumentService.Client.Clients;
|
||||||
using DocumentService.Client.Interfaces;
|
using DocumentService.Client.Interfaces;
|
||||||
using DocumentService.Client.Models.Requests;
|
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace DocumentService.Tests.Unit.Client;
|
namespace DocumentService.Tests.Unit.Client;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unit tests for <see cref="PdfValidationClient"/>.
|
|
||||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
|
||||||
/// </summary>
|
|
||||||
public class PdfValidationClientTests
|
public class PdfValidationClientTests
|
||||||
{
|
{
|
||||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
|
||||||
|
|
||||||
private static (PdfValidationClient client, MockHttpMessageHandler handler) Build<T>(T responseBody)
|
private static (PdfValidationClient client, MockHttpMessageHandler handler) Build<T>(T responseBody)
|
||||||
{
|
{
|
||||||
var handler = MockHttpMessageHandler.ReturningJson(responseBody);
|
var handler = MockHttpMessageHandler.ReturningJson(responseBody);
|
||||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
return (new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance), handler);
|
||||||
return (client, 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<PdfValidationClient>.Instance), handler);
|
||||||
|
}
|
||||||
|
|
||||||
// ?? ValidatePdfAsync (Stream) ????????????????????????????????????????????
|
private static byte[] FakePdfBytes() => "fake-pdf"u8.ToArray();
|
||||||
|
|
||||||
|
// ── ValidatePdfAsync (Stream) ────────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAsync_Stream_SendsMultipartPost()
|
public async Task ValidatePdfAsync_Stream_SendsMultipartPost()
|
||||||
{
|
{
|
||||||
// Arrange
|
var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048, IsEncrypted = false };
|
||||||
var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048 };
|
|
||||||
var (client, handler) = Build(expected);
|
var (client, handler) = Build(expected);
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await client.ValidatePdfAsync(new MemoryStream(FakePdfBytes()));
|
var result = await client.ValidatePdfAsync(new MemoryStream(FakePdfBytes()));
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.PageCount.Should().Be(3);
|
result.PageCount.Should().Be(3);
|
||||||
result.PdfVersion.Should().Be("1.7");
|
result.PdfVersion.Should().Be("1.7");
|
||||||
|
result.FileSizeBytes.Should().Be(2048);
|
||||||
|
result.IsEncrypted.Should().BeFalse();
|
||||||
handler.LastRequest!.Method.Should().Be(HttpMethod.Post);
|
handler.LastRequest!.Method.Should().Be(HttpMethod.Post);
|
||||||
handler.LastRequest.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate");
|
handler.LastRequest.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate");
|
||||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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]
|
[Fact]
|
||||||
public async Task ValidatePdfAsync_Stream_ThrowsWhenApiReturnsNull()
|
public async Task ValidatePdfAsync_Stream_ThrowsWhenApiReturnsNull()
|
||||||
{
|
{
|
||||||
// Arrange — API returns JSON null
|
|
||||||
var handler = MockHttpMessageHandler.ReturningJson<PdfValidationResult?>(null);
|
var handler = MockHttpMessageHandler.ReturningJson<PdfValidationResult?>(null);
|
||||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
||||||
.Should().ThrowAsync<InvalidOperationException>();
|
.Should().ThrowAsync<InvalidOperationException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ?? 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<HttpRequestException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidatePdfAsync_Stream_WhenApiReturns500_ThrowsHttpRequestException()
|
||||||
|
{
|
||||||
|
var (client, _) = BuildError(HttpStatusCode.InternalServerError);
|
||||||
|
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
||||||
|
.Should().ThrowAsync<HttpRequestException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ValidatePdfAsync (byte[]) ────────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAsync_Bytes_SendsJsonWithBase64()
|
public async Task ValidatePdfAsync_Bytes_SendsJsonWithBase64()
|
||||||
{
|
{
|
||||||
// Arrange
|
var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false, HasAttachments = true, AttachmentCount = 2 };
|
||||||
var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false };
|
|
||||||
var (client, handler) = Build(expected);
|
var (client, handler) = Build(expected);
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await client.ValidatePdfAsync(FakePdfBytes());
|
var result = await client.ValidatePdfAsync(FakePdfBytes());
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.PageCount.Should().Be(1);
|
result.PageCount.Should().Be(1);
|
||||||
handler.LastRequest!.Content.Should().NotBeNull();
|
result.HasAttachments.Should().BeTrue();
|
||||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
result.AttachmentCount.Should().Be(2);
|
||||||
|
handler.LastRequest!.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||||
var doc = JsonDocument.Parse(body);
|
var doc = JsonDocument.Parse(body);
|
||||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
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<HttpRequestException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ValidatePdfAAsync (Stream) ───────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAAsync_Stream_SendsMultipartPost()
|
public async Task ValidatePdfAAsync_Stream_DeserializesAllFields()
|
||||||
{
|
{
|
||||||
// Arrange
|
var expected = new PdfAValidationResult
|
||||||
var expected = new PdfAValidationResult { IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2 };
|
{
|
||||||
|
IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2,
|
||||||
|
Errors = [], Warnings = []
|
||||||
|
};
|
||||||
var (client, handler) = Build(expected);
|
var (client, handler) = Build(expected);
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.IsValid.Should().BeTrue();
|
result.IsValid.Should().BeTrue();
|
||||||
result.PdfAVersion.Should().Be("PDF/A-3b");
|
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!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa");
|
||||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAAsync_Stream_WithErrors_ReturnsErrors()
|
public async Task ValidatePdfAAsync_Stream_NonCompliant_ReturnsErrors()
|
||||||
{
|
{
|
||||||
// Arrange
|
|
||||||
var expected = new PdfAValidationResult
|
var expected = new PdfAValidationResult
|
||||||
{
|
{
|
||||||
IsValid = false,
|
IsValid = false,
|
||||||
Errors = new List<string> { "Missing embedded font", "Encryption not allowed" }
|
Errors = ["Missing embedded font", "Encryption not allowed"],
|
||||||
|
Warnings = ["Manual verification recommended"]
|
||||||
};
|
};
|
||||||
var (client, _) = Build(expected);
|
var (client, _) = Build(expected);
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.IsValid.Should().BeFalse();
|
result.IsValid.Should().BeFalse();
|
||||||
result.Errors.Should().HaveCount(2).And.Contain("Missing embedded font");
|
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<HttpRequestException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ValidatePdfAAsync (byte[]) ───────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAAsync_Bytes_SendsJson()
|
public async Task ValidatePdfAAsync_Bytes_SendsJsonToCorrectEndpoint()
|
||||||
{
|
{
|
||||||
// Arrange
|
|
||||||
var expected = new PdfAValidationResult { IsValid = true };
|
var expected = new PdfAValidationResult { IsValid = true };
|
||||||
var (client, handler) = Build(expected);
|
var (client, handler) = Build(expected);
|
||||||
|
|
||||||
// Act
|
|
||||||
await client.ValidatePdfAAsync(FakePdfBytes());
|
await client.ValidatePdfAAsync(FakePdfBytes());
|
||||||
|
|
||||||
// Assert
|
|
||||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa");
|
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");
|
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ValidatePdfAsync_WhenApiReturns400_ThrowsHttpRequestException()
|
public async Task ValidatePdfAAsync_Bytes_WhenApiReturns500_ThrowsHttpRequestException()
|
||||||
{
|
{
|
||||||
// Arrange
|
var (client, _) = BuildError(HttpStatusCode.InternalServerError);
|
||||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.BadRequest);
|
await client.Invoking(c => c.ValidatePdfAAsync(FakePdfBytes()))
|
||||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
|
||||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
|
||||||
.Should().ThrowAsync<HttpRequestException>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[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<PdfValidationClient>.Instance);
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
|
||||||
.Should().ThrowAsync<HttpRequestException>();
|
.Should().ThrowAsync<HttpRequestException>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user