mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-07-23 13:45:21 +00:00
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.
This commit is contained in:
parent
bc9c127819
commit
6461b93131
@ -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",
|
||||
"<!DOCTYPE html>"
|
||||
}, 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("<title></title>") ||
|
||||
htmlResult.contains("<title>No Subject</title>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Should parse HTML email with styling")
|
||||
void parseHtmlEmailWithStyling() throws IOException {
|
||||
String htmlBody = "<html><head><style>.header{color:blue;font-weight:bold;}" +
|
||||
".content{margin:10px;}.footer{font-size:12px;}</style></head>" +
|
||||
"<body><div class=\"header\">Important Notice</div>" +
|
||||
"<div class=\"content\">This is <strong>HTML content</strong> with styling.</div>" +
|
||||
"<div class=\"footer\">Best regards</div></body></html>";
|
||||
|
||||
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 = "<html><body><p>Here is an image:</p><img src=\"cid:" + cid + "\"></body></html>";
|
||||
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[] {
|
||||
"<!DOCTYPE html>", "<html", "</html>", "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 = "<html><head><style>" +
|
||||
".safe { color: blue; font-size: 14px; }" +
|
||||
".problematic { position: fixed; word-break: break-all; }" +
|
||||
".good { margin: 10px; padding: 5px; }" +
|
||||
"</style></head><body>" +
|
||||
"<div class=\"safe\">Safe styling</div>" +
|
||||
"<div class=\"problematic\">Problematic styling</div>" +
|
||||
"<div class=\"good\">Good styling</div>" +
|
||||
"</body></html>";
|
||||
|
||||
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 = "<html><head><title>Complex Email</title></head><body>" +
|
||||
"<div class=\"container\"><header><h1>Email Header</h1></header><main><section>" +
|
||||
"<p>Paragraph with <a href=\"https://example.com\">link</a></p><ul>" +
|
||||
"<li>List item 1</li><li>List item 2 with <em>emphasis</em></li></ul><table>" +
|
||||
"<tr><td>Cell 1</td><td>Cell 2</td></tr><tr><td>Cell 3</td><td>Cell 4</td></tr>" +
|
||||
"</table></section></main></div></body></html>";
|
||||
|
||||
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 = "<html><body><h1>This is the HTML part</h1></body></html>";
|
||||
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 = "<html><body><img src=\"cid:" + cid + "\"></body></html>";
|
||||
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> 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> fileToPdf = mockStatic(FileToPdf.class)) {
|
||||
fileToPdf
|
||||
.when(
|
||||
() ->
|
||||
FileToPdf.convertHtmlToPdf(
|
||||
anyString(),
|
||||
any(),
|
||||
any(byte[].class),
|
||||
anyString(),
|
||||
anyBoolean()))
|
||||
.thenReturn(fakePdfBytes);
|
||||
|
||||
try (MockedStatic<EmlToPdf> 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> 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user