from pathlib import Path import pytest from click.testing import CliRunner from markitect_tool.cli import main from markitect_tool.ops import ( IncludeError, compose_files, resolve_includes, transform_markdown, ) def test_transform_sets_frontmatter_and_shifts_headings(): markdown = """--- title: Original --- # Intro ## Detail """ result = transform_markdown( markdown, set_frontmatter={"status": "draft", "nested": {"owner": "Docs"}}, heading_delta=1, ) assert "title: Original" in result.markdown assert "status: draft" in result.markdown assert "owner: Docs" in result.markdown assert "## Intro" in result.markdown assert "### Detail" in result.markdown assert result.operations == ["set_frontmatter", "shift_headings:1"] def test_transform_extracts_selector_text(): markdown = """# Doc ## Keep This section should remain. ## Drop This section should not remain. """ result = transform_markdown(markdown, extract_selector="sections[heading=Keep]") assert result.markdown == "## Keep\n\nThis section should remain." assert "Drop" not in result.markdown def test_compose_files_adds_title_and_separators(tmp_path: Path): one = tmp_path / "one.md" two = tmp_path / "two.md" one.write_text("# One\n\nText one.", encoding="utf-8") two.write_text("---\ntitle: Two\n---\n\n# Two\n\nText two.", encoding="utf-8") result = compose_files([one, two], title="Combined", heading_delta=1) assert result.sources == [str(one), str(two)] assert result.markdown.startswith("# Combined") assert "## One" in result.markdown assert "## Two" in result.markdown assert "title: Two" not in result.markdown assert "\n\n---\n\n" in result.markdown def test_resolve_includes_supports_comment_marker_selector_and_heading_shift(tmp_path: Path): partial = tmp_path / "partial.md" partial.write_text( """# Partial ## Keep Selected text. ## Drop Nope. """, encoding="utf-8", ) markdown = '' result = resolve_includes(markdown, base_dir=tmp_path) assert result.included_paths == [str(partial.resolve())] assert "### Keep" in result.markdown assert "Selected text" in result.markdown assert "Drop" not in result.markdown def test_resolve_includes_supports_brace_shorthand(tmp_path: Path): partial = tmp_path / "partial.md" partial.write_text("Included body.", encoding="utf-8") result = resolve_includes("Before\n\n{{include:partial.md}}\n\nAfter", base_dir=tmp_path) assert "Before" in result.markdown assert "Included body." in result.markdown assert "After" in result.markdown def test_resolve_includes_rejects_cycles(tmp_path: Path): one = tmp_path / "one.md" two = tmp_path / "two.md" one.write_text("{{include:two.md}}", encoding="utf-8") two.write_text("{{include:one.md}}", encoding="utf-8") with pytest.raises(IncludeError, match="Circular include"): resolve_includes(one.read_text(encoding="utf-8"), base_dir=tmp_path, current_path=one) def test_resolve_includes_rejects_paths_outside_base_dir(tmp_path: Path): outside = tmp_path.parent / "outside.md" outside.write_text("Nope", encoding="utf-8") with pytest.raises(IncludeError, match="escapes base directory"): resolve_includes("{{include:../outside.md}}", base_dir=tmp_path) def test_mkt_transform_writes_markdown(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("# One\n", encoding="utf-8") result = CliRunner().invoke( main, ["transform", str(source), "--heading-delta", "1", "--set", "status=draft"] ) assert result.exit_code == 0 assert "status: draft" in result.output assert "## One" in result.output def test_mkt_compose_writes_output_file(tmp_path: Path): one = tmp_path / "one.md" output = tmp_path / "out.md" one.write_text("# One\n", encoding="utf-8") result = CliRunner().invoke(main, ["compose", str(one), "--title", "Combined", "--output", str(output)]) assert result.exit_code == 0 assert result.output == "" assert output.read_text(encoding="utf-8").startswith("# Combined") def test_mkt_include_reports_errors(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("{{include:missing.md}}", encoding="utf-8") result = CliRunner().invoke(main, ["include", str(source)]) assert result.exit_code == 1 assert "Included file not found" in result.output