"""Tests for the HTML emit renderer.""" from __future__ import annotations import tempfile import unittest from html.parser import HTMLParser from pathlib import Path import last30days as cli from lib import html_render, schema def _report(topic: str, cluster_titles: list[str]) -> schema.Report: items: list[schema.SourceItem] = [] candidates: list[schema.Candidate] = [] clusters: list[schema.Cluster] = [] for index, title in enumerate(cluster_titles, start=1): item = schema.SourceItem( item_id=f"item-{index}", source="grounding", title=title, body=f"Body for {title}", url=f"https://example.test/{index}", container="example.test", published_at="2026-04-20", date_confidence="high", engagement={"views": index * 100}, snippet=f"Snippet for {title}", ) candidate = schema.Candidate( candidate_id=f"candidate-{index}", item_id=item.item_id, source="grounding", title=title, url=item.url, snippet=item.snippet, subquery_labels=["primary"], native_ranks={"primary:grounding": index}, local_relevance=0.9, freshness=80, engagement=50, source_quality=1.0, rrf_score=0.5, final_score=90 - index, sources=["grounding"], source_items=[item], ) cluster = schema.Cluster( cluster_id=f"cluster-{index}", title=title, candidate_ids=[candidate.candidate_id], representative_ids=[candidate.candidate_id], sources=["grounding"], score=90 - index, ) items.append(item) candidates.append(candidate) clusters.append(cluster) return schema.Report( topic=topic, range_from="2026-03-30", range_to="2026-04-29", generated_at="2026-04-29T12:00:00+00:00", provider_runtime=schema.ProviderRuntime( reasoning_provider="local", planner_model="mock-planner", rerank_model="mock-rerank", ), query_plan=schema.QueryPlan( intent="research", freshness_mode="balanced_recent", cluster_mode="story", raw_topic=topic, subqueries=[ schema.SubQuery( label="primary", search_query=topic, ranking_query=topic, sources=["grounding"], ) ], source_weights={"grounding": 1.0}, ), clusters=clusters, ranked_candidates=candidates, items_by_source={"grounding": items}, errors_by_source={}, artifacts={"pre_research_flags_present": True}, ) def _assert_parses(test_case: unittest.TestCase, html: str) -> None: parser = HTMLParser() parser.feed(html) parser.close() test_case.assertIn("", html) class HtmlRenderSnapshotTests(unittest.TestCase): def test_rich_cluster_fixture_snapshot(self): rendered = html_render.render_html( _report("AI agent frameworks", ["OpenClaw ships containers", "Skills marketplace grows"]) ) snapshot_markers = [ "", "last30days ยท AI agent frameworks", '
๐ŸŒ last30days v', '
2026-03-30 to 2026-04-29', '', '
2026-03-30 to 2026-04-29', '/last30days OpenClaw vs Hermes', ] for marker in snapshot_markers: self.assertIn(marker, rendered) class HtmlRenderBehaviorTests(unittest.TestCase): def test_prose_label_promotion(self): md = html_render._promote_prose_labels("What I learned:") rendered = html_render._markdown_to_html(md) self.assertIn("

What I learned

", rendered) self.assertNotIn("What I learned:", rendered) def test_invitation_strip(self): md = "---\nI'm now an expert on OpenClaw. Some things you could ask:\n\nJust ask." self.assertNotIn("I'm now an expert", html_render._strip_invitation(md)) def test_evidence_block_strip(self): md = "keep\n\nsecret\n" stripped = html_render._strip_evidence_block(md) self.assertIn("keep", stripped) self.assertNotIn("EVIDENCE FOR SYNTHESIS", stripped) self.assertNotIn("secret", stripped) def test_engine_footer_wrapping_preserves_tree(self): md = ( "\n" "โœ… All agents reported back!\n" "โ”œโ”€ ๐Ÿ”ต X: 2 posts\n" "โ””โ”€ ๐ŸŒ Web: 1 result\n" "" ) body = html_render._wrap_engine_footer(html_render._markdown_to_html(md)) self.assertIn('' ) def test_meta_marker_preserves_plain_text(self): """Legitimate date/source-name markers render unchanged (no double-escape).""" body = html_render._promote_meta_marker( "" ) self.assertEqual( body, '
2026-01-01 to 2026-01-31 ยท reddit, x
', ) def test_markdown_links_allow_relative_url(self): rendered = html_render._markdown_to_html("[home](/path?x=1#section)") self.assertIn( '
home', rendered, ) def test_markdown_links_allow_mailto(self): rendered = html_render._markdown_to_html("[mail](mailto:a@example.com)") self.assertIn( 'mail', rendered, ) def test_no_file_header_h1(self): rendered = html_render.render_html(_report("AI agent frameworks", ["One"])) self.assertNotIn("

last30days v", rendered) def test_no_safety_note(self): rendered = html_render.render_html(_report("AI agent frameworks", ["One"])) self.assertNotIn("Safety note", rendered) def test_synthesis_md_embedded(self): synthesis = "**Test brief** - body content per [@example](https://example.com)" rendered = html_render.render_html( _report("AI agent frameworks", ["One"]), synthesis_md=synthesis, ) self.assertIn("Test brief - body content per", rendered) self.assertIn( '@example', rendered, ) metadata_index = rendered.index('
') synthesis_index = rendered.index("Test brief") footer_index = rendered.index('