Spaces:
Paused
Paused
| """Tests for PDF generation module.""" | |
| import pytest | |
| import tempfile | |
| from pathlib import Path | |
| from pipeline.pdf_generator import PDFGenerator, PDFResult, generate_sow_pdf, SOW_CSS | |
| class TestPDFGenerator: | |
| """Test PDF generator functionality.""" | |
| def generator(self): | |
| """Create PDF generator instance.""" | |
| return PDFGenerator() | |
| def sample_markdown(self): | |
| """Sample markdown for testing.""" | |
| return """# Test Document | |
| ## Section One | |
| This is a test paragraph with **bold** and *italic* text. | |
| | Column A | Column B | | |
| |----------|----------| | |
| | Value 1 | Value 2 | | |
| | Value 3 | Value 4 | | |
| ## Section Two | |
| - Bullet point one | |
| - Bullet point two | |
| - Bullet point three | |
| --- | |
| *Generated by test* | |
| """ | |
| def test_weasyprint_available(self, generator): | |
| """Test that WeasyPrint is detected as available.""" | |
| assert generator.weasyprint_available is True | |
| def test_markdown_to_html(self, generator, sample_markdown): | |
| """Test markdown to HTML conversion.""" | |
| html = generator.markdown_to_html(sample_markdown) | |
| assert "<!DOCTYPE html>" in html | |
| assert "<html>" in html | |
| assert "<style>" in html | |
| # Note: markdown library adds id attribute to headers (from TOC extension) | |
| assert "<h1" in html and "Test Document</h1>" in html | |
| assert "<table>" in html | |
| assert "<strong>bold</strong>" in html | |
| def test_markdown_to_html_includes_css(self, generator, sample_markdown): | |
| """Test that HTML includes CSS styling.""" | |
| html = generator.markdown_to_html(sample_markdown) | |
| # Check key CSS rules are included | |
| assert "font-family" in html | |
| assert "border-collapse" in html | |
| assert "@page" in html | |
| def test_generate_pdf_success(self, generator, sample_markdown): | |
| """Test successful PDF generation.""" | |
| result = generator.generate_pdf(sample_markdown) | |
| assert isinstance(result, PDFResult) | |
| assert result.success is True | |
| assert result.pdf_path is not None | |
| assert result.error_message is None | |
| assert result.file_size_bytes > 0 | |
| # Verify file exists | |
| pdf_path = Path(result.pdf_path) | |
| assert pdf_path.exists() | |
| assert pdf_path.suffix == ".pdf" | |
| # Clean up | |
| pdf_path.unlink() | |
| def test_generate_pdf_with_custom_path(self, generator, sample_markdown): | |
| """Test PDF generation with custom output path.""" | |
| with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as f: | |
| output_path = f.name | |
| result = generator.generate_pdf(sample_markdown, output_path=output_path) | |
| assert result.success is True | |
| assert result.pdf_path == output_path | |
| # Clean up | |
| Path(output_path).unlink() | |
| def test_generate_pdf_empty_content(self, generator): | |
| """Test PDF generation with empty content.""" | |
| result = generator.generate_pdf("") | |
| # Should still succeed with empty content | |
| assert result.success is True | |
| assert result.pdf_path is not None | |
| # Clean up | |
| Path(result.pdf_path).unlink() | |
| def test_generate_pdf_complex_tables(self, generator): | |
| """Test PDF with complex table content.""" | |
| markdown = """# Thresholds | |
| | Metal | Non-Operational | Operational | Unit | | |
| |-------|-----------------|-------------|------| | |
| | Lead | 22 | 500 | µg/100cm² | | |
| | Cadmium | 3.3 | 50 | µg/100cm² | | |
| | Arsenic | 6.7 | 100 | µg/100cm² | | |
| ## Notes | |
| Special characters: µ, °, ², ™ | |
| """ | |
| result = generator.generate_pdf(markdown) | |
| assert result.success is True | |
| assert result.file_size_bytes > 0 | |
| # Clean up | |
| Path(result.pdf_path).unlink() | |
| def test_generate_html_fallback(self, generator, sample_markdown): | |
| """Test HTML generation as fallback.""" | |
| success, html_path, error = generator.generate_html(sample_markdown) | |
| assert success is True | |
| assert html_path is not None | |
| assert error is None | |
| # Verify file exists and contains HTML | |
| html_path = Path(html_path) | |
| assert html_path.exists() | |
| content = html_path.read_text() | |
| assert "<html>" in content | |
| # Clean up | |
| html_path.unlink() | |
| def test_custom_css(self): | |
| """Test PDF generator with custom CSS.""" | |
| custom_css = """ | |
| body { font-family: monospace; } | |
| h1 { color: red; } | |
| """ | |
| generator = PDFGenerator(custom_css=custom_css) | |
| html = generator.markdown_to_html("# Test") | |
| assert "font-family: monospace" in html | |
| assert "color: red" in html | |
| class TestGenerateSowPdf: | |
| """Test the convenience function.""" | |
| def test_generate_sow_pdf(self): | |
| """Test generate_sow_pdf convenience function.""" | |
| markdown = """# Scope of Work | |
| ## Project: Test Fire | |
| | Field | Value | | |
| |-------|-------| | |
| | Client | ACME Corp | | |
| | Date | 2024-01-15 | | |
| ## Recommendations | |
| - Clean all surfaces | |
| - HEPA vacuum required | |
| """ | |
| result = generate_sow_pdf( | |
| markdown_content=markdown, | |
| project_name="Test Fire", | |
| ) | |
| assert result.success is True | |
| assert result.pdf_path is not None | |
| # Clean up | |
| Path(result.pdf_path).unlink() | |
| class TestSOWCSS: | |
| """Test CSS styling constants.""" | |
| def test_sow_css_exists(self): | |
| """Test that SOW_CSS is defined.""" | |
| assert SOW_CSS is not None | |
| assert len(SOW_CSS) > 0 | |
| def test_sow_css_has_page_settings(self): | |
| """Test that CSS includes page settings.""" | |
| assert "@page" in SOW_CSS | |
| assert "margin" in SOW_CSS | |
| def test_sow_css_has_table_styling(self): | |
| """Test that CSS includes table styling.""" | |
| assert "table" in SOW_CSS | |
| assert "border-collapse" in SOW_CSS | |
| assert "th" in SOW_CSS | |
| assert "td" in SOW_CSS | |
| def test_sow_css_has_header_styling(self): | |
| """Test that CSS includes header styling.""" | |
| assert "h1" in SOW_CSS | |
| assert "h2" in SOW_CSS | |
| class TestPDFResultDataclass: | |
| """Test PDFResult dataclass.""" | |
| def test_pdf_result_success(self): | |
| """Test PDFResult with success.""" | |
| result = PDFResult( | |
| success=True, | |
| pdf_path="/tmp/test.pdf", | |
| file_size_bytes=1000, | |
| ) | |
| assert result.success is True | |
| assert result.pdf_path == "/tmp/test.pdf" | |
| assert result.error_message is None | |
| assert result.file_size_bytes == 1000 | |
| def test_pdf_result_failure(self): | |
| """Test PDFResult with failure.""" | |
| result = PDFResult( | |
| success=False, | |
| pdf_path=None, | |
| error_message="Something went wrong", | |
| ) | |
| assert result.success is False | |
| assert result.pdf_path is None | |
| assert result.error_message == "Something went wrong" | |
| assert result.file_size_bytes == 0 | |