From 3de6e0bcf1b6e0e7afe578e37fd67f5b455c1987 Mon Sep 17 00:00:00 2001 From: phail Date: Thu, 3 Nov 2022 00:58:36 +0100 Subject: [PATCH] put parser into setup make test using convert optional Gotenberg live testing --- .github/workflows/ci.yml | 4 + src/paperless_mail/tests/samples/first.pdf | Bin 0 -> 6714 bytes src/paperless_mail/tests/samples/second.pdf | Bin 0 -> 6723 bytes .../tests/samples/simple_text.eml.pdf | Bin 0 -> 22301 bytes .../tests/samples/simple_text.eml.pdf.webp | Bin 0 -> 5340 bytes src/paperless_mail/tests/test_parsers.py | 195 +++++++++------- src/paperless_mail/tests/test_parsers_live.py | 220 ++++++++++++++++++ 7 files changed, 329 insertions(+), 90 deletions(-) create mode 100644 src/paperless_mail/tests/samples/first.pdf create mode 100644 src/paperless_mail/tests/samples/second.pdf create mode 100644 src/paperless_mail/tests/samples/simple_text.eml.pdf create mode 100644 src/paperless_mail/tests/samples/simple_text.eml.pdf.webp create mode 100644 src/paperless_mail/tests/test_parsers_live.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4965a348..8e506c8aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,10 @@ jobs: PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }} PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }} PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }} + # Skip Tests which require convert + PAPERLESS_TEST_SKIP_CONVERT: 1 + # Enable Gotenberg end to end testing + GOTENBERG_LIVE: 1 steps: - name: Checkout diff --git a/src/paperless_mail/tests/samples/first.pdf b/src/paperless_mail/tests/samples/first.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4f74613f97a8cf297ce4c453e346b34c54a5364c GIT binary patch literal 6714 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 literal 0 HcmV?d00001 diff --git a/src/paperless_mail/tests/samples/second.pdf b/src/paperless_mail/tests/samples/second.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2955c8d5d133267d97e5279dc7a30886fdd95fb9 GIT binary patch literal 6723 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 literal 0 HcmV?d00001 diff --git a/src/paperless_mail/tests/samples/simple_text.eml.pdf b/src/paperless_mail/tests/samples/simple_text.eml.pdf new file mode 100644 index 0000000000000000000000000000000000000000..678e6df42eb3d69a13c6b209bef01494c5847240 GIT binary patch literal 22301 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 literal 0 HcmV?d00001 diff --git a/src/paperless_mail/tests/samples/simple_text.eml.pdf.webp b/src/paperless_mail/tests/samples/simple_text.eml.pdf.webp new file mode 100644 index 0000000000000000000000000000000000000000..614aeee9c2edaef9ac705e15de6c9234050b008a GIT binary patch literal 5340 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 literal 0 HcmV?d00001 diff --git a/src/paperless_mail/tests/test_parsers.py b/src/paperless_mail/tests/test_parsers.py index 596e7d180..a0c19e6f5 100644 --- a/src/paperless_mail/tests/test_parsers.py +++ b/src/paperless_mail/tests/test_parsers.py @@ -1,9 +1,6 @@ import datetime -import hashlib import os from unittest import mock -from urllib.error import HTTPError -from urllib.request import urlopen import pytest from django.test import TestCase @@ -15,19 +12,25 @@ from pdfminer.high_level import extract_text class TestParser(TestCase): SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples") - def test_get_parsed(self): - parser = MailDocumentParser(None) + def setUp(self) -> None: + self.parser = MailDocumentParser(logging_group=None) + def tearDown(self) -> None: + self.parser.cleanup() + + def test_get_parsed(self): # Check if exception is raised when parsing fails. with pytest.raises(ParseError): - parser.get_parsed(os.path.join(self.SAMPLE_FILES, "na")) + self.parser.get_parsed(os.path.join(self.SAMPLE_FILES, "na")) # Check if exception is raised when the mail is faulty. with pytest.raises(ParseError): - parser.get_parsed(os.path.join(self.SAMPLE_FILES, "broken.eml")) + self.parser.get_parsed(os.path.join(self.SAMPLE_FILES, "broken.eml")) # Parse Test file and check relevant content - parsed1 = parser.get_parsed(os.path.join(self.SAMPLE_FILES, "simple_text.eml")) + parsed1 = self.parser.get_parsed( + os.path.join(self.SAMPLE_FILES, "simple_text.eml"), + ) self.assertEqual(parsed1.date.year, 2022) self.assertEqual(parsed1.date.month, 10) @@ -42,48 +45,45 @@ class TestParser(TestCase): self.assertEqual(parsed1.to, ("some@one.de",)) # Check if same parsed object as before is returned, even if another file is given. - parsed2 = parser.get_parsed(os.path.join(os.path.join(self.SAMPLE_FILES, "na"))) + parsed2 = self.parser.get_parsed( + os.path.join(os.path.join(self.SAMPLE_FILES, "na")), + ) self.assertEqual(parsed1, parsed2) - @staticmethod - def hashfile(file): - buf_size = 65536 # An arbitrary (but fixed) buffer - sha256 = hashlib.sha256() - with open(file, "rb") as f: - while True: - data = f.read(buf_size) - if not data: - break - sha256.update(data) - return sha256.hexdigest() - + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf") @mock.patch("paperless_mail.parsers.make_thumbnail_from_pdf") - @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output - def test_get_thumbnail(self, m, mock_make_thumbnail_from_pdf: mock.MagicMock): - parser = MailDocumentParser(None) - thumb = parser.get_thumbnail( + def test_get_thumbnail( + self, + mock_make_thumbnail_from_pdf: mock.MagicMock, + mock_generate_pdf: mock.MagicMock, + ): + mocked_return = "Passing the return value through.." + mock_make_thumbnail_from_pdf.return_value = mocked_return + + mock_generate_pdf.return_value = "Mocked return value.." + + thumb = self.parser.get_thumbnail( os.path.join(self.SAMPLE_FILES, "simple_text.eml"), "message/rfc822", ) self.assertEqual( - parser.archive_path, + self.parser.archive_path, mock_make_thumbnail_from_pdf.call_args_list[0].args[0], ) self.assertEqual( - parser.tempdir, + self.parser.tempdir, mock_make_thumbnail_from_pdf.call_args_list[0].args[1], ) + self.assertEqual(mocked_return, thumb) @mock.patch("documents.loggers.LoggingMixin.log") def test_extract_metadata(self, m: mock.MagicMock): - parser = MailDocumentParser(None) - # Validate if warning is logged when parsing fails - self.assertEqual([], parser.extract_metadata("na", "message/rfc822")) + self.assertEqual([], self.parser.extract_metadata("na", "message/rfc822")) self.assertEqual("warning", m.call_args[0][0]) # Validate Metadata parsing returns the expected results - metadata = parser.extract_metadata( + metadata = self.parser.extract_metadata( os.path.join(self.SAMPLE_FILES, "simple_text.eml"), "message/rfc822", ) @@ -209,22 +209,22 @@ class TestParser(TestCase): in metadata, ) - @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output - def test_parse(self, m): - parser = MailDocumentParser(None) - + def test_parse_na(self): # Check if exception is raised when parsing fails. with pytest.raises(ParseError): - parser.parse( + self.parser.parse( os.path.join(os.path.join(self.SAMPLE_FILES, "na")), "message/rfc822", ) + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf") + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_parse_html_eml(self, m, n): # Validate parsing returns the expected results - parser.parse(os.path.join(self.SAMPLE_FILES, "html.eml"), "message/rfc822") + self.parser.parse(os.path.join(self.SAMPLE_FILES, "html.eml"), "message/rfc822") text_expected = "Some Text\nand an embedded image.\n\nSubject: HTML Message\n\nFrom: Name \n\nTo: someone@example.de\n\nAttachments: IntM6gnXFm00FEV5.png (6.89 KiB), 600+kbfile.txt (0.59 MiB)\n\nHTML content: Some Text\nand an embedded image.\nParagraph unchanged." - self.assertEqual(text_expected, parser.text) + self.assertEqual(text_expected, self.parser.text) self.assertEqual( datetime.datetime( 2022, @@ -235,17 +235,20 @@ class TestParser(TestCase): 19, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200)), ), - parser.date, + self.parser.date, ) + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf") + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_parse_simple_eml(self, m, n): # Validate parsing returns the expected results - parser = MailDocumentParser(None) - parser.parse( + + self.parser.parse( os.path.join(self.SAMPLE_FILES, "simple_text.eml"), "message/rfc822", ) text_expected = "This is just a simple Text Mail.\n\nSubject: Simple Text Mail\n\nFrom: Some One \n\nTo: some@one.de\n\nCC: asdasd@æsdasd.de, asdadasdasdasda.asdasd@æsdasd.de\n\nBCC: fdf@fvf.de\n\n" - self.assertEqual(text_expected, parser.text) + self.assertEqual(text_expected, self.parser.text) self.assertEqual( datetime.datetime( 2022, @@ -256,33 +259,32 @@ class TestParser(TestCase): 43, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200)), ), - parser.date, + self.parser.date, ) # Just check if file exists, the unittest for generate_pdf() goes deeper. - self.assertTrue(os.path.isfile(parser.archive_path)) + self.assertTrue(os.path.isfile(self.parser.archive_path)) @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output def test_tika_parse(self, m): html = '

Some Text

' expected_text = "\n\n\n\n\n\n\n\n\nSome Text\n" - parser = MailDocumentParser(None) - tika_server_original = parser.tika_server + tika_server_original = self.parser.tika_server # Check if exception is raised when Tika cannot be reached. with pytest.raises(ParseError): - parser.tika_server = "" - parser.tika_parse(html) + self.parser.tika_server = "" + self.parser.tika_parse(html) # Check unsuccessful parsing - parser.tika_server = tika_server_original + self.parser.tika_server = tika_server_original - parsed = parser.tika_parse(None) + parsed = self.parser.tika_parse(None) self.assertEqual("", parsed) # Check successful parsing - parsed = parser.tika_parse(html) + parsed = self.parser.tika_parse(html) self.assertEqual(expected_text, parsed) @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf_from_mail") @@ -290,32 +292,63 @@ class TestParser(TestCase): def test_generate_pdf_parse_error(self, m: mock.MagicMock, n: mock.MagicMock): m.return_value = b"" n.return_value = b"" - parser = MailDocumentParser(None) # Check if exception is raised when the pdf can not be created. - parser.gotenberg_server = "" + self.parser.gotenberg_server = "" with pytest.raises(ParseError): - parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml")) - - @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output - def test_generate_pdf(self, m): - parser = MailDocumentParser(None) + self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml")) + @mock.patch("paperless_mail.parsers.requests.post") + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf_from_mail") + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf_from_html") + def test_generate_pdf( + self, + mock_generate_pdf_from_html: mock.MagicMock, + mock_generate_pdf_from_mail: mock.MagicMock, + mock_post: mock.MagicMock, + ): # Check if exception is raised when the mail can not be parsed. with pytest.raises(ParseError): - parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "broken.eml")) + self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "broken.eml")) - pdf_path = parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml")) + mock_generate_pdf_from_mail.return_value = b"Mail Return" + mock_generate_pdf_from_html.return_value = b"HTML Return" + + mock_response = mock.MagicMock() + mock_response.content = b"Content" + mock_post.return_value = mock_response + pdf_path = self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml")) self.assertTrue(os.path.isfile(pdf_path)) - extracted = extract_text(pdf_path) - expected = "From Name \n\n2022-10-15 09:23\n\nSubject HTML Message\n\nTo someone@example.de\n\nAttachments IntM6gnXFm00FEV5.png (6.89 KiB), 600+kbfile.txt (0.59 MiB)\n\nSome Text \n\nand an embedded image.\n\n\x0cSome Text\n\n This image should not be shown.\n\nand an embedded image.\n\nParagraph unchanged.\n\n\x0c" - self.assertEqual(expected, extracted) + mock_generate_pdf_from_mail.assert_called_once_with( + self.parser.get_parsed(None), + ) + mock_generate_pdf_from_html.assert_called_once_with( + self.parser.get_parsed(None).html, + self.parser.get_parsed(None).attachments, + ) + self.assertEqual( + self.parser.gotenberg_server + "/forms/pdfengines/merge", + mock_post.call_args.args[0], + ) + self.assertEqual({}, mock_post.call_args.kwargs["headers"]) + self.assertEqual( + b"Mail Return", + mock_post.call_args.kwargs["files"]["1_mail.pdf"][1].read(), + ) + self.assertEqual( + b"HTML Return", + mock_post.call_args.kwargs["files"]["2_html.pdf"][1].read(), + ) + + mock_response.raise_for_status.assert_called_once() + + with open(pdf_path, "rb") as file: + self.assertEqual(b"Content", file.read()) def test_mail_to_html(self): - parser = MailDocumentParser(None) - mail = parser.get_parsed(os.path.join(self.SAMPLE_FILES, "html.eml")) - html_handle = parser.mail_to_html(mail) + mail = self.parser.get_parsed(os.path.join(self.SAMPLE_FILES, "html.eml")) + html_handle = self.parser.mail_to_html(mail) with open( os.path.join(self.SAMPLE_FILES, "html.eml.html"), @@ -324,13 +357,12 @@ class TestParser(TestCase): @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output def test_generate_pdf_from_mail(self, m): - parser = MailDocumentParser(None) - mail = parser.get_parsed(os.path.join(self.SAMPLE_FILES, "html.eml")) + mail = self.parser.get_parsed(os.path.join(self.SAMPLE_FILES, "html.eml")) - pdf_path = os.path.join(parser.tempdir, "test_generate_pdf_from_mail.pdf") + pdf_path = os.path.join(self.parser.tempdir, "test_generate_pdf_from_mail.pdf") with open(pdf_path, "wb") as file: - file.write(parser.generate_pdf_from_mail(mail)) + file.write(self.parser.generate_pdf_from_mail(mail)) file.close() extracted = extract_text(pdf_path) @@ -343,8 +375,6 @@ class TestParser(TestCase): self.payload = payload self.content_id = content_id - parser = MailDocumentParser(None) - result = None with open(os.path.join(self.SAMPLE_FILES, "sample.html")) as html_file: @@ -354,7 +384,7 @@ class TestParser(TestCase): attachments = [ MailAttachmentMock(png, "part1.pNdUSz0s.D3NqVtPg@example.de"), ] - result = parser.transform_inline_html(html, attachments) + result = self.parser.transform_inline_html(html, attachments) resulting_html = result[-1][1].read() self.assertTrue(result[-1][0] == "index.html") @@ -368,8 +398,6 @@ class TestParser(TestCase): self.payload = payload self.content_id = content_id - parser = MailDocumentParser(None) - result = None with open(os.path.join(self.SAMPLE_FILES, "sample.html")) as html_file: @@ -379,9 +407,9 @@ class TestParser(TestCase): attachments = [ MailAttachmentMock(png, "part1.pNdUSz0s.D3NqVtPg@example.de"), ] - result = parser.generate_pdf_from_html(html, attachments) + result = self.parser.generate_pdf_from_html(html, attachments) - pdf_path = os.path.join(parser.tempdir, "test_generate_pdf_from_html.pdf") + pdf_path = os.path.join(self.parser.tempdir, "test_generate_pdf_from_html.pdf") with open(pdf_path, "wb") as file: file.write(result) @@ -390,16 +418,3 @@ class TestParser(TestCase): extracted = extract_text(pdf_path) expected = "Some Text\n\n This image should not be shown.\n\nand an embedded image.\n\nParagraph unchanged.\n\n\x0c" self.assertEqual(expected, extracted) - - def test_is_online_image_still_available(self): - """ - A public image is used in the html sample file. We have no control - whether this image stays online forever, so here we check if it is still there - """ - - # Start by Testing if nonexistent URL really throws an Exception - with pytest.raises(HTTPError): - urlopen("https://upload.wikimedia.org/wikipedia/en/f/f7/nonexistent.png") - - # Now check the URL used in samples/sample.html - urlopen("https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png") diff --git a/src/paperless_mail/tests/test_parsers_live.py b/src/paperless_mail/tests/test_parsers_live.py new file mode 100644 index 000000000..6676ebc1e --- /dev/null +++ b/src/paperless_mail/tests/test_parsers_live.py @@ -0,0 +1,220 @@ +import hashlib +import os +from unittest import mock +from urllib.error import HTTPError +from urllib.request import urlopen + +import pytest +from django.test import TestCase +from documents.parsers import ParseError +from documents.parsers import run_convert +from paperless_mail.parsers import MailDocumentParser +from pdfminer.high_level import extract_text + + +class TestParserLive(TestCase): + SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples") + + def setUp(self) -> None: + self.parser = MailDocumentParser(logging_group=None) + + def tearDown(self) -> None: + self.parser.cleanup() + + @staticmethod + def hashfile(file): + buf_size = 65536 # An arbitrary (but fixed) buffer + sha256 = hashlib.sha256() + with open(file, "rb") as f: + while True: + data = f.read(buf_size) + if not data: + break + sha256.update(data) + return sha256.hexdigest() + + # Only run if convert is available + @pytest.mark.skipif( + "PAPERLESS_TEST_SKIP_CONVERT" in os.environ, + reason="PAPERLESS_TEST_SKIP_CONVERT set, skipping Test", + ) + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf") + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_get_thumbnail(self, m, mock_generate_pdf: mock.MagicMock): + mock_generate_pdf.return_value = os.path.join( + self.SAMPLE_FILES, + "simple_text.eml.pdf", + ) + thumb = self.parser.get_thumbnail( + os.path.join(self.SAMPLE_FILES, "simple_text.eml"), + "message/rfc822", + ) + self.assertTrue(os.path.isfile(thumb)) + + expected = os.path.join(self.SAMPLE_FILES, "simple_text.eml.pdf.webp") + + self.assertEqual( + self.hashfile(thumb), + self.hashfile(expected), + f"Created Thumbnail {thumb} differs from expected file {expected}", + ) + + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_tika_parse(self, m): + html = '

Some Text

' + expected_text = "\n\n\n\n\n\n\n\n\nSome Text\n" + + tika_server_original = self.parser.tika_server + + # Check if exception is raised when Tika cannot be reached. + with pytest.raises(ParseError): + self.parser.tika_server = "" + self.parser.tika_parse(html) + + # Check unsuccessful parsing + self.parser.tika_server = tika_server_original + + parsed = self.parser.tika_parse(None) + self.assertEqual("", parsed) + + # Check successful parsing + parsed = self.parser.tika_parse(html) + self.assertEqual(expected_text, parsed) + + @pytest.mark.skipif( + "GOTENBERG_LIVE" not in os.environ, + reason="No gotenberg server", + ) + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf_from_mail") + @mock.patch("paperless_mail.parsers.MailDocumentParser.generate_pdf_from_html") + def test_generate_pdf_gotenberg_merging( + self, + mock_generate_pdf_from_html: mock.MagicMock, + mock_generate_pdf_from_mail: mock.MagicMock, + ): + + with open(os.path.join(self.SAMPLE_FILES, "first.pdf"), "rb") as first: + mock_generate_pdf_from_mail.return_value = first.read() + + with open(os.path.join(self.SAMPLE_FILES, "second.pdf"), "rb") as second: + mock_generate_pdf_from_html.return_value = second.read() + + pdf_path = self.parser.generate_pdf(os.path.join(self.SAMPLE_FILES, "html.eml")) + self.assertTrue(os.path.isfile(pdf_path)) + + extracted = extract_text(pdf_path) + expected = ( + "first\tPDF\tto\tbe\tmerged.\n\n\x0csecond\tPDF\tto\tbe\tmerged.\n\n\x0c" + ) + self.assertEqual(expected, extracted) + + # Only run if convert is available + @pytest.mark.skipif( + "PAPERLESS_TEST_SKIP_CONVERT" in os.environ, + reason="PAPERLESS_TEST_SKIP_CONVERT set, skipping Test", + ) + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_generate_pdf_from_mail(self, m): + # TODO + mail = self.parser.get_parsed(os.path.join(self.SAMPLE_FILES, "html.eml")) + + pdf_path = os.path.join(self.parser.tempdir, "test_generate_pdf_from_mail.pdf") + + with open(pdf_path, "wb") as file: + file.write(self.parser.generate_pdf_from_mail(mail)) + file.close() + + converted = os.path.join(parser.tempdir, "test_generate_pdf_from_mail.webp") + run_convert( + density=300, + scale="500x5000>", + alpha="remove", + strip=True, + trim=False, + auto_orient=True, + input_file=f"{pdf_path}", # Do net define an index to convert all pages. + output_file=converted, + logging_group=None, + ) + self.assertTrue(os.path.isfile(converted)) + thumb_hash = self.hashfile(converted) + + # The created pdf is not reproducible. But the converted image should always look the same. + expected_hash = ( + "8734a3f0a567979343824e468cd737bf29c02086bbfd8773e94feb986968ad32" + ) + self.assertEqual( + thumb_hash, + expected_hash, + f"PDF looks different. Check if {converted} looks weird.", + ) + + # Only run if convert is available + @pytest.mark.skipif( + "PAPERLESS_TEST_SKIP_CONVERT" in os.environ, + reason="PAPERLESS_TEST_SKIP_CONVERT set, skipping Test", + ) + @mock.patch("documents.loggers.LoggingMixin.log") # Disable log output + def test_generate_pdf_from_html(self, m): + # TODO + class MailAttachmentMock: + def __init__(self, payload, content_id): + self.payload = payload + self.content_id = content_id + + result = None + + with open(os.path.join(self.SAMPLE_FILES, "sample.html")) as html_file: + with open(os.path.join(self.SAMPLE_FILES, "sample.png"), "rb") as png_file: + html = html_file.read() + png = png_file.read() + attachments = [ + MailAttachmentMock(png, "part1.pNdUSz0s.D3NqVtPg@example.de"), + ] + result = self.parser.generate_pdf_from_html(html, attachments) + + pdf_path = os.path.join(self.parser.tempdir, "test_generate_pdf_from_html.pdf") + + with open(pdf_path, "wb") as file: + file.write(result) + file.close() + + converted = os.path.join(parser.tempdir, "test_generate_pdf_from_html.webp") + run_convert( + density=300, + scale="500x5000>", + alpha="remove", + strip=True, + trim=False, + auto_orient=True, + input_file=f"{pdf_path}", # Do net define an index to convert all pages. + output_file=converted, + logging_group=None, + ) + self.assertTrue(os.path.isfile(converted)) + thumb_hash = self.hashfile(converted) + + # The created pdf is not reproducible. But the converted image should always look the same. + expected_hash = ( + "267d61f0ab8f128a037002a424b2cb4bfe18a81e17f0b70f15d241688ed47d1a" + ) + self.assertEqual( + thumb_hash, + expected_hash, + f"PDF looks different. Check if {converted} looks weird. " + f"If Rick Astley is shown, Gotenberg loads from web which is bad for Mail content.", + ) + + @staticmethod + def test_is_online_image_still_available(): + """ + A public image is used in the html sample file. We have no control + whether this image stays online forever, so here we check if it is still there + """ + + # Start by Testing if nonexistent URL really throws an Exception + with pytest.raises(HTTPError): + urlopen("https://upload.wikimedia.org/wikipedia/en/f/f7/nonexistent.png") + + # Now check the URL used in samples/sample.html + urlopen("https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png")