"""Tests Sprint 67 — vue HTML d'un benchmark de pipeline composée. Couvre : 1. ``build_pipeline_summary_html`` : affiche pipeline, corpus, n_docs, succeeded/failed, durée totale. 2. ``build_pipeline_steps_table_html`` : tableau par étape avec colonnes attendues, métriques aux jonctions formatées, error_breakdown affiché, vide si aucune étape. 3. ``build_pipeline_report_html`` : document HTML autonome (````, head, body, styles inline). 4. Anti-injection HTML : noms de pipeline / corpus / step contenant ``", corpus_name="demo", ) html = build_pipeline_summary_html(bench) assert "" not in html assert "<script>" in html def test_corpus_name_escaped(self) -> None: bench = PipelineBenchmarkResult( pipeline_name="p", corpus_name="", ) html = build_pipeline_report_html(bench) assert " None: bench = PipelineBenchmarkResult( pipeline_name="p", corpus_name="c", ) bench.per_step_aggregates = [ StepAggregate( step_name="