generated from coulomb/repo-seed
test: RED phase — manifest tests (T02)
tests/test_manifest.py: full test suite for FR-100 manifest model (load, validate, missing sources, invalid feature_level/family, path resolution). Tests fail — module not yet implemented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
129
tests/test_manifest.py
Normal file
129
tests/test_manifest.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""Tests for manifest model (FR-100)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from markidocx.manifest import (
|
||||
FeatureLevel,
|
||||
Manifest,
|
||||
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")
|
||||
Reference in New Issue
Block a user