using DocumentService.Application.Common.DTOs; using DocumentService.Application.Common.Interfaces; using DocumentService.Application.ValidatePdfA.Queries; using DocumentService.Domain.Common.Exceptions; using FluentAssertions; using Moq; using Xunit; namespace DocumentService.Tests.Unit.Application.Features.ValidatePdfA; public class ValidatePdfAQueryHandlerTests { private readonly Mock _mockPdfProcessor; private readonly ValidatePdfAQueryHandler _handler; public ValidatePdfAQueryHandlerTests() { _mockPdfProcessor = new Mock(); _handler = new ValidatePdfAQueryHandler(_mockPdfProcessor.Object); } [Fact] public async Task Handle_ValidCompliantPdfA_ReturnsCompliantResult() { // Arrange var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; var expected = new PdfAValidationResult { IsValid = true, PdfVersion = "1.7", PageCount = 3, FileSize = 2048, Encrypted = false, PdfAVersion = "PDF/A-3b", PdfACompliant = true, Errors = [], Warnings = [] }; _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.IsValid.Should().BeTrue(); result.PageCount.Should().Be(3); result.PdfVersion.Should().Be("1.7"); result.PdfAVersion.Should().Be("PDF/A-3b"); result.PdfACompliant.Should().BeTrue(); result.Encrypted.Should().BeFalse(); result.Errors.Should().BeEmpty(); result.Warnings.Should().BeEmpty(); _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); } [Fact] public async Task Handle_NonCompliantPdfA_ReturnsErrorsAndWarnings() { // Arrange var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; var expected = new PdfAValidationResult { IsValid = false, PdfVersion = "1.4", PageCount = 2, FileSize = 1024, Encrypted = false, PdfAVersion = null, PdfACompliant = false, Errors = ["Missing XMP metadata", "Invalid color space"], Warnings = ["Embedded font not subset"] }; _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.IsValid.Should().BeFalse(); result.PdfACompliant.Should().BeFalse(); result.PdfAVersion.Should().BeNull(); result.Errors.Should().HaveCount(2).And.Contain("Missing XMP metadata").And.Contain("Invalid color space"); result.Warnings.Should().HaveCount(1).And.Contain("Embedded font not subset"); _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); } [Fact] public async Task Handle_EncryptedPdf_ReturnsEncryptedFlagAndErrors() { // Arrange var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; var expected = new PdfAValidationResult { IsValid = false, PdfVersion = "1.7", PageCount = 1, FileSize = 512, Encrypted = true, PdfAVersion = null, PdfACompliant = false, Errors = ["PDF/A documents cannot be encrypted"], Warnings = [] }; _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert result.Encrypted.Should().BeTrue(); result.IsValid.Should().BeFalse(); result.PdfACompliant.Should().BeFalse(); result.Errors.Should().Contain("PDF/A documents cannot be encrypted"); _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); } [Fact] public async Task Handle_ProcessorThrowsBadRequest_PropagatesException() { // Arrange var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())) .ThrowsAsync(new BadRequestException("Invalid PDF format")); // Act & Assert var ex = await Assert.ThrowsAsync( () => _handler.Handle(query, CancellationToken.None)); ex.Message.Should().Be("Invalid PDF format"); } [Fact] public async Task Handle_ProcessorThrowsGenericException_PropagatesException() { // Arrange var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())) .ThrowsAsync(new Exception("Unexpected processing error")); // Act & Assert await Assert.ThrowsAsync(() => _handler.Handle(query, CancellationToken.None)); } }