using DocumentService.Application.Common.Interfaces; using DocumentService.Application.MergePdfs; using DocumentService.Domain.Common.Exceptions; using FluentAssertions; using Moq; using Xunit; namespace DocumentService.Tests.Unit.Application.Features.MergePdfs; public class MergePdfsCommandHandlerTests { private readonly Mock _mockProcessor; private readonly MergePdfsCommandHandler _handler; public MergePdfsCommandHandlerTests() { _mockProcessor = new Mock(); _handler = new MergePdfsCommandHandler(_mockProcessor.Object); } [Fact] public async Task Handle_TwoStreams_ReturnsMergedBytes() { // Arrange var stream1 = new MemoryStream("%PDF-1"u8.ToArray()); var stream2 = new MemoryStream("%PDF-2"u8.ToArray()); var command = new MergePdfsCommand { PdfStreams = [stream1, stream2] }; var expected = "%PDF-merged"u8.ToArray(); _mockProcessor.Setup(x => x.MergePdfsAsync(It.IsAny>(), null)) .ReturnsAsync(expected); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull().And.NotBeEmpty(); result.Should().BeEquivalentTo(expected); _mockProcessor.Verify(x => x.MergePdfsAsync(It.IsAny>(), null), Times.Once); } [Fact] public async Task Handle_WithPageRanges_PassesRangesToProcessor() { // Arrange var stream1 = new MemoryStream("%PDF-1"u8.ToArray()); var stream2 = new MemoryStream("%PDF-2"u8.ToArray()); var pageRanges = new List { "1-2", null }; var command = new MergePdfsCommand { PdfStreams = [stream1, stream2], PageRanges = pageRanges }; var expected = new byte[] { 0x25, 0x50, 0x44, 0x46 }; _mockProcessor.Setup(x => x.MergePdfsAsync(It.IsAny>(), pageRanges)) .ReturnsAsync(expected); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().BeEquivalentTo(expected); _mockProcessor.Verify(x => x.MergePdfsAsync(It.IsAny>(), pageRanges), Times.Once); } [Fact] public async Task Handle_ProcessorThrowsBadRequest_PropagatesException() { // Arrange var command = new MergePdfsCommand { PdfStreams = [new MemoryStream("%PDF-1"u8.ToArray()), new MemoryStream("%PDF-2"u8.ToArray())] }; _mockProcessor.Setup(x => x.MergePdfsAsync(It.IsAny>(), null)) .ThrowsAsync(new BadRequestException("At least 2 PDF files are required")); // Act & Assert var ex = await Assert.ThrowsAsync( () => _handler.Handle(command, CancellationToken.None)); ex.Message.Should().Be("At least 2 PDF files are required"); } [Fact] public async Task Handle_ProcessorThrowsGenericException_PropagatesException() { // Arrange var command = new MergePdfsCommand { PdfStreams = [new MemoryStream("%PDF"u8.ToArray()), new MemoryStream("%PDF"u8.ToArray())] }; _mockProcessor.Setup(x => x.MergePdfsAsync(It.IsAny>(), null)) .ThrowsAsync(new InvalidOperationException("Unexpected error")); // Act & Assert await Assert.ThrowsAsync( () => _handler.Handle(command, CancellationToken.None)); } }