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;
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);
}
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
// ?? ValidatePdfAsync (Stream) ????????????????????????????????????????????
[Fact]
public async Task ValidatePdfAsync_Stream_SendsMultipartPost()
{
// Arrange
var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048 };
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");
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_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_Bytes_SendsJsonWithBase64()
{
// Arrange
var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false };
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");
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 ValidatePdfAAsync_Stream_SendsMultipartPost()
{
// Arrange
var expected = new PdfAValidationResult { IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2 };
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");
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa");
handler.LastRequest.Content.Should().BeOfType();
}
[Fact]
public async Task ValidatePdfAAsync_Stream_WithErrors_ReturnsErrors()
{
// Arrange
var expected = new PdfAValidationResult
{
IsValid = false,
Errors = new List { "Missing embedded font", "Encryption not allowed" }
};
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");
}
// ?? ValidatePdfAAsync (byte[]) ???????????????????????????????????????????
[Fact]
public async Task ValidatePdfAAsync_Bytes_SendsJson()
{
// 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()
{
// 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())))
.Should().ThrowAsync();
}
}