"""Tests for marketing_intelligence.reporting.pdf_generator.""" from __future__ import annotations from pathlib import Path from typing import Any from marketing_intelligence.core.models import TrendSignal from marketing_intelligence.reporting.pdf_generator import ( build_pdf, save_pdf_temp, ) _MOD = "marketing_intelligence.reporting.pdf_generator" # ──────────────────────────── fixtures ──────────────────────────────────────── def _minimal_sentiment(**overrides: object) -> dict[str, Any]: base: dict[str, Any] = dict( campaign_config_key="artist_track", run_id="run1", observed_at="2024-01-01T00:00:00Z", sentiment="positive", confidence=0.85, summary="Fans love it.", reasoning="High like rate.", how_sound_is_used="background melody", confidence_rationale="Multiple signals agree.", ) base.update(overrides) # coerce TrendSignal objects to dicts so the generator receives plain dicts if "trend_signals" in base: base["trend_signals"] = [ s.model_dump() if isinstance(s, TrendSignal) else s for s in base["trend_signals"] ] return base # ──────────────────────────── build_pdf ─────────────────────────────────────── class TestBuildPdf: def test_creates_file(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(), out) assert out.exists() def test_pdf_starts_with_magic_bytes(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(), out) assert out.read_bytes()[:4] == b"%PDF" def test_non_empty_output(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(), out) assert out.stat().st_size > 1024 def test_negative_sentiment(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(sentiment="negative", confidence=0.4), out) assert out.read_bytes()[:4] == b"%PDF" def test_neutral_sentiment(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(sentiment="neutral", confidence=0.6), out) assert out.read_bytes()[:4] == b"%PDF" def test_with_trend_signals(self, tmp_path: Path) -> None: signals = [ TrendSignal( signal="Viral loop", evidence="10M views in 24h", supporting_video_ids=["v1", "v2", "v3"], ) ] out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(trend_signals=signals), out) assert out.read_bytes()[:4] == b"%PDF" def test_with_many_trend_signals(self, tmp_path: Path) -> None: signals = [ TrendSignal(signal=f"sig{i}", evidence="ev", supporting_video_ids=[f"v{i}"]) for i in range(5) ] out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(trend_signals=signals), out) assert out.read_bytes()[:4] == b"%PDF" def test_with_hashtags(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf( _minimal_sentiment( primary_hashtags=["dance", "viral", "fyp", "trending", "music"] ), out, ) assert out.read_bytes()[:4] == b"%PDF" def test_with_detected_topics(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf( _minimal_sentiment(detected_topics=["nostalgia", "summer", "party"]), out ) assert out.read_bytes()[:4] == b"%PDF" def test_with_virality_factors(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf( _minimal_sentiment(virality_factors=["fast tempo", "relatable lyrics"]), out ) assert out.read_bytes()[:4] == b"%PDF" def test_empty_optional_lists(self, tmp_path: Path) -> None: out = tmp_path / "report.pdf" build_pdf( _minimal_sentiment( primary_hashtags=[], detected_topics=[], virality_factors=[], trend_signals=[], ), out, ) assert out.read_bytes()[:4] == b"%PDF" def test_long_text_fields(self, tmp_path: Path) -> None: long_text = "word " * 200 out = tmp_path / "report.pdf" build_pdf( _minimal_sentiment( summary=long_text, reasoning=long_text, how_sound_is_used=long_text, confidence_rationale=long_text, ), out, ) assert out.read_bytes()[:4] == b"%PDF" def test_trend_signal_many_video_ids(self, tmp_path: Path) -> None: signals = [ TrendSignal( signal="big", evidence="ev", supporting_video_ids=[f"v{i}" for i in range(20)], ) ] out = tmp_path / "report.pdf" build_pdf(_minimal_sentiment(trend_signals=signals), out) assert out.read_bytes()[:4] == b"%PDF" # ──────────────────────────── save_pdf_temp ────────────────────────────────── class TestSavePdfTemp: def test_creates_temp_file(self) -> None: path = save_pdf_temp(_minimal_sentiment()) p = Path(path) assert p.exists() p.unlink() def test_temp_file_is_valid_pdf(self) -> None: path = save_pdf_temp(_minimal_sentiment()) p = Path(path) assert p.read_bytes()[:4] == b"%PDF" p.unlink() def test_temp_filename_has_prefix(self) -> None: path = save_pdf_temp(_minimal_sentiment()) assert "artist_track" in Path(path).name Path(path).unlink()