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; public class CheckPdfAttachmentsQueryHandlerTests { private readonly Mock _mockPdfProcessor; private readonly Mock _mockMapper; private readonly CheckPdfAttachmentsQueryHandler _sut; public CheckPdfAttachmentsQueryHandlerTests() { _mockPdfProcessor = new Mock(); _mockMapper = new Mock(); _sut = new CheckPdfAttachmentsQueryHandler(_mockPdfProcessor.Object, _mockMapper.Object); } [Fact] public async Task Handle_PdfWithAttachments_ReturnsAttachmentDetails() { // Arrange 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)]); var expectedDto = new AttachmentCheckResult { HasAttachments = true, AttachmentCount = 2, Attachments = [ new() { FileName = "invoice.xml", MimeType = "text/xml", Size = 1024 }, new() { FileName = "metadata.json", MimeType = "application/json", Size = 512 } ] }; _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.Should().NotBeNull(); 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_PdfWithNoAttachments_ReturnsEmptyResult() { // Arrange 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); _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)); } }