""" Tests for web/services/pdf_service.py Tests cover: - PDFService initialization - Markdown to HTML conversion - Markdown to PDF conversion - Error handling - Singleton pattern """ import pytest from unittest.mock import Mock, patch class TestPDFServiceInit: """Tests for PDFService initialization.""" def test_pdf_service_init(self): """Test PDFService initializes with minimal CSS.""" from local_deep_research.web.services.pdf_service import PDFService service = PDFService() assert service.minimal_css is not None def test_pdf_service_css_contains_a4(self): """Test that CSS sets A4 page size.""" from local_deep_research.web.services.pdf_service import PDFService service = PDFService() # The CSS string should be accessible assert service.minimal_css is not None def test_minimal_css_excludes_emoji_font_families(self): """No font-family stack may list an emoji font explicitly. Digits 0-9, '#' and '*' carry the Unicode Emoji property and have glyphs in Noto Color Emoji, so Pango routes them to an explicitly listed emoji font even when an earlier family in the stack covers them — every number in an exported report then renders as wide, square emoji glyphs ("2 0 2 6" instead of "2026"). Emoji still render via Pango/fontconfig per-character fallback to an installed emoji font, so the stacks must not name one. Regression test for the digit-capture bug introduced by #4730. """ import re from local_deep_research.web.services.pdf_service import MINIMAL_CSS font_family_blocks = re.findall(r"font-family:\s*([^;]+);", MINIMAL_CSS) assert len(font_family_blocks) >= 2, ( "expected at least body + code font-family declarations" ) for block in font_family_blocks: assert "emoji" not in block.lower(), ( f"font-family block must not list an emoji font (it would " f"capture digits): {block!r}" ) def test_digits_not_rendered_with_emoji_font(self): """Digits in a rendered PDF must come from a text font, not an emoji font. Functional regression test for the digit-capture bug: with an emoji family in the font-family stack, Pango renders 0-9 with Noto Color Emoji (wide square glyphs). Renders a digit-heavy document and asserts no digit glyph is drawn with an emoji font. On hosts with no emoji font installed the assertion passes trivially, which is correct — there is no emoji font to capture the digits. """ import io pytest.importorskip("pdfminer.high_level") from pdfminer.high_level import extract_pages from pdfminer.layout import LTChar, LTTextContainer, LTTextLine from local_deep_research.web.services.pdf_service import PDFService service = PDFService() pdf_bytes = service.markdown_to_pdf( "As of July 11, 2026, plans cost $20/month [21][30]." ) digit_fonts = set() for page in extract_pages(io.BytesIO(pdf_bytes)): for element in page: if not isinstance(element, LTTextContainer): continue for line in element: if not isinstance(line, LTTextLine): continue for char in line: if ( isinstance(char, LTChar) and char.get_text().isdigit() ): digit_fonts.add(char.fontname) assert digit_fonts, "expected the rendered PDF to contain digits" emoji_fonts = {f for f in digit_fonts if "emoji" in f.lower()} assert not emoji_fonts, ( f"digits were rendered with an emoji font: {emoji_fonts}" ) class TestMarkdownToHTML: """Tests for _markdown_to_html method.""" def test_markdown_to_html_basic(self): """Test basic markdown conversion.""" from local_deep_research.web.services.pdf_service import PDFService service = PDFService() markdown = "# Hello World\n\nThis is a **test**." html = service._markdown_to_html(markdown) # h1 may have an id attribute added by markdown TOC extension assert "Hello World" in html assert "test" in html assert "" in html assert "" in html def test_markdown_to_html_with_title(self): """Test conversion with title.""" from local_deep_research.web.services.pdf_service import PDFService service = PDFService() markdown = "Content here" html = service._markdown_to_html(markdown, title="Test Title") assert "Test Title" in html def test_markdown_to_html_escapes_html_in_title(self): """Test that HTML special characters in title are escaped.""" from local_deep_research.web.services.pdf_service import PDFService service = PDFService() html = service._markdown_to_html( "content", title='' ) assert "