test(reports): add ReportEngine unit tests [FASE-2.1]
Stub WeasyPrint for CI-friendly PDF generation and verify HTML render, PDF path, and HTML file output.
This commit is contained in:
@@ -76,6 +76,10 @@ class ReportEngine:
|
||||
logger.info("DOCX generated: %s", output_path)
|
||||
return output_path
|
||||
|
||||
def generate_html(self, template_name: str, context: dict) -> str:
|
||||
"""Render and save a standalone HTML report (alias for spec compatibility)."""
|
||||
return self.generate_html_file(template_name, context)
|
||||
|
||||
def generate_html_file(self, template_name: str, context: dict) -> str:
|
||||
"""Render and save a standalone HTML report."""
|
||||
html_content = self.render_html(template_name, context)
|
||||
|
||||
63
backend/tests/test_report_engine.py
Normal file
63
backend/tests/test_report_engine.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Report engine unit tests (FASE-2.1)."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from app.services.report_engine import ReportEngine
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def engine(tmp_path, monkeypatch):
|
||||
templates = tmp_path / "templates"
|
||||
styles = templates / "styles"
|
||||
styles.mkdir(parents=True)
|
||||
(styles / "report.css").write_text("body { color: #0e7490; }", encoding="utf-8")
|
||||
(templates / "coverage_report.html").write_text(
|
||||
"<html><body><h1>{{ company_name }}</h1><p>{{ score }}</p></body></html>",
|
||||
encoding="utf-8",
|
||||
)
|
||||
out_dir = tmp_path / "out"
|
||||
monkeypatch.setattr("app.services.report_engine.settings.REPORT_TEMPLATES_DIR", str(templates))
|
||||
monkeypatch.setattr("app.services.report_engine.settings.REPORT_OUTPUT_DIR", str(out_dir))
|
||||
monkeypatch.setattr("app.services.report_engine.settings.COMPANY_NAME", "Test Org")
|
||||
return ReportEngine()
|
||||
|
||||
|
||||
def test_render_html_injects_company_and_timestamp(engine):
|
||||
html = engine.render_html("coverage_report", {"score": 42})
|
||||
assert "Test Org" in html
|
||||
assert "42" in html
|
||||
|
||||
|
||||
def test_generate_pdf_writes_file(engine, monkeypatch, tmp_path):
|
||||
"""WeasyPrint is stubbed so tests run without GTK/Pango system libraries."""
|
||||
def _write_pdf(output_path, stylesheets=None):
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(b"%PDF-1.4 mock")
|
||||
|
||||
mock_html_instance = MagicMock()
|
||||
mock_html_instance.write_pdf.side_effect = _write_pdf
|
||||
mock_wp = MagicMock()
|
||||
mock_wp.HTML.return_value = mock_html_instance
|
||||
mock_wp.CSS = MagicMock()
|
||||
for key in list(sys.modules):
|
||||
if key == "weasyprint" or key.startswith("weasyprint."):
|
||||
monkeypatch.delitem(sys.modules, key, raising=False)
|
||||
monkeypatch.setitem(sys.modules, "weasyprint", mock_wp)
|
||||
|
||||
path = engine.generate_pdf("coverage_report", {"score": 99})
|
||||
|
||||
assert path.endswith(".pdf")
|
||||
assert os.path.isfile(path)
|
||||
mock_html_instance.write_pdf.assert_called_once()
|
||||
|
||||
|
||||
def test_generate_html_file_writes_file(engine):
|
||||
path = engine.generate_html("coverage_report", {"score": 7})
|
||||
assert path.endswith(".html")
|
||||
assert os.path.isfile(path)
|
||||
with open(path, encoding="utf-8") as f:
|
||||
assert "Test Org" in f.read()
|
||||
Reference in New Issue
Block a user