From 9040aed1dadde9a3b53975a4c2b4d3df15ff5a1f Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 28 Aug 2026 11:46:09 +0200 Subject: [PATCH] test: rewrite existing Application handler tests (no mapper mock, full assertions) ValidatePdfHandlerTests: - Remove IMapper mock (handler no longer uses mapper) - Add: EncryptedPdf_ReturnsIsEncryptedTrue - Add: PdfWithAttachments_ReturnsAttachmentData - Add: ProcessorThrowsGenericException_PropagatesException ValidatePdfAQueryHandlerTests: - Remove IMapper mock - Strengthen assertions (Errors count, Warnings content) - Add: ProcessorThrowsGenericException_PropagatesException CheckPdfAttachmentsQueryHandlerTests: - Remove duplicate Handle_WithBase64Pdf test (identical to WithPdfBytes) - Add: ProcessorThrowsBadRequest_PropagatesException - Add: ProcessorThrowsGenericException_PropagatesException - Strengthen: assert individual attachment fields (FileName, MimeType, Size) --- .../CheckPdfAttachmentsQueryHandlerTests.cs | 102 +++++------ .../ValidatePdf/ValidatePdfHandlerTests.cs | 103 ++++++----- .../ValidatePdfAQueryHandlerTests.cs | 173 +++++------------- 3 files changed, 148 insertions(+), 230 deletions(-) diff --git a/DocumentOperator.Tests/Unit/Application/CheckPdfAttachments/CheckPdfAttachmentsQueryHandlerTests.cs b/DocumentOperator.Tests/Unit/Application/CheckPdfAttachments/CheckPdfAttachmentsQueryHandlerTests.cs index 62eb85c..3c1677c 100644 --- a/DocumentOperator.Tests/Unit/Application/CheckPdfAttachments/CheckPdfAttachmentsQueryHandlerTests.cs +++ b/DocumentOperator.Tests/Unit/Application/CheckPdfAttachments/CheckPdfAttachmentsQueryHandlerTests.cs @@ -2,16 +2,13 @@ using AutoMapper; using DocumentService.Application.CheckPdfAttachments.Queries; using DocumentService.Application.Common.DTOs; using DocumentService.Application.Common.Interfaces; - +using DocumentService.Domain.Common.Exceptions; using FluentAssertions; using Moq; +using Xunit; namespace DocumentService.Tests.Unit.Application.CheckPdfAttachments; -/// -/// Unit tests for CheckPdfAttachmentsQueryHandler. -/// Tests handler logic with mocked dependencies (IPdfProcessor, IMapper). -/// public class CheckPdfAttachmentsQueryHandlerTests { private readonly Mock _mockPdfProcessor; @@ -26,26 +23,16 @@ public class CheckPdfAttachmentsQueryHandlerTests } [Fact] - public async Task Handle_WithPdfBytes_CallsProcessorAndMapper() + public async Task Handle_PdfWithAttachments_ReturnsAttachmentDetails() { // Arrange - byte[] pdfBytes = "fake-pdf-content"u8.ToArray(); - var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) }; - + var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream("fake-pdf"u8.ToArray()) }; var domainResult = new AttachmentInfo( - hasAttachments: true, - attachmentCount: 2, - attachments: - [ - new("invoice.xml", "text/xml", 1024), - new("metadata.json", "application/json", 512) - ] - ); - + hasAttachments: true, attachmentCount: 2, + attachments: [new("invoice.xml", "text/xml", 1024), new("metadata.json", "application/json", 512)]); var expectedDto = new AttachmentCheckResult { - HasAttachments = true, - AttachmentCount = 2, + HasAttachments = true, AttachmentCount = 2, Attachments = [ new() { FileName = "invoice.xml", MimeType = "text/xml", Size = 1024 }, @@ -61,20 +48,22 @@ public class CheckPdfAttachmentsQueryHandlerTests // Assert result.Should().NotBeNull(); - result.Should().BeEquivalentTo(expectedDto); - + result.HasAttachments.Should().BeTrue(); + result.AttachmentCount.Should().Be(2); + result.Attachments.Should().HaveCount(2); + result.Attachments[0].FileName.Should().Be("invoice.xml"); + result.Attachments[0].MimeType.Should().Be("text/xml"); + result.Attachments[0].Size.Should().Be(1024); _mockPdfProcessor.Verify(p => p.CheckAttachmentsAsync(It.IsAny()), Times.Once); _mockMapper.Verify(m => m.Map(domainResult), Times.Once); } [Fact] - public async Task Handle_WithBase64Pdf_DecodesAndCallsProcessor() + public async Task Handle_PdfWithNoAttachments_ReturnsEmptyResult() { // Arrange - byte[] pdfBytes = "fake-pdf-content"u8.ToArray(); - var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) }; - - var domainResult = new AttachmentInfo(false, 0, []); + var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream("fake-pdf"u8.ToArray()) }; + var domainResult = AttachmentInfo.Empty; var expectedDto = new AttachmentCheckResult { HasAttachments = false, AttachmentCount = 0, Attachments = [] }; _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny())).ReturnsAsync(domainResult); @@ -83,40 +72,37 @@ public class CheckPdfAttachmentsQueryHandlerTests // Act var result = await _sut.Handle(query, CancellationToken.None); - // Assert - result.Should().NotBeNull(); - result.HasAttachments.Should().BeFalse(); - result.AttachmentCount.Should().Be(0); - - _mockPdfProcessor.Verify(p => p.CheckAttachmentsAsync(It.IsAny()), Times.Once); - } - - [Fact] - public async Task Handle_WithEmptyAttachments_ReturnsEmptyList() - { - // Arrange - byte[] pdfBytes = "fake-pdf-content"u8.ToArray(); - var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream(pdfBytes) }; - - var domainResult = new AttachmentInfo(false, 0, []); - var expectedDto = new AttachmentCheckResult - { - HasAttachments = false, - AttachmentCount = 0, - Attachments = [] - }; - - _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny())).ReturnsAsync(domainResult); - _mockMapper.Setup(m => m.Map(domainResult)).Returns(expectedDto); - - // Act - var result = await _sut.Handle(query, CancellationToken.None); - // Assert result.HasAttachments.Should().BeFalse(); result.AttachmentCount.Should().Be(0); result.Attachments.Should().BeEmpty(); + _mockPdfProcessor.Verify(p => p.CheckAttachmentsAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ProcessorThrowsBadRequest_PropagatesException() + { + // Arrange + var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream("fake-pdf"u8.ToArray()) }; + _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny())) + .ThrowsAsync(new BadRequestException("PDF stream cannot be empty")); + + // Act & Assert + var ex = await Assert.ThrowsAsync( + () => _sut.Handle(query, CancellationToken.None)); + ex.Message.Should().Be("PDF stream cannot be empty"); + } + + [Fact] + public async Task Handle_ProcessorThrowsGenericException_PropagatesException() + { + // Arrange + var query = new CheckPdfAttachmentsQuery { PdfStream = new MemoryStream("fake-pdf"u8.ToArray()) }; + _mockPdfProcessor.Setup(p => p.CheckAttachmentsAsync(It.IsAny())) + .ThrowsAsync(new InvalidOperationException("Unexpected error")); + + // Act & Assert + await Assert.ThrowsAsync( + () => _sut.Handle(query, CancellationToken.None)); } } - - diff --git a/DocumentOperator.Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs b/DocumentOperator.Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs index f1b3699..51ec94b 100644 --- a/DocumentOperator.Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs +++ b/DocumentOperator.Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs @@ -1,4 +1,3 @@ -using AutoMapper; using DocumentService.Application.Common.DTOs; using DocumentService.Application.Common.Interfaces; using DocumentService.Application.ValidatePdf.Queries; @@ -12,47 +11,22 @@ namespace DocumentService.Tests.Unit.Application.Features.ValidatePdf; public class ValidatePdfHandlerTests { private readonly Mock _mockPdfProcessor; - private readonly Mock _mockMapper; private readonly ValidatePdfQueryHandler _handler; public ValidatePdfHandlerTests() { _mockPdfProcessor = new Mock(); - _mockMapper = new Mock(); - _handler = new ValidatePdfQueryHandler(_mockPdfProcessor.Object, _mockMapper.Object); + _handler = new ValidatePdfQueryHandler(_mockPdfProcessor.Object); } [Fact] - public async Task Handle_ValidPdf_ReturnsPdfMetadata() + public async Task Handle_ValidPdf_ReturnsResult() { // Arrange - var pdfBytes = "%PDF"u8.ToArray(); // "%PDF" - var query = new ValidatePdfQuery { PdfStream = new MemoryStream(pdfBytes) }; + var query = new ValidatePdfQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + var expected = new PdfValidationResult(5, 1024, 1024 / 1024.0 / 1024.0, "1.4", false, 0, false); - var domainMetadata = new PdfMetadata( - pageCount: 5, - fileSizeBytes: 1024, - pdfVersion: "1.4", - hasAttachments: false, - attachmentCount: 0 - ); - - var expectedDto = new PdfValidationResult( - PageCount: 5, - FileSizeBytes: 1024, - FileSizeMB: 0.00, - PdfVersion: "1.4", - HasAttachments: false, - AttachmentCount: 0 - ); - - _mockPdfProcessor - .Setup(x => x.ValidateAsync(It.IsAny())) - .ReturnsAsync(domainMetadata); - - _mockMapper - .Setup(x => x.Map(domainMetadata)) - .Returns(expectedDto); + _mockPdfProcessor.Setup(x => x.ValidateAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); @@ -64,28 +38,71 @@ public class ValidatePdfHandlerTests result.PdfVersion.Should().Be("1.4"); result.HasAttachments.Should().BeFalse(); result.AttachmentCount.Should().Be(0); - + result.IsEncrypted.Should().BeFalse(); _mockPdfProcessor.Verify(x => x.ValidateAsync(It.IsAny()), Times.Once); - _mockMapper.Verify(x => x.Map(domainMetadata), Times.Once); } [Fact] - public async Task Handle_PdfProcessorThrowsException_PropagatesException() + public async Task Handle_EncryptedPdf_ReturnsIsEncryptedTrue() { // Arrange - var pdfBytes = "%PDF"u8.ToArray(); // "%PDF" - var query = new ValidatePdfQuery { PdfStream = new MemoryStream(pdfBytes) }; + var query = new ValidatePdfQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + var expected = new PdfValidationResult(1, 512, 512 / 1024.0 / 1024.0, "1.7", false, 0, true); - _mockPdfProcessor - .Setup(x => x.ValidateAsync(It.IsAny())) + _mockPdfProcessor.Setup(x => x.ValidateAsync(It.IsAny())).ReturnsAsync(expected); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.IsEncrypted.Should().BeTrue(); + result.PageCount.Should().Be(1); + _mockPdfProcessor.Verify(x => x.ValidateAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_PdfWithAttachments_ReturnsAttachmentData() + { + // Arrange + var query = new ValidatePdfQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + var expected = new PdfValidationResult(2, 4096, 4096 / 1024.0 / 1024.0, "1.6", true, 3, false); + + _mockPdfProcessor.Setup(x => x.ValidateAsync(It.IsAny())).ReturnsAsync(expected); + + // Act + var result = await _handler.Handle(query, CancellationToken.None); + + // Assert + result.HasAttachments.Should().BeTrue(); + result.AttachmentCount.Should().Be(3); + _mockPdfProcessor.Verify(x => x.ValidateAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task Handle_ProcessorThrowsBadRequest_PropagatesException() + { + // Arrange + var query = new ValidatePdfQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + _mockPdfProcessor.Setup(x => x.ValidateAsync(It.IsAny())) .ThrowsAsync(new BadRequestException("Invalid PDF format")); // Act & Assert - var exception = await Assert.ThrowsAsync( - () => _handler.Handle(query, CancellationToken.None) - ); - - exception.Message.Should().Be("Invalid PDF format"); + var ex = await Assert.ThrowsAsync( + () => _handler.Handle(query, CancellationToken.None)); + ex.Message.Should().Be("Invalid PDF format"); _mockPdfProcessor.Verify(x => x.ValidateAsync(It.IsAny()), Times.Once); } + + [Fact] + public async Task Handle_ProcessorThrowsGenericException_PropagatesException() + { + // Arrange + var query = new ValidatePdfQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + _mockPdfProcessor.Setup(x => x.ValidateAsync(It.IsAny())) + .ThrowsAsync(new InvalidOperationException("Unexpected error")); + + // Act & Assert + await Assert.ThrowsAsync( + () => _handler.Handle(query, CancellationToken.None)); + } } diff --git a/DocumentOperator.Tests/Unit/Application/Features/ValidatePdfA/ValidatePdfAQueryHandlerTests.cs b/DocumentOperator.Tests/Unit/Application/Features/ValidatePdfA/ValidatePdfAQueryHandlerTests.cs index 22d9c8a..2d5ae15 100644 --- a/DocumentOperator.Tests/Unit/Application/Features/ValidatePdfA/ValidatePdfAQueryHandlerTests.cs +++ b/DocumentOperator.Tests/Unit/Application/Features/ValidatePdfA/ValidatePdfAQueryHandlerTests.cs @@ -1,9 +1,7 @@ -using AutoMapper; 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; @@ -13,61 +11,32 @@ namespace DocumentService.Tests.Unit.Application.Features.ValidatePdfA; public class ValidatePdfAQueryHandlerTests { private readonly Mock _mockPdfProcessor; - private readonly Mock _mockMapper; private readonly ValidatePdfAQueryHandler _handler; public ValidatePdfAQueryHandlerTests() { _mockPdfProcessor = new Mock(); - _mockMapper = new Mock(); - _handler = new ValidatePdfAQueryHandler(_mockPdfProcessor.Object, _mockMapper.Object); + _handler = new ValidatePdfAQueryHandler(_mockPdfProcessor.Object); } [Fact] - public async Task Handle_ValidPdfA_ReturnsPdfAMetadata() + public async Task Handle_ValidCompliantPdfA_ReturnsCompliantResult() { // Arrange - var pdfBytes = "%PDF"u8.ToArray(); // "%PDF" - var query = new ValidatePdfAQuery { PdfStream = new MemoryStream(pdfBytes) }; - - var domainMetadata = new PdfAMetadata( - isValid: true, - pdfVersion: "1.7", - pageCount: 3, - fileSizeBytes: 2048, - encrypted: false, - pdfaVersion: "PDF/A-3b", - pdfaCompliant: true, - errors: [], - warnings: [] - ); - - var expectedDto = new PdfAValidationResult + 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 = new List(), - Warnings = new List() + 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(domainMetadata); - - _mockMapper - .Setup(x => x.Map(domainMetadata)) - .Returns(expectedDto); + _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert - result.Should().NotBeNull(); result.IsValid.Should().BeTrue(); result.PageCount.Should().Be(3); result.PdfVersion.Should().Be("1.7"); @@ -76,139 +45,85 @@ public class ValidatePdfAQueryHandlerTests result.Encrypted.Should().BeFalse(); result.Errors.Should().BeEmpty(); result.Warnings.Should().BeEmpty(); - _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); - _mockMapper.Verify(x => x.Map(domainMetadata), Times.Once); } [Fact] public async Task Handle_NonCompliantPdfA_ReturnsErrorsAndWarnings() { // Arrange - var pdfBytes = new byte[] { 0x25, 0x50, 0x44, 0x46 }; // "%PDF" - var query = new ValidatePdfAQuery { PdfStream = new MemoryStream(pdfBytes) }; - - var errors = new List { "Missing XMP metadata", "Invalid color space" }; - var warnings = new List { "Embedded font not subset" }; - - var domainMetadata = new PdfAMetadata( - isValid: false, - pdfVersion: "1.4", - pageCount: 2, - fileSizeBytes: 1024, - encrypted: false, - pdfaVersion: null, - pdfaCompliant: false, - errors: errors, - warnings: warnings - ); - - var expectedDto = new PdfAValidationResult + 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 = errors, - Warnings = warnings + 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(domainMetadata); - - _mockMapper - .Setup(x => x.Map(domainMetadata)) - .Returns(expectedDto); + _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert - result.Should().NotBeNull(); result.IsValid.Should().BeFalse(); result.PdfACompliant.Should().BeFalse(); result.PdfAVersion.Should().BeNull(); - result.Errors.Should().HaveCount(2); - result.Errors.Should().Contain("Missing XMP metadata"); - result.Warnings.Should().HaveCount(1); - + 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_ReturnsEncryptedFlag() + public async Task Handle_EncryptedPdf_ReturnsEncryptedFlagAndErrors() { // Arrange - var pdfBytes = new byte[] { 0x25, 0x50, 0x44, 0x46 }; // "%PDF" - var query = new ValidatePdfAQuery { PdfStream = new MemoryStream(pdfBytes) }; - - var domainMetadata = new PdfAMetadata( - isValid: true, - pdfVersion: "1.7", - pageCount: 1, - fileSizeBytes: 512, - encrypted: true, - pdfaVersion: null, - pdfaCompliant: false, - errors: new List { "Encrypted PDFs cannot be PDF/A compliant" }, - warnings: new List() - ); - - var expectedDto = new PdfAValidationResult + var query = new ValidatePdfAQuery { PdfStream = new MemoryStream("%PDF"u8.ToArray()) }; + var expected = new PdfAValidationResult { - IsValid = true, - PdfVersion = "1.7", - PageCount = 1, - FileSize = 512, - Encrypted = true, - PdfAVersion = null, - PdfACompliant = false, - Errors = new List { "Encrypted PDFs cannot be PDF/A compliant" }, - Warnings = new List() + 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(domainMetadata); - - _mockMapper - .Setup(x => x.Map(domainMetadata)) - .Returns(expectedDto); + _mockPdfProcessor.Setup(x => x.ValidatePdfAAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(query, CancellationToken.None); // Assert - result.Should().NotBeNull(); result.Encrypted.Should().BeTrue(); + result.IsValid.Should().BeFalse(); result.PdfACompliant.Should().BeFalse(); - result.Errors.Should().Contain("Encrypted PDFs cannot be PDF/A compliant"); - + result.Errors.Should().Contain("PDF/A documents cannot be encrypted"); _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); } [Fact] - public async Task Handle_PdfProcessorThrowsException_PropagatesException() + public async Task Handle_ProcessorThrowsBadRequest_PropagatesException() { // Arrange - var pdfBytes = new byte[] { 0x25, 0x50, 0x44, 0x46 }; // "%PDF" - var query = new ValidatePdfAQuery { PdfStream = new MemoryStream(pdfBytes) }; - - _mockPdfProcessor - .Setup(x => x.ValidatePdfAAsync(It.IsAny())) + 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 exception = await Assert.ThrowsAsync( - () => _handler.Handle(query, CancellationToken.None) - ); + var ex = await Assert.ThrowsAsync( + () => _handler.Handle(query, CancellationToken.None)); + ex.Message.Should().Be("Invalid PDF format"); + } - exception.Message.Should().Be("Invalid PDF format"); - _mockPdfProcessor.Verify(x => x.ValidatePdfAAsync(It.IsAny()), Times.Once); + [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)); } } -