Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

516 lines
16 KiB
Python

"""
Extended Tests for PDF Service
Phase 19: Socket & Real-time Services - PDF Service Tests
Tests PDF generation and extraction functionality.
"""
import pytest
from datetime import datetime, UTC
from unittest.mock import patch, MagicMock
class TestPDFGeneration:
"""Tests for PDF generation functionality"""
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_from_markdown(self, mock_service_cls):
"""Test PDF generation from markdown"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 mock content"
markdown = "# Test Title\n\nThis is a test paragraph."
result = mock_service.markdown_to_pdf(markdown)
assert result.startswith(b"%PDF")
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_with_images(self, mock_service_cls):
"""Test PDF generation with embedded images"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with images"
markdown = """
# Report with Image
![Chart](data:image/png;base64,iVBORw0KGgo=)
This is a caption.
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_with_tables(self, mock_service_cls):
"""Test PDF generation with tables"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with tables"
markdown = """
# Report with Table
| Column 1 | Column 2 |
|----------|----------|
| Value 1 | Value 2 |
| Value 3 | Value 4 |
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_with_code_blocks(self, mock_service_cls):
"""Test PDF generation with code blocks"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with code"
markdown = """
# Code Example
```python
def hello():
print("Hello, World!")
```
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_with_math(self, mock_service_cls):
"""Test PDF generation with math expressions"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with math"
markdown = """
# Math Example
The quadratic formula is: $x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}$
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_unicode_content(self, mock_service_cls):
"""Test PDF generation with unicode content"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with unicode"
markdown = """
# Unicode Test
Chinese: 中文测试
Japanese: 日本語テスト
Emoji: 🔬📊📈
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_large_document(self, mock_service_cls):
"""Test PDF generation for large documents"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 large document"
# Generate large markdown content
sections = [
f"## Section {i}\n\nContent for section {i}.\n\n"
for i in range(100)
]
markdown = "# Large Document\n\n" + "".join(sections)
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_page_layout(self, mock_service_cls):
"""Test PDF page layout settings"""
mock_service = MagicMock()
mock_service._get_page_settings.return_value = {
"size": "A4",
"margins": {
"top": "1.5cm",
"bottom": "1.5cm",
"left": "1.5cm",
"right": "1.5cm",
},
}
settings = mock_service._get_page_settings()
assert settings["size"] == "A4"
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_headers_footers(self, mock_service_cls):
"""Test PDF headers and footers"""
mock_service = MagicMock()
mock_service._add_headers_footers.return_value = True
result = mock_service._add_headers_footers(
"Test Report", {"page_numbers": True}
)
assert result is True
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_table_of_contents(self, mock_service_cls):
"""Test PDF table of contents generation"""
mock_service = MagicMock()
mock_service._generate_toc.return_value = """
## Table of Contents
1. [Section 1](#section-1)
2. [Section 2](#section-2)
"""
toc = mock_service._generate_toc(["Section 1", "Section 2"])
assert "Section 1" in toc
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_hyperlinks(self, mock_service_cls):
"""Test PDF hyperlink preservation"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with links"
markdown = """
# Links Test
Visit [Example](https://example.com) for more information.
"""
result = mock_service.markdown_to_pdf(markdown)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_metadata(self, mock_service_cls):
"""Test PDF metadata embedding"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.return_value = b"%PDF-1.4 with metadata"
result = mock_service.markdown_to_pdf(
"# Test",
metadata={
"title": "Test Report",
"author": "Test Author",
"created": datetime.now(UTC).isoformat(),
},
)
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_compression(self, mock_service_cls):
"""Test PDF compression"""
mock_service = MagicMock()
mock_service._compress_pdf.return_value = b"%PDF-1.4 compressed"
original = b"%PDF-1.4 original content" * 1000
compressed = mock_service._compress_pdf(original)
# Compressed should be smaller or equal
assert len(compressed) <= len(original)
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_error_recovery(self, mock_service_cls):
"""Test error recovery during PDF generation"""
mock_service = MagicMock()
mock_service.markdown_to_pdf.side_effect = [
Exception("First attempt failed"),
b"%PDF-1.4 success on retry",
]
# First call fails
with pytest.raises(Exception):
mock_service.markdown_to_pdf("# Test")
# Second call succeeds
result = mock_service.markdown_to_pdf("# Test")
assert result is not None
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_generate_pdf_timeout_handling(self, mock_service_cls):
"""Test timeout handling during generation"""
mock_service = MagicMock()
mock_service._generate_with_timeout.return_value = {
"success": True,
"pdf": b"%PDF-1.4",
}
result = mock_service._generate_with_timeout(
"# Test", timeout_seconds=30
)
assert result["success"] is True
class TestPDFExtraction:
"""Tests for PDF extraction functionality"""
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_text_from_pdf(self, mock_service_cls):
"""Test text extraction from PDF"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "Extracted text content",
"pages": 5,
"success": True,
}
result = mock_service.extract_text_and_metadata(
b"%PDF-1.4 content", "test.pdf"
)
assert result["success"] is True
assert len(result["text"]) > 0
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_text_corrupted_pdf(self, mock_service_cls):
"""Test handling corrupted PDF"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "",
"success": False,
"error": "Invalid PDF format",
}
result = mock_service.extract_text_and_metadata(
b"not a valid pdf", "corrupted.pdf"
)
assert result["success"] is False
assert "error" in result
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_text_encrypted_pdf(self, mock_service_cls):
"""Test handling encrypted PDF"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "",
"success": False,
"error": "PDF is encrypted",
}
result = mock_service.extract_text_and_metadata(
b"%PDF-1.4 encrypted", "encrypted.pdf"
)
assert result["success"] is False
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_text_scanned_pdf(self, mock_service_cls):
"""Test handling scanned (image-based) PDF"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "",
"pages": 3,
"success": True,
"warning": "PDF appears to be image-based",
}
result = mock_service.extract_text_and_metadata(
b"%PDF-1.4 scanned", "scanned.pdf"
)
# May succeed but with empty or minimal text
assert result["success"] is True
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_metadata_from_pdf(self, mock_service_cls):
"""Test metadata extraction"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "Content",
"pages": 10,
"size": 50000,
"filename": "research.pdf",
"success": True,
}
result = mock_service.extract_text_and_metadata(
b"%PDF-1.4 content", "research.pdf"
)
assert result["pages"] == 10
assert result["size"] == 50000
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_images_from_pdf(self, mock_service_cls):
"""Test image extraction from PDF"""
mock_service = MagicMock()
mock_service._extract_images.return_value = [
{"page": 1, "image": b"image1"},
{"page": 3, "image": b"image2"},
]
images = mock_service._extract_images(b"%PDF-1.4 with images")
assert len(images) == 2
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_tables_from_pdf(self, mock_service_cls):
"""Test table extraction from PDF"""
mock_service = MagicMock()
mock_service._extract_tables.return_value = [
{"page": 1, "data": [["Header", "Value"], ["Row", "Data"]]}
]
tables = mock_service._extract_tables(b"%PDF-1.4 with tables")
assert len(tables) == 1
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_large_pdf_streaming(self, mock_service_cls):
"""Test streaming extraction for large PDFs"""
mock_service = MagicMock()
mock_service._extract_streaming.return_value = iter(
[
{"page": 1, "text": "Page 1 text"},
{"page": 2, "text": "Page 2 text"},
]
)
pages = list(mock_service._extract_streaming(b"%PDF-1.4 large"))
assert len(pages) == 2
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_pdf_page_selection(self, mock_service_cls):
"""Test extracting specific pages"""
mock_service = MagicMock()
mock_service._extract_pages.return_value = {
"text": "Pages 5-10 content",
"pages_extracted": [5, 6, 7, 8, 9, 10],
}
result = mock_service._extract_pages(
b"%PDF-1.4 content", start_page=5, end_page=10
)
assert len(result["pages_extracted"]) == 6
@patch(
"local_deep_research.web.services.pdf_extraction_service.PDFExtractionService"
)
def test_extract_pdf_timeout_handling(self, mock_service_cls):
"""Test extraction timeout handling"""
mock_service = MagicMock()
mock_service.extract_text_and_metadata.return_value = {
"text": "",
"success": False,
"error": "Extraction timeout",
}
result = mock_service.extract_text_and_metadata(
b"%PDF-1.4 large complex pdf", "huge.pdf"
)
# May fail due to timeout
assert "error" in result or result["success"] is True
class TestHTMLToMarkdown:
"""Tests for HTML to markdown conversion"""
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_markdown_to_html_conversion(self, mock_service_cls):
"""Test markdown to HTML conversion"""
mock_service = MagicMock()
mock_service._markdown_to_html.return_value = (
"<h1>Title</h1><p>Content</p>"
)
html = mock_service._markdown_to_html("# Title\n\nContent")
assert "<h1>" in html
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_markdown_extensions(self, mock_service_cls):
"""Test markdown extensions are applied"""
mock_service = MagicMock()
mock_service._get_markdown_extensions.return_value = [
"tables",
"fenced_code",
"footnotes",
"toc",
]
extensions = mock_service._get_markdown_extensions()
assert "tables" in extensions
assert "fenced_code" in extensions
class TestCSSGeneration:
"""Tests for CSS generation"""
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_default_css_generation(self, mock_service_cls):
"""Test default CSS is generated"""
mock_service = MagicMock()
mock_service._get_default_css.return_value = """
@page { size: A4; margin: 1.5cm; }
body { font-family: Arial; font-size: 10pt; }
"""
css = mock_service._get_default_css()
assert "@page" in css
assert "A4" in css
@patch("local_deep_research.web.services.pdf_service.PDFService")
def test_custom_css_application(self, mock_service_cls):
"""Test custom CSS is applied"""
mock_service = MagicMock()
mock_service._apply_custom_css.return_value = """
body { font-size: 12pt; color: #333; }
"""
custom_css = "body { font-size: 12pt; color: #333; }"
result = mock_service._apply_custom_css(custom_css)
assert "12pt" in result
class TestSingleton:
"""Tests for PDF service singleton"""
def test_get_pdf_service_returns_instance(self):
"""Test get_pdf_service returns an instance"""
from local_deep_research.web.services.pdf_service import get_pdf_service
service = get_pdf_service()
assert service is not None
def test_get_pdf_service_singleton(self):
"""Test get_pdf_service returns same instance"""
from local_deep_research.web.services.pdf_service import get_pdf_service
service1 = get_pdf_service()
service2 = get_pdf_service()
assert service1 is service2