import yaml from markitect_tool.render import ( RENDER_REFERENCE_MANIFEST_KIND, RENDER_REFERENCE_SCHEMA_VERSION, RenderAsset, RenderAssetManifest, RenderCrossReference, RenderExportRequest, RenderReferenceError, RenderReferenceManifest, RenderSourceMap, RenderSourceSpan, RenderTocEntry, RenderUnitReference, render_asset_id, render_manifest_id, render_unit_id, render_with_adapter, ) from markitect_tool.source import SourceAsset def test_render_unit_reference_serializes_with_stable_id_and_no_final_numbering(): span = RenderSourceSpan( source_path="docs/report.md", line_start=12, line_end=16, selector="#fig-revenue", ) first = RenderUnitReference( kind="figure", label="Figure", caption="Quarterly revenue", source_path="docs/report.md", anchor="fig:revenue", source_span=span, content_hash="sha256:figure-content", ordinal_hint=1, numbering={"scope": "document", "sequence": "figures"}, ) second = RenderUnitReference( kind="figure", label="Figure", caption="Quarterly revenue", source_path="docs/report.md", anchor="fig:revenue", source_span=span, content_hash="sha256:figure-content", ordinal_hint=1, numbering={"scope": "document", "sequence": "figures"}, ) assert first.unit_id == second.unit_id assert first.unit_id == render_unit_id( "figure", source_path="docs/report.md", anchor="fig:revenue", content_hash="sha256:figure-content", ordinal_hint=1, title="Quarterly revenue", ) assert "final_number" not in first.to_dict() assert RenderUnitReference.from_dict(first.to_dict()).to_dict() == first.to_dict() def test_render_manifest_models_cross_references_and_toc_before_layout(): section = RenderUnitReference( kind="section", title="Results", anchor="results", source_path="docs/report.md", ordinal_hint=1, ) table = RenderUnitReference( kind="table", label="Table", caption="Quarterly totals", anchor="tbl:totals", source_path="docs/report.md", ordinal_hint=2, numbering={"scope": "section", "sequence": "tables"}, ) cross_reference = RenderCrossReference( source_unit_id=section.unit_id, target_unit_id=table.unit_id, label="Table", requested_style="numbered", fallback_text="Quarterly totals", ) toc_entry = RenderTocEntry( unit_id=section.unit_id, title="Results", level=1, order=1, ) manifest = RenderReferenceManifest( source_path="docs/report.md", source_digest="sha256:report", units=[section, table], cross_references=[cross_reference], toc=[toc_entry], ) data = manifest.to_dict() assert data["schema_version"] == RENDER_REFERENCE_SCHEMA_VERSION assert data["kind"] == RENDER_REFERENCE_MANIFEST_KIND assert data["cross_references"][0]["target_unit_id"] == table.unit_id assert "final_number" not in data["cross_references"][0] assert "href" not in data["toc"][0] assert RenderReferenceManifest.from_dict(data).to_dict() == data assert render_manifest_id(manifest) == manifest.manifest_id def test_render_asset_manifest_preserves_source_adapter_attachment_metadata(): source_asset = SourceAsset( uri="attachments/chart.png", path="attachments/chart.png", name="chart.png", media_type="image/png", extension=".png", digest="sha256:chart", metadata={"source_adapter": "markitect-filter"}, ) render_asset = RenderAsset.from_source_asset( source_asset, role="figure-image", copy_policy="copy", output_reference="renderer://asset/chart", ) skipped_asset = RenderAsset( source_uri="https://example.test/logo.svg", media_type="image/svg+xml", extension=".svg", digest="sha256:logo", role="brand-logo", copy_policy="link", output_reference="renderer://asset/logo", ) manifest = RenderAssetManifest( assets=[skipped_asset, render_asset], source_path="docs/report.md", source_digest="sha256:report", ) data = manifest.to_dict() assert [asset["asset_id"] for asset in data["assets"]] == sorted( [render_asset.asset_id, skipped_asset.asset_id] ) assert render_asset.asset_id == render_asset_id( "attachments/chart.png", digest="sha256:chart", role="figure-image", output_reference="renderer://asset/chart", ) chart_entry = next(asset for asset in data["assets"] if asset["asset_id"] == render_asset.asset_id) assert chart_entry["provenance"][0]["digest"].startswith("sha256:") assert chart_entry["copy_policy"] == "copy" assert RenderAssetManifest.from_dict(data).to_dict() == data def test_render_source_maps_are_echoed_by_fake_renderer_metadata(): section = RenderUnitReference( kind="section", title="Demo", anchor="demo", source_path="docs/demo.md", ) source_map = RenderSourceMap( source=RenderSourceSpan(source_path="docs/demo.md", line_start=1, line_end=3), source_unit_id=section.unit_id, generated_by="document.function.echo", function_run_id="run:echo", render_unit_id=section.unit_id, artifact_ref="renderer-source", ) asset = RenderAsset( source_uri="images/demo.png", source_path="images/demo.png", media_type="image/png", extension=".png", digest="sha256:demo-image", role="figure-image", copy_policy="copy", output_reference="renderer://asset/demo-image", ) manifest = RenderReferenceManifest( units=[section], source_maps=[source_map], asset_manifest=RenderAssetManifest(assets=[asset]), source_path="docs/demo.md", ) result = render_with_adapter( RenderExportRequest( source="# Demo\n\n![Demo](images/demo.png)", operation="render-artifact", profile="docs", source_path="docs/demo.md", render_manifest=manifest, ) ) assert result.valid assert result.metadata["external_renderer_invoked"] is False assert result.metadata["render_reference_manifest_id"] == manifest.manifest_id assert result.metadata["render_source_maps"] == 1 assert result.artifacts[0].metadata["asset_manifest_id"] == manifest.asset_manifest.manifest_id def test_render_reference_manifest_rejects_unknown_cross_reference_targets(): unit = RenderUnitReference(kind="section", title="Known", anchor="known") try: RenderReferenceManifest( units=[unit], cross_references=[RenderCrossReference(target_unit_id="missing")], ) except RenderReferenceError as exc: assert "targets unknown unit" in str(exc) else: raise AssertionError("expected unknown cross-reference target to fail") def test_render_reference_manifest_example_loads_and_roundtrips(): with open("examples/render/render-reference-manifest.yaml", encoding="utf-8") as handle: data = yaml.safe_load(handle) manifest = RenderReferenceManifest.from_dict(data) assert manifest.schema_version == RENDER_REFERENCE_SCHEMA_VERSION assert manifest.asset_manifest.assets assert manifest.to_dict()["kind"] == RENDER_REFERENCE_MANIFEST_KIND