"""End-to-end round-trip regression tests (FR-1100). Tests the full build → import → compare cycle using the SIMPLE_MARKDOWN fixture and per-family smoke tests. """ from __future__ import annotations import textwrap from pathlib import Path import yaml from markidocx.builder import build_document from markidocx.differ import compare from markidocx.importer import import_document from markidocx.manifest import load_manifest LEVEL1_MARKDOWN = textwrap.dedent("""\ # Document Title This is the introduction paragraph. ## Section One - Item alpha - Item beta - Item gamma ## Section Two | Column A | Column B | |----------|----------| | row1a | row1b | | row2a | row2b | 1. First ordered item 2. Second ordered item """) def _make_project(tmp_path: Path, family: str, content: str) -> Path: """Create a minimal project directory and return the manifest path.""" (tmp_path / "doc.md").write_text(content, encoding="utf-8") manifest_path = tmp_path / "manifest.yaml" manifest_path.write_text( yaml.dump( { "project": {"name": f"test-{family}", "feature_level": "level1", "family": family}, "sources": [{"path": "doc.md"}], "output": {"dir": "./dist"}, } ) ) (tmp_path / "dist").mkdir() return manifest_path class TestRoundtripArticle: def test_build_import_cycle(self, tmp_path: Path) -> None: manifest_path = _make_project(tmp_path, "article", LEVEL1_MARKDOWN) manifest = load_manifest(manifest_path) build_result = build_document(manifest) assert build_result.success, f"Build failed: {build_result.errors}" import_result = import_document(manifest, build_result.output_path) assert import_result.success, f"Import failed: {import_result.warnings}" assert import_result.mapping_status == "redistributed" def test_heading_structure_preserved(self, tmp_path: Path) -> None: manifest_path = _make_project(tmp_path, "article", LEVEL1_MARKDOWN) manifest = load_manifest(manifest_path) build_result = build_document(manifest) import_result = import_document(manifest, build_result.output_path) assert import_result.success md = import_result.output_files[0].read_text(encoding="utf-8") report = compare(LEVEL1_MARKDOWN, md) broken_headings = [b for b in report.broken if b.startswith("heading:")] assert not broken_headings, f"Headings lost in round-trip: {broken_headings}" def test_no_errors_on_clean_roundtrip(self, tmp_path: Path) -> None: manifest_path = _make_project(tmp_path, "article", LEVEL1_MARKDOWN) manifest = load_manifest(manifest_path) build_result = build_document(manifest) assert not build_result.errors class TestRoundtripBook: def test_build_import_cycle(self, tmp_path: Path) -> None: manifest_path = _make_project(tmp_path, "book", LEVEL1_MARKDOWN) manifest = load_manifest(manifest_path) build_result = build_document(manifest) assert build_result.success import_result = import_document(manifest, build_result.output_path) assert import_result.success class TestRoundtripWebsite: def test_build_import_cycle(self, tmp_path: Path) -> None: manifest_path = _make_project(tmp_path, "website", LEVEL1_MARKDOWN) manifest = load_manifest(manifest_path) build_result = build_document(manifest) assert build_result.success import_result = import_document(manifest, build_result.output_path) assert import_result.success class TestMultiFileRoundtrip: def test_two_source_files(self, tmp_path: Path) -> None: ch1 = textwrap.dedent("""\ # Chapter One Introduction text. - Point one - Point two """) ch2 = textwrap.dedent("""\ # Chapter Two Conclusion text. ## Subsection Final paragraph. """) (tmp_path / "ch1.md").write_text(ch1, encoding="utf-8") (tmp_path / "ch2.md").write_text(ch2, encoding="utf-8") manifest_path = tmp_path / "manifest.yaml" manifest_path.write_text( yaml.dump( { "project": {"name": "two-chap", "feature_level": "level1", "family": "book"}, "sources": [{"path": "ch1.md"}, {"path": "ch2.md"}], "output": {"dir": "./dist"}, } ) ) (tmp_path / "dist").mkdir() manifest = load_manifest(manifest_path) build_result = build_document(manifest) assert build_result.success import_result = import_document(manifest, build_result.output_path) assert import_result.success # Should have 2 output files (redistributed) or 1 merged assert len(import_result.output_files) >= 1