From 6461b931317b83e2a5be9670d2af4b41f4bba27a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20Sz=C3=BCcs?=
<127139797+balazs-szucs@users.noreply.github.com>
Date: Wed, 25 Jun 2025 19:33:50 +0200
Subject: [PATCH] feat: add JUnit tests for EML to PDF conversion (#3806)
# Description of Changes
This PR introduces tests for EML-to-PDF feature, but indirectly tests
also HTML-to-PDF as well as Attachments feature.
- Adds JUnit tests for EML-to-PDF conversion, including scenarios with
file attachments, multipart, complex, CSS styled etc.. emails.
- Confirms that adding Angus Mail as a runtime dependency does not
introduce any issues or regressions.
PR is relevant after #3781
---
## Checklist
### General
- [x] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [x] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md)
(if applicable)
- [x] I have performed a self-review of my own code
- [x] My changes generate no new warnings
### Documentation
- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)
### UI Changes (if applicable)
- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)
### Testing (if applicable)
- [x] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing)
for more details.
---
.../software/common/util/EmlToPdfTest.java | 883 ++++++++++++++++++
1 file changed, 883 insertions(+)
create mode 100644 common/src/test/java/stirling/software/common/util/EmlToPdfTest.java
diff --git a/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java b/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java
new file mode 100644
index 000000000..75a744e39
--- /dev/null
+++ b/common/src/test/java/stirling/software/common/util/EmlToPdfTest.java
@@ -0,0 +1,883 @@
+package stirling.software.common.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import stirling.software.common.model.api.converters.EmlToPdfRequest;
+import stirling.software.common.service.CustomPDFDocumentFactory;
+
+@DisplayName("EML to PDF Conversion tests")
+class EmlToPdfTest {
+
+ // Focus on testing EML to HTML conversion functionality since the PDF conversion relies on WeasyPrint
+ // But HTML to PDF conversion is also briefly tested at PdfConversionTests class.
+ private void testEmailConversion(String emlContent, String[] expectedContent, boolean includeAttachments) throws IOException {
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = includeAttachments ? createRequestWithAttachments() : createBasicRequest();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ for (String expected : expectedContent) {
+ assertTrue(htmlResult.contains(expected), "HTML should contain: " + expected);
+ }
+ }
+
+ @Nested
+ @DisplayName("Core EML Parsing")
+ class CoreParsingTests {
+ @Test
+ @DisplayName("Should parse simple text email correctly")
+ void parseSimpleTextEmail() throws IOException {
+ String emlContent = createSimpleTextEmail(
+ "sender@example.com",
+ "recipient@example.com",
+ "Simple Test Subject",
+ "This is a simple plain text email body.");
+
+ testEmailConversion(emlContent, new String[] {
+ "Simple Test Subject",
+ "sender@example.com",
+ "recipient@example.com",
+ "This is a simple plain text email body",
+ ""
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should parse email with missing Subject and To headers")
+ void parseEmailWithMissingHeaders() throws IOException {
+ String emlContent = createEmailWithCustomHeaders();
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("sender@example.com"));
+ assertTrue(htmlResult.contains("This is an email body"));
+ assertTrue(htmlResult.contains("
") ||
+ htmlResult.contains("No Subject"));
+ }
+
+ @Test
+ @DisplayName("Should parse HTML email with styling")
+ void parseHtmlEmailWithStyling() throws IOException {
+ String htmlBody = "" +
+ "" +
+ "This is HTML content with styling.
" +
+ "";
+
+ String emlContent = createHtmlEmail(
+ "html@example.com", "user@example.com", "HTML Email Test", htmlBody);
+
+ testEmailConversion(emlContent, new String[] {
+ "HTML Email Test",
+ "Important Notice",
+ "HTML content",
+ "font-weight: bold"
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should parse multipart email with attachments")
+ void parseMultipartEmailWithAttachments() throws IOException {
+ String boundary = "----=_Part_" + getTimestamp();
+ String emlContent = createMultipartEmailWithAttachment(
+ "multipart@example.com",
+ "user@example.com",
+ "Multipart Email Test",
+ "This email has both text content and an attachment.",
+ boundary,
+ "document.txt",
+ "Sample attachment content");
+
+ testEmailConversion(emlContent, new String[] {
+ "Multipart Email Test",
+ "This email has both text content"
+ }, true);
+ }
+ }
+
+ @Nested
+ @DisplayName("Email Encoding Support")
+ class EncodingTests {
+
+ @Test
+ @DisplayName("Should handle international characters and UTF-8")
+ void handleInternationalCharacters() throws IOException {
+ String bodyWithIntlChars = "Hello! 你好 Привет مرحبا Hëllö Thañks! Önë Mörë";
+ String emlContent = createSimpleTextEmail(
+ "intl@example.com",
+ "user@example.com",
+ "International Characters Test",
+ bodyWithIntlChars);
+
+ testEmailConversion(emlContent, new String[] {
+ "你好", "Привет", "مرحبا", "Hëllö", "Önë", "Mörë"
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should decode quoted-printable content correctly")
+ void decodeQuotedPrintableContent() throws IOException {
+ String content = createQuotedPrintableEmail(
+ );
+
+ testEmailConversion(content, new String[] {
+ "Quoted-Printable Test",
+ "This is quoted printable content with special chars: éàè."
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should decode Base64 content")
+ void decodeBase64Content() throws IOException {
+ String originalText = "This is Base64 encoded content: éàü ñ";
+ String content = createBase64Email(
+ originalText);
+
+ testEmailConversion(content, new String[] {
+ "Base64 Test", "Base64 encoded content"
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should correctly handle inline images with CID references")
+ void handleInlineImages() throws IOException {
+ String boundary = "----=_Part_CID_1234567890";
+ String cid = "image123@example.com";
+ String htmlBody = "Here is an image:
";
+ String imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
+
+ String emlContent = createEmailWithInlineImage(htmlBody, boundary, cid, imageBase64);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createRequestWithAttachments();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("data:image/png;base64," + imageBase64));
+ assertFalse(htmlResult.contains("cid:" + cid));
+ }
+ }
+
+ @Nested
+ @DisplayName("HTML Output Quality")
+ class HtmlOutputTests {
+
+ @Test
+ @DisplayName("Should generate valid HTML structure")
+ void generateValidHtmlStructure() throws IOException {
+ String emlContent = createSimpleTextEmail(
+ "structure@test.com",
+ "user@test.com",
+ "HTML Structure Test",
+ "Testing HTML structure output");
+
+ testEmailConversion(emlContent, new String[] {
+ "", "", "HTML Structure Test"
+ }, false);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, createBasicRequest());
+ assertTrue(htmlResult.length() > 100, "HTML should have substantial content");
+ }
+
+ @Test
+ @DisplayName("Should preserve safe CSS and remove problematic styles")
+ void handleCssStylesCorrectly() throws IOException {
+ String styledHtml = "" +
+ "Safe styling
" +
+ "Problematic styling
" +
+ "Good styling
" +
+ "";
+
+ String emlContent = createHtmlEmail("css@test.com", "user@test.com", "CSS Test", styledHtml);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("color: blue"));
+ assertTrue(htmlResult.contains("font-size: 14px"));
+ assertTrue(htmlResult.contains("margin: 10px"));
+
+ assertFalse(htmlResult.contains("position: fixed"));
+ }
+
+ @Test
+ @DisplayName("Should handle complex nested HTML structures")
+ void handleComplexNestedHtml() throws IOException {
+ String complexHtml = "Complex Email" +
+ "" +
+ "Paragraph with link
" +
+ "- List item 1
- List item 2 with emphasis
" +
+ "Cell 1 | Cell 2 |
Cell 3 | Cell 4 |
" +
+ "
";
+
+ String emlContent = createHtmlEmail(
+ "complex@test.com", "user@test.com", "Complex HTML Test", complexHtml);
+
+ testEmailConversion(emlContent, new String[] {
+ "Email Header", "List item 2", "Cell 3", "example.com"
+ }, false);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, createBasicRequest());
+ assertTrue(htmlResult.length() > 300, "HTML should have substantial content");
+ }
+ }
+
+ @Nested
+ @DisplayName("Error Handling & Edge Cases")
+ class ErrorHandlingTests {
+
+ @Test
+ @DisplayName("Should reject null input")
+ void rejectNullInput() {
+ EmlToPdfRequest request = createBasicRequest();
+
+ Exception exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> EmlToPdf.convertEmlToHtml(null, request));
+ assertTrue(exception.getMessage().contains("EML file is empty or null"));
+ }
+
+ @Test
+ @DisplayName("Should reject empty input")
+ void rejectEmptyInput() {
+ EmlToPdfRequest request = createBasicRequest();
+
+ Exception exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> EmlToPdf.convertEmlToHtml(new byte[0], request));
+ assertTrue(exception.getMessage().contains("EML file is empty or null"));
+ }
+
+ @Test
+ @DisplayName("Should handle malformed EML gracefully")
+ void handleMalformedEmlGracefully() {
+ String malformedEml = """
+ From: sender@test.com
+ Subject: Malformed EML
+ This line breaks header format
+ Content-Type: text/plain
+
+ Body content""";
+
+ byte[] emlBytes = malformedEml.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ try {
+ String result = EmlToPdf.convertEmlToHtml(emlBytes, request);
+ assertNotNull(result, "Result should not be null");
+ assertFalse(result.isEmpty(), "Result should not be empty");
+ } catch (Exception e) {
+ assertNotNull(e.getMessage(), "Exception message should not be null");
+ assertFalse(e.getMessage().isEmpty(), "Exception message should not be empty");
+ }
+ }
+
+ @Test
+ @DisplayName("Should reject invalid EML format")
+ void rejectInvalidEmlFormat() {
+ byte[] invalidEml = "This is definitely not an EML file".getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ Exception exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> EmlToPdf.convertEmlToHtml(invalidEml, request));
+ assertTrue(exception.getMessage().contains("Invalid EML file format"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Advanced Parsing Tests (Jakarta Mail)")
+ class AdvancedParsingTests {
+
+ @Test
+ @DisplayName("Should successfully parse email using advanced parser")
+ void initializeDependencyMailSession() {
+ assertDoesNotThrow(() -> {
+ String emlContent = createSimpleTextEmail(
+ "Dependency@test.com",
+ "user@test.com",
+ "Dependency Mail Test",
+ "Testing Dependency Mail integration.");
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("Dependency Mail Test"));
+ });
+ }
+
+ @Test
+ @DisplayName("Should parse complex MIME structures and select HTML part")
+ void parseComplexMimeStructures() throws IOException {
+ String boundary = "----=_Advanced_1234567890";
+ String textBody = "This is the plain text part.";
+ String htmlBody = "This is the HTML part
";
+ String emlContent = createMultipartAlternativeEmail(textBody, htmlBody, boundary);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createRequestWithAttachments();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("This is the HTML part"));
+ assertFalse(htmlResult.contains("This is the plain text part"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Additional Correctness Tests")
+ class AdditionalCorrectnessTests {
+
+ @Test
+ @DisplayName("Should handle email with only an attachment and no body")
+ void handleAttachmentOnlyEmail() throws IOException {
+ String boundary = "----=_Part_AttachmentOnly_1234567890";
+ String emlContent = createMultipartEmailWithAttachment(
+ "sender@example.com",
+ "recipient@example.com",
+ "Attachment Only Test",
+ "",
+ boundary,
+ "data.bin",
+ "binary data");
+
+ testEmailConversion(emlContent, new String[] {
+ "Attachment Only Test", "data.bin", "No content available"
+ }, true);
+ }
+
+ @Test
+ @DisplayName("Should handle mixed inline and regular attachments")
+ void handleMixedAttachments() throws IOException {
+ String boundary = "----=_Part_MixedAttachments_1234567890";
+ String cid = "inline_image@example.com";
+ String htmlBody = "
";
+ String imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR42mNkAAIAAAoAAb6A/yoAAAAASUVORK5CYII=";
+ String attachmentText = "This is a text attachment.";
+
+ String emlContent = createEmailWithMixedAttachments(
+ htmlBody, boundary, cid, imageBase64, attachmentText);
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createRequestWithAttachments();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains("data:image/png;base64," + imageBase64));
+ assertTrue(htmlResult.contains("text.txt"));
+ }
+
+ @Test
+ @DisplayName("Should handle non-standard but valid character sets like ISO-8859-1")
+ void handleIso88591Charset() throws IOException {
+ String subject = "Subject with special characters: ñ é ü";
+ String body = "Body with special characters: ñ é ü";
+
+ String emlContent = createSimpleTextEmailWithCharset(
+ "sender@example.com",
+ "recipient@example.com",
+ subject,
+ body,
+ "ISO-8859-1");
+
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.ISO_8859_1);
+ EmlToPdfRequest request = createBasicRequest();
+
+ String htmlResult = EmlToPdf.convertEmlToHtml(emlBytes, request);
+
+ assertNotNull(htmlResult);
+ assertTrue(htmlResult.contains(subject));
+ assertTrue(htmlResult.contains(body));
+ }
+
+ @Test
+ @DisplayName("Should handle emails with extremely long lines")
+ void handleLongLines() throws IOException {
+ StringBuilder longLine = new StringBuilder("This is a very long line: ");
+ for (int i = 0; i < 1000; i++) {
+ longLine.append("word").append(i).append(" ");
+ }
+
+ String emlContent = createSimpleTextEmail(
+ "sender@example.com",
+ "recipient@example.com",
+ "Long Line Test",
+ longLine.toString());
+
+ testEmailConversion(emlContent, new String[] {
+ "Long Line Test", "This is a very long line", "word999"
+ }, false);
+ }
+
+ @Test
+ @DisplayName("Should handle .eml files as attachments")
+ void handleEmlAttachment() throws IOException {
+ String boundary = "----=_Part_EmlAttachment_1234567890";
+ String innerEmlContent = createSimpleTextEmail(
+ "inner@example.com",
+ "inner_recipient@example.com",
+ "Inner Email Subject",
+ "This is the body of the attached email.");
+
+ String emlContent = createEmailWithEmlAttachment(
+ boundary,
+ innerEmlContent);
+
+ testEmailConversion(emlContent, new String[] {
+ "Fwd: Inner Email Subject",
+ "Please see the attached email.",
+ "attached_email.eml"
+ }, true);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"windows-1252", "ISO-8859-2", "KOI8-R", "Shift_JIS"})
+ @DisplayName("Should handle various character encodings")
+ void handleVariousEncodings(String charset) throws IOException {
+ String subject = "Encoding Test";
+ String body = "Testing " + charset + " encoding";
+
+ String emlContent = createSimpleTextEmailWithCharset(
+ "sender@example.com",
+ "recipient@example.com",
+ subject,
+ body,
+ charset);
+
+ testEmailConversion(emlContent, new String[] {subject, body}, false);
+ }
+ }
+
+ @Nested
+ @ExtendWith(MockitoExtension.class)
+ @DisplayName("PDF Conversion Tests")
+ class PdfConversionTests {
+
+ @Mock private CustomPDFDocumentFactory mockPdfDocumentFactory;
+
+ @Mock private PDDocument mockPdDocument;
+
+ @Test
+ @DisplayName("Should convert EML to PDF without attachments when not requested")
+ void convertEmlToPdfWithoutAttachments() throws Exception {
+ String emlContent =
+ createSimpleTextEmail("from@test.com", "to@test.com", "Subject", "Body");
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+
+ PDDocument testPdf = new PDDocument();
+ testPdf.addPage(new PDPage());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ testPdf.save(baos);
+ testPdf.close();
+ byte[] fakePdfBytes = baos.toByteArray();
+
+ when(mockPdfDocumentFactory.load(any(byte[].class))).thenReturn(mockPdDocument);
+ when(mockPdDocument.getNumberOfPages()).thenReturn(1);
+
+ try (MockedStatic fileToPdf = mockStatic(FileToPdf.class)) {
+ fileToPdf
+ .when(
+ () ->
+ FileToPdf.convertHtmlToPdf(
+ anyString(),
+ any(),
+ any(byte[].class),
+ anyString(),
+ anyBoolean()))
+ .thenReturn(fakePdfBytes);
+
+ byte[] resultPdf =
+ EmlToPdf.convertEmlToPdf(
+ "weasyprint",
+ request,
+ emlBytes,
+ "test.eml",
+ false,
+ mockPdfDocumentFactory);
+
+ assertArrayEquals(fakePdfBytes, resultPdf);
+
+ try (PDDocument resultDoc = mockPdfDocumentFactory.load(resultPdf)) {
+ assertNotNull(resultDoc);
+ assertTrue(resultDoc.getNumberOfPages() > 0);
+ }
+
+ fileToPdf.verify(
+ () ->
+ FileToPdf.convertHtmlToPdf(
+ anyString(),
+ any(),
+ any(byte[].class),
+ anyString(),
+ anyBoolean()));
+ verify(mockPdfDocumentFactory).load(resultPdf);
+ }
+ }
+
+ @Test
+ @DisplayName("Should convert EML to PDF with attachments when requested")
+ void convertEmlToPdfWithAttachments() throws Exception {
+ String boundary = "----=_Part_1234567890";
+ String emlContent = createMultipartEmailWithAttachment(
+ "multipart@example.com",
+ "user@example.com",
+ "Multipart Email Test",
+ "This email has both text content and an attachment.",
+ boundary,
+ "document.txt",
+ "Sample attachment content");
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createRequestWithAttachments();
+
+ PDDocument testPdf = new PDDocument();
+ testPdf.addPage(new PDPage());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ testPdf.save(baos);
+ testPdf.close();
+ byte[] fakePdfBytes = baos.toByteArray();
+
+ when(mockPdfDocumentFactory.load(any(byte[].class))).thenReturn(mockPdDocument);
+ when(mockPdDocument.getNumberOfPages()).thenReturn(1);
+
+ try (MockedStatic fileToPdf = mockStatic(FileToPdf.class)) {
+ fileToPdf
+ .when(
+ () ->
+ FileToPdf.convertHtmlToPdf(
+ anyString(),
+ any(),
+ any(byte[].class),
+ anyString(),
+ anyBoolean()))
+ .thenReturn(fakePdfBytes);
+
+ try (MockedStatic ignored =
+ mockStatic(
+ EmlToPdf.class,
+ invocation -> {
+ String methodName = invocation.getMethod().getName();
+ return switch (methodName) {
+ case "shouldAttachFiles" -> true;
+ case "attachFilesToPdf" -> fakePdfBytes;
+ default -> invocation.callRealMethod();
+ };
+ })) {
+ byte[] resultPdf =
+ EmlToPdf.convertEmlToPdf(
+ "weasyprint",
+ request,
+ emlBytes,
+ "test.eml",
+ false,
+ mockPdfDocumentFactory);
+
+ assertArrayEquals(fakePdfBytes, resultPdf);
+
+ try (PDDocument resultDoc = mockPdfDocumentFactory.load(resultPdf)) {
+ assertNotNull(resultDoc);
+ assertTrue(resultDoc.getNumberOfPages() > 0);
+ }
+
+ fileToPdf.verify(
+ () ->
+ FileToPdf.convertHtmlToPdf(
+ anyString(),
+ any(),
+ any(byte[].class),
+ anyString(),
+ anyBoolean()));
+
+ verify(mockPdfDocumentFactory).load(resultPdf);
+ }
+ }
+ }
+
+ @Test
+ @DisplayName("Should handle errors during EML to PDF conversion")
+ void handleErrorsDuringConversion() {
+ String emlContent =
+ createSimpleTextEmail("from@test.com", "to@test.com", "Subject", "Body");
+ byte[] emlBytes = emlContent.getBytes(StandardCharsets.UTF_8);
+ EmlToPdfRequest request = createBasicRequest();
+ String errorMessage = "Conversion failed";
+
+ try (MockedStatic fileToPdf = mockStatic(FileToPdf.class)) {
+ fileToPdf
+ .when(
+ () ->
+ FileToPdf.convertHtmlToPdf(
+ anyString(),
+ any(),
+ any(byte[].class),
+ anyString(),
+ anyBoolean()))
+ .thenThrow(new IOException(errorMessage));
+
+ IOException exception = assertThrows(
+ IOException.class,
+ () -> EmlToPdf.convertEmlToPdf(
+ "weasyprint",
+ request,
+ emlBytes,
+ "test.eml",
+ false,
+ mockPdfDocumentFactory));
+
+ assertTrue(exception.getMessage().contains(errorMessage));
+ }
+ }
+ }
+
+ // Helper methods
+ private String getTimestamp() {
+ java.time.ZonedDateTime fixedDateTime = java.time.ZonedDateTime.of(2023, 1, 1, 12, 0, 0, 0, java.time.ZoneId.of("GMT"));
+ return java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME.format(fixedDateTime);
+ }
+ private String createSimpleTextEmail(String from, String to, String subject, String body) {
+ return createSimpleTextEmailWithCharset(from, to, subject, body, "UTF-8");
+ }
+
+ private String createSimpleTextEmailWithCharset(String from, String to, String subject, String body, String charset) {
+ return String.format(
+ "From: %s\nTo: %s\nSubject: %s\nDate: %s\nContent-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n%s",
+ from, to, subject, getTimestamp(), charset, body);
+ }
+
+ private String createEmailWithCustomHeaders() {
+ return String.format(
+ "From: sender@example.com\nDate: %s\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n\n%s",
+ getTimestamp(), "This is an email body with some headers missing.");
+ }
+
+ private String createHtmlEmail(String from, String to, String subject, String htmlBody) {
+ return String.format(
+ "From: %s\nTo: %s\nSubject: %s\nDate: %s\nContent-Type: text/html; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n\n%s",
+ from, to, subject, getTimestamp(), htmlBody);
+ }
+
+ private String createMultipartEmailWithAttachment(String from, String to, String subject, String body,
+ String boundary, String filename, String attachmentContent) {
+ String encodedContent = Base64.getEncoder().encodeToString(attachmentContent.getBytes(StandardCharsets.UTF_8));
+ return String.format(
+ """
+ From: %s
+ To: %s
+ Subject: %s
+ Date: %s
+ Content-Type: multipart/mixed; boundary="%s"
+
+ --%s
+ Content-Type: text/plain; charset=UTF-8
+ Content-Transfer-Encoding: 8bit
+
+ %s
+
+ --%s
+ Content-Type: text/plain; charset=UTF-8
+ Content-Disposition: attachment; filename="%s"
+ Content-Transfer-Encoding: base64
+
+ %s
+
+ --%s--""",
+ from, to, subject, getTimestamp(), boundary, boundary, body, boundary, filename, encodedContent, boundary);
+ }
+
+ private String createEmailWithEmlAttachment(String boundary, String attachmentEmlContent) {
+ String encodedContent = Base64.getEncoder().encodeToString(attachmentEmlContent.getBytes(StandardCharsets.UTF_8));
+ return String.format(
+ """
+ From: %s
+ To: %s
+ Subject: %s
+ Date: %s
+ Content-Type: multipart/mixed; boundary="%s"
+
+ --%s
+ Content-Type: text/plain; charset=UTF-8
+ Content-Transfer-Encoding: 8bit
+
+ %s
+
+ --%s
+ Content-Type: message/rfc822; name="%s"
+ Content-Disposition: attachment; filename="%s"
+ Content-Transfer-Encoding: base64
+
+ %s
+
+ --%s--""",
+ "outer@example.com", "outer_recipient@example.com", "Fwd: Inner Email Subject", getTimestamp(), boundary, boundary, "Please see the attached email.", boundary, "attached_email.eml", "attached_email.eml", encodedContent, boundary);
+ }
+
+ private String createMultipartAlternativeEmail(String textBody, String htmlBody, String boundary) {
+ return String.format(
+ """
+ From: %s
+ To: %s
+ Subject: %s
+ Date: %s
+ MIME-Version: 1.0
+ Content-Type: multipart/alternative; boundary="%s"
+
+ --%s
+ Content-Type: text/plain; charset=UTF-8
+ Content-Transfer-Encoding: 7bit
+
+ %s
+
+ --%s
+ Content-Type: text/html; charset=UTF-8
+ Content-Transfer-Encoding: 7bit
+
+ %s
+
+ --%s--""",
+ "sender@example.com", "receiver@example.com", "Multipart/Alternative Test", getTimestamp(),
+ boundary, boundary, textBody, boundary, htmlBody, boundary);
+ }
+
+ private String createQuotedPrintableEmail() {
+ return String.format(
+ "From: %s\nTo: %s\nSubject: %s\nDate: %s\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: quoted-printable\n\n%s",
+ "sender@example.com", "recipient@example.com", "Quoted-Printable Test", getTimestamp(), "This is quoted=20printable content with special chars: =C3=A9=C3=A0=C3=A8.");
+ }
+
+ private String createBase64Email(String body) {
+ String encodedBody = Base64.getEncoder().encodeToString(body.getBytes(StandardCharsets.UTF_8));
+ return String.format(
+ "From: %s\nTo: %s\nSubject: %s\nDate: %s\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: base64\n\n%s",
+ "sender@example.com", "recipient@example.com", "Base64 Test", getTimestamp(), encodedBody);
+ }
+
+ private String createEmailWithInlineImage(String htmlBody, String boundary, String contentId, String base64Image) {
+ return String.format(
+ """
+ From: %s
+ To: %s
+ Subject: %s
+ Date: %s
+ Content-Type: multipart/related; boundary="%s"
+
+ --%s
+ Content-Type: text/html; charset=UTF-8
+ Content-Transfer-Encoding: 8bit
+
+ %s
+
+ --%s
+ Content-Type: image/png
+ Content-Transfer-Encoding: base64
+ Content-ID: <%s>
+ Content-Disposition: inline; filename="image.png"
+
+ %s
+
+ --%s--""",
+ "sender@example.com", "receiver@example.com", "Inline Image Test", getTimestamp(),
+ boundary, boundary, htmlBody, boundary, contentId, base64Image, boundary);
+ }
+
+ private String createEmailWithMixedAttachments(String htmlBody, String boundary, String contentId,
+ String base64Image, String attachmentBody) {
+ String encodedAttachment = Base64.getEncoder().encodeToString(attachmentBody.getBytes(StandardCharsets.UTF_8));
+ return String.format(
+ """
+ From: %s
+ To: %s
+ Subject: %s
+ Date: %s
+ Content-Type: multipart/mixed; boundary="%s"
+
+ --%s
+ Content-Type: multipart/related; boundary="related-%s"
+
+ --related-%s
+ Content-Type: text/html; charset=UTF-8
+ Content-Transfer-Encoding: 8bit
+
+ %s
+
+ --related-%s
+ Content-Type: image/png
+ Content-Transfer-Encoding: base64
+ Content-ID: <%s>
+ Content-Disposition: inline; filename="image.png"
+
+ %s
+
+ --related-%s--
+
+ --%s
+ Content-Type: text/plain; charset=UTF-8
+ Content-Disposition: attachment; filename="%s"
+ Content-Transfer-Encoding: base64
+
+ %s
+
+ --%s--""",
+ "sender@example.com", "receiver@example.com", "Mixed Attachments Test", getTimestamp(),
+ boundary, boundary, boundary, boundary, htmlBody, boundary, contentId, base64Image, boundary,
+ boundary, "text.txt", encodedAttachment, boundary);
+ }
+ // Creates a basic EmlToPdfRequest with default settings
+ private EmlToPdfRequest createBasicRequest() {
+ EmlToPdfRequest request = new EmlToPdfRequest();
+ request.setIncludeAttachments(false);
+ return request;
+ }
+
+ private EmlToPdfRequest createRequestWithAttachments() {
+ EmlToPdfRequest request = new EmlToPdfRequest();
+ request.setIncludeAttachments(true);
+ request.setMaxAttachmentSizeMB(10);
+ return request;
+ }
+}