diff --git a/DocumentOperator.Tests/Unit/Infrastructure/Services/PdfProcessing/DevExpressPdfProcessorTests.cs b/DocumentOperator.Tests/Unit/Infrastructure/Services/PdfProcessing/DevExpressPdfProcessorTests.cs
index d0abe89..7aed60b 100644
--- a/DocumentOperator.Tests/Unit/Infrastructure/Services/PdfProcessing/DevExpressPdfProcessorTests.cs
+++ b/DocumentOperator.Tests/Unit/Infrastructure/Services/PdfProcessing/DevExpressPdfProcessorTests.cs
@@ -47,6 +47,17 @@ public class DevExpressPdfProcessorTests
return memoryStream.ToArray();
}
+ ///
+ /// Loads a test PDF from embedded resources as Stream.
+ ///
+ /// Name of the PDF file (e.g., "valid.pdf")
+ /// PDF content as MemoryStream (caller must dispose)
+ private static MemoryStream LoadTestPdfAsStream(string filename)
+ {
+ byte[] pdfBytes = LoadTestPdf(filename);
+ return new MemoryStream(pdfBytes);
+ }
+
///
/// Converts byte array to MemoryStream for testing
///
@@ -364,8 +375,195 @@ public class DevExpressPdfProcessorTests
}
#endregion
+
+ #region MergePdfsAsync Tests
+
+ [Fact]
+ public async Task MergePdfsAsync_TwoPdfs_ReturnsMergedPdf()
+ {
+ // Arrange
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ var streams = new List { pdf1, pdf2 };
+
+ // Act
+ byte[] mergedPdf = await _sut.MergePdfsAsync(streams);
+
+ // Assert
+ mergedPdf.Should().NotBeEmpty("merged PDF should contain data");
+ mergedPdf.Length.Should().BeGreaterThan(1000, "merged PDF should be reasonably sized");
+
+ // Verify it's a valid PDF by loading it
+ using var verifyStream = new MemoryStream(mergedPdf);
+ using var verifyProcessor = new DevExpress.Pdf.PdfDocumentProcessor();
+ verifyProcessor.LoadDocument(verifyStream);
+ verifyProcessor.Document.Pages.Count.Should().BeGreaterOrEqualTo(2, "merged PDF should have at least 2 pages");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_ThreePdfs_ReturnsMergedPdf()
+ {
+ // Arrange
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ using var pdf3 = LoadTestPdfAsStream("pdfWithMoreThanOneAttachment.pdf");
+ var streams = new List { pdf1, pdf2, pdf3 };
+
+ // Act
+ byte[] mergedPdf = await _sut.MergePdfsAsync(streams);
+
+ // Assert
+ mergedPdf.Should().NotBeEmpty();
+
+ // Verify merged PDF
+ using var verifyStream = new MemoryStream(mergedPdf);
+ using var verifyProcessor = new DevExpress.Pdf.PdfDocumentProcessor();
+ verifyProcessor.LoadDocument(verifyStream);
+ verifyProcessor.Document.Pages.Count.Should().BeGreaterOrEqualTo(3, "merged PDF should have at least 3 pages");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_WithPageRanges_MergesSelectedPages()
+ {
+ // Arrange: Merge first page of each PDF
+ using var pdf1 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf"); // Multi-page
+ using var pdf2 = LoadTestPdfAsStream("pdfWithMoreThanOneAttachment.pdf"); // Multi-page
+ var streams = new List { pdf1, pdf2 };
+ var pageRanges = new List { "1", "1" }; // Only first page from each
+
+ // Act
+ byte[] mergedPdf = await _sut.MergePdfsAsync(streams, pageRanges);
+
+ // Assert
+ mergedPdf.Should().NotBeEmpty();
+
+ // Verify merged PDF has exactly 2 pages
+ using var verifyStream = new MemoryStream(mergedPdf);
+ using var verifyProcessor = new DevExpress.Pdf.PdfDocumentProcessor();
+ verifyProcessor.LoadDocument(verifyStream);
+ verifyProcessor.Document.Pages.Count.Should().Be(2, "should have exactly 2 pages (1 from each PDF)");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_WithRangeFormat_MergesSelectedPages()
+ {
+ // Arrange: Merge 2 PDFs, second PDF with page range "1" (first page only)
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf"); // 1 page
+ using var pdf2 = LoadTestPdfAsStream("pdfWithMoreThanOneAttachment.pdf"); // PDF with attachments
+ var streams = new List { pdf1, pdf2 };
+ var pageRanges = new List { null, "1" }; // All pages from first, page 1 only from second
+
+ // Act
+ byte[] mergedPdf = await _sut.MergePdfsAsync(streams, pageRanges);
+
+ // Assert
+ mergedPdf.Should().NotBeEmpty();
+
+ // Verify merged PDF has at least 2 pages (1 from first + 1 from second)
+ using var verifyStream = new MemoryStream(mergedPdf);
+ using var verifyProcessor = new DevExpress.Pdf.PdfDocumentProcessor();
+ verifyProcessor.LoadDocument(verifyStream);
+ verifyProcessor.Document.Pages.Count.Should().BeGreaterOrEqualTo(2, "should have at least 2 pages");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_SinglePdf_ThrowsBadRequestException()
+ {
+ // Arrange: Only one PDF (minimum 2 required)
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ var streams = new List { pdf1 };
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("*at least 2*");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_EmptyStream_ThrowsBadRequestException()
+ {
+ // Arrange: One valid PDF + one empty stream
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var emptyStream = new MemoryStream();
+ var streams = new List { pdf1, emptyStream };
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("*empty*");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_StreamNotAtBeginning_ThrowsBadRequestException()
+ {
+ // Arrange: Valid PDFs but second stream not at Position=0
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ pdf2.Position = 10; // Move position away from beginning
+ var streams = new List { pdf1, pdf2 };
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams);
+
+ // Assert
+ await act.Should().ThrowAsync();
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_InvalidPageRange_ThrowsBadRequestException()
+ {
+ // Arrange: Invalid page range format
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ var streams = new List { pdf1, pdf2 };
+ var pageRanges = new List { "1", "abc" }; // Invalid format
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams, pageRanges);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("*Invalid page*");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_PageRangeExceedsPageCount_ThrowsBadRequestException()
+ {
+ // Arrange: Page range exceeds document page count
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf"); // 1 page
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ var streams = new List { pdf1, pdf2 };
+ var pageRanges = new List { "1-99", null }; // 99 exceeds pdf1 page count
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams, pageRanges);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("*exceeds document page count*");
+ }
+
+ [Fact]
+ public async Task MergePdfsAsync_PageRangesMismatch_ThrowsBadRequestException()
+ {
+ // Arrange: Page ranges count doesn't match streams count
+ using var pdf1 = LoadTestPdfAsStream("valid.pdf");
+ using var pdf2 = LoadTestPdfAsStream("pdfWithSwissQRCode.pdf");
+ var streams = new List { pdf1, pdf2 };
+ var pageRanges = new List { "1" }; // Only 1 range for 2 PDFs
+
+ // Act
+ var act = async () => await _sut.MergePdfsAsync(streams, pageRanges);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("*must match*");
+ }
+
+ #endregion
}
-
-
-