using DocumentService.Application.Common.Interfaces; using DocumentService.Application.ExtractPdfAttachments; using DocumentService.Domain.Common.Exceptions; using FluentAssertions; using Moq; using Xunit; namespace DocumentService.Tests.Unit.Application.Features.ExtractAttachments; public class ExtractPdfAttachmentsHandlerTests { private readonly Mock _mockProcessor; private readonly ExtractPdfAttachmentsCommandHandler _handler; private static readonly byte[] FakePdfBytes = "%PDF"u8.ToArray(); public ExtractPdfAttachmentsHandlerTests() { _mockProcessor = new Mock(); _handler = new ExtractPdfAttachmentsCommandHandler(_mockProcessor.Object); } [Fact] public async Task Handle_PdfWithAttachments_ReturnsZipBytes() { // Arrange var command = new ExtractPdfAttachmentsCommand { PdfStream = new MemoryStream(FakePdfBytes) }; // ZIP magic bytes PK var expected = new byte[] { 0x50, 0x4B, 0x03, 0x04, 0x00, 0x00 }; _mockProcessor.Setup(x => x.ExtractAttachmentsAsync(It.IsAny())).ReturnsAsync(expected); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeEmpty(); result[0].Should().Be(0x50, "ZIP starts with 'P'"); result[1].Should().Be(0x4B, "ZIP continues with 'K'"); _mockProcessor.Verify(x => x.ExtractAttachmentsAsync(It.IsAny()), Times.Once); } [Fact] public async Task Handle_PdfWithNoAttachments_ThrowsNotFoundException() { // Arrange var command = new ExtractPdfAttachmentsCommand { PdfStream = new MemoryStream(FakePdfBytes) }; _mockProcessor.Setup(x => x.ExtractAttachmentsAsync(It.IsAny())) .ThrowsAsync(new NotFoundException("PDF does not contain any attachments")); // Act & Assert var ex = await Assert.ThrowsAsync( () => _handler.Handle(command, CancellationToken.None)); ex.Message.Should().Contain("attachments"); } [Fact] public async Task Handle_EmptyStream_ThrowsBadRequestException() { // Arrange var command = new ExtractPdfAttachmentsCommand { PdfStream = new MemoryStream(FakePdfBytes) }; _mockProcessor.Setup(x => x.ExtractAttachmentsAsync(It.IsAny())) .ThrowsAsync(new BadRequestException("PDF stream cannot be empty")); // Act & Assert var ex = await Assert.ThrowsAsync( () => _handler.Handle(command, CancellationToken.None)); ex.Message.Should().Contain("empty"); } [Fact] public async Task Handle_ProcessorThrowsGenericException_PropagatesException() { // Arrange var command = new ExtractPdfAttachmentsCommand { PdfStream = new MemoryStream(FakePdfBytes) }; _mockProcessor.Setup(x => x.ExtractAttachmentsAsync(It.IsAny())) .ThrowsAsync(new Exception("Unexpected error")); // Act & Assert await Assert.ThrowsAsync(() => _handler.Handle(command, CancellationToken.None)); } }