"""Tests for manifest model (FR-100).""" from __future__ import annotations from pathlib import Path import pytest import yaml from markidocx.manifest import ( FeatureLevel, ManifestError, load_manifest, ) @pytest.fixture def minimal_manifest_yaml(tmp_path: Path) -> Path: src = tmp_path / "doc.md" src.write_text("# Hello\n\nContent here.") manifest = tmp_path / "manifest.yaml" manifest.write_text( yaml.dump( { "project": {"name": "Test Doc", "feature_level": "level1", "family": "article"}, "sources": [{"path": "doc.md"}], "output": {"dir": "./dist"}, } ) ) return manifest @pytest.fixture def multi_source_manifest(tmp_path: Path) -> Path: for name in ("ch1.md", "ch2.md"): (tmp_path / name).write_text(f"# {name}\n\nContent.") manifest = tmp_path / "manifest.yaml" manifest.write_text( yaml.dump( { "project": {"name": "Book", "feature_level": "level1", "family": "book"}, "sources": [{"path": "ch1.md"}, {"path": "ch2.md"}], "output": {"dir": "./dist"}, "metadata": {"title": "My Book", "author": "Author", "date": "2026-01-01"}, } ) ) return manifest class TestLoadManifest: def test_loads_minimal_manifest(self, minimal_manifest_yaml: Path) -> None: m = load_manifest(minimal_manifest_yaml) assert m.project.name == "Test Doc" assert m.project.feature_level == FeatureLevel.LEVEL1 assert m.project.family == "article" assert len(m.sources) == 1 def test_loads_multi_source(self, multi_source_manifest: Path) -> None: m = load_manifest(multi_source_manifest) assert len(m.sources) == 2 assert m.metadata["title"] == "My Book" def test_missing_file_raises(self, tmp_path: Path) -> None: manifest = tmp_path / "manifest.yaml" manifest.write_text( yaml.dump( { "project": {"name": "X", "feature_level": "level1", "family": "article"}, "sources": [{"path": "missing.md"}], "output": {"dir": "./dist"}, } ) ) with pytest.raises(ManifestError, match="missing.md"): load_manifest(manifest) def test_missing_project_key_raises(self, tmp_path: Path) -> None: manifest = tmp_path / "manifest.yaml" manifest.write_text(yaml.dump({"sources": []})) with pytest.raises(ManifestError, match="project"): load_manifest(manifest) def test_invalid_feature_level_raises(self, tmp_path: Path) -> None: src = tmp_path / "doc.md" src.write_text("# x") manifest = tmp_path / "manifest.yaml" manifest.write_text( yaml.dump( { "project": {"name": "X", "feature_level": "level9", "family": "article"}, "sources": [{"path": "doc.md"}], "output": {"dir": "./dist"}, } ) ) with pytest.raises(ManifestError, match="feature_level"): load_manifest(manifest) def test_invalid_family_raises(self, tmp_path: Path) -> None: src = tmp_path / "doc.md" src.write_text("# x") manifest = tmp_path / "manifest.yaml" manifest.write_text( yaml.dump( { "project": {"name": "X", "feature_level": "level1", "family": "unknown"}, "sources": [{"path": "doc.md"}], "output": {"dir": "./dist"}, } ) ) with pytest.raises(ManifestError, match="family"): load_manifest(manifest) def test_sources_resolved_relative_to_manifest(self, minimal_manifest_yaml: Path) -> None: m = load_manifest(minimal_manifest_yaml) assert m.sources[0].path.is_absolute() assert m.sources[0].path.exists() def test_output_dir_resolved(self, minimal_manifest_yaml: Path) -> None: m = load_manifest(minimal_manifest_yaml) assert m.output_dir.is_absolute() def test_manifest_not_found_raises(self, tmp_path: Path) -> None: with pytest.raises(ManifestError, match="not found"): load_manifest(tmp_path / "nonexistent.yaml")