"""Tests for scripts/aggregate_directives.py. These tests exercise the script via subprocess to verify the contract: - reads v1.md files only (never meta.md) - emits clean bodies separated by '====' header - exits 1 on missing files - emits to stdout by default; -o writes to file The importable aggregate_directives() function tests cover the API surface used by the MCP server (scripts/mcp_server.py:aggregate_directives tool). """ from __future__ import annotations import subprocess import sys from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parent.parent SCRIPT = REPO_ROOT / "scripts" / "aggregate_directives.py" PRESET = REPO_ROOT / "conductor" / "directives" / "presets" / "current_baseline.md" def _run(args: list[str], cwd: Path | None = None) -> subprocess.CompletedProcess: return subprocess.run( [sys.executable, str(SCRIPT), *args], capture_output=True, text=True, encoding="utf-8", cwd=cwd or REPO_ROOT, ) def test_runs_against_current_baseline() -> None: result = _run([str(PRESET)]) assert result.returncode == 0, "script should succeed; stderr=" + result.stderr body = result.stdout assert "atomic_per_task_commits" in body assert "ban_dict_any" in body assert "knowledge_harvest_pattern" in body assert body.count("========================================") >= 51 * 2 def test_meta_md_pollution_fix() -> None: result = _run([str(PRESET)]) body = result.stdout assert "**Why this iteration:**" not in body assert "**Lifted:**" not in body def test_missing_file_exits_nonzero(tmp_path: Path) -> None: bad = tmp_path / "bad_preset.md" bad.write_text("- missing_thing: conductor/directives/missing_thing/v1.md\n", encoding="utf-8") result = _run([str(bad)]) assert result.returncode != 0 assert "missing_thing" in result.stderr def test_output_flag_writes_to_file(tmp_path: Path) -> None: out = tmp_path / "out.md" result = _run([str(PRESET), "-o", str(out)]) assert result.returncode == 0 assert out.exists() content = out.read_text(encoding="utf-8") assert "ban_dict_any" in content assert content.count("========================================") >= 51 * 2 def test_no_meta_md_reads() -> None: result = _run([str(PRESET)]) body = result.stdout assert "Provenance" not in body assert "## v1" not in body assert "# ban_dict_any" not in body DIRECTIVES_DIR = REPO_ROOT / "conductor" / "directives" def _iter_v1_files() -> list[Path]: return sorted(DIRECTIVES_DIR.glob("*/v1.md")) def test_every_v1_has_top_level_heading() -> None: v1_files = _iter_v1_files() assert len(v1_files) >= 66, "expected at least 66 directive v1.md files; found " + str(len(v1_files)) missing: list[str] = [] for path in v1_files: first_line = path.read_text(encoding="utf-8").splitlines()[0] if path.read_text(encoding="utf-8") else "" if not first_line.startswith("# "): missing.append(path.parent.name) assert not missing, "v1.md files missing a top-level '# ' rule-statement heading: " + ", ".join(missing) def test_v1_heading_is_not_meta_provenance_format() -> None: for path in _iter_v1_files(): first_line = path.read_text(encoding="utf-8").splitlines()[0] assert not first_line.strip().endswith("- v1"), path.parent.name + " uses the meta.md provenance heading format in v1.md body" def _import_aggregate_directives_module(): import importlib.util spec = importlib.util.spec_from_file_location("aggregate_directives_under_test", SCRIPT) if spec is None or spec.loader is None: raise RuntimeError("failed to build import spec for " + str(SCRIPT)) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod def test_importable_function_returns_clean_body() -> None: mod = _import_aggregate_directives_module() body = mod.aggregate_directives(str(PRESET)) assert "atomic_per_task_commits" in body assert "ban_dict_any" in body assert "knowledge_harvest_pattern" in body assert body.count("========================================") >= 51 * 2 def test_importable_function_no_meta_md_pollution() -> None: mod = _import_aggregate_directives_module() body = mod.aggregate_directives(str(PRESET)) assert "**Why this iteration:**" not in body assert "**Source:**" not in body assert "**Lifted:**" not in body assert "## v1" not in body assert "Provenance" not in body def test_importable_function_max_chars_truncates_with_suffix() -> None: mod = _import_aggregate_directives_module() body = mod.aggregate_directives(str(PRESET), max_chars=2000) assert len(body) > 2000, "truncated body must include suffix text exceeding the cap" assert "truncated" in body.lower() def test_importable_function_max_chars_zero_is_unlimited() -> None: mod = _import_aggregate_directives_module() full = mod.aggregate_directives(str(PRESET)) same = mod.aggregate_directives(str(PRESET), max_chars=0) assert same == full def test_importable_function_max_chars_above_size_is_unlimited() -> None: mod = _import_aggregate_directives_module() full = mod.aggregate_directives(str(PRESET)) capped = mod.aggregate_directives(str(PRESET), max_chars=len(full) + 5000) assert capped == full def test_importable_function_missing_preset_raises() -> None: mod = _import_aggregate_directives_module() bad = REPO_ROOT / "conductor" / "directives" / "presets" / "_nonexistent_preset_xyz.md" with pytest.raises(FileNotFoundError) as exc_info: mod.aggregate_directives(str(bad)) assert "preset file not found" in str(exc_info.value) def test_importable_function_missing_v1_in_preset_raises(tmp_path: Path) -> None: mod = _import_aggregate_directives_module() bad = tmp_path / "bad_preset.md" bad.write_text("- missing_v1_xyz: conductor/directives/missing_v1_xyz/v1.md\n", encoding="utf-8") with pytest.raises(FileNotFoundError) as exc_info: mod.aggregate_directives(str(bad)) assert "missing_v1_xyz" in str(exc_info.value) def test_importable_function_accepts_relative_preset_path() -> None: mod = _import_aggregate_directives_module() rel = "conductor/directives/presets/current_baseline.md" body = mod.aggregate_directives(rel, project_root=str(REPO_ROOT)) assert "ban_dict_any" in body