Private
Public Access
0
0
Files
manual_slop/tests/test_aggregate_directives_presets.py
T

256 lines
7.9 KiB
Python

"""Tests for aggregate_directives preset inheritance and non-directive context."""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
# Add scripts/ to sys.path so we can import aggregate_directives
SCRIPTS_DIR = Path(__file__).resolve().parent.parent / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
from aggregate_directives import parse_preset_sections, resolve_inheritance, aggregate_directives
def _preset_dir(root: Path) -> Path:
"""Return the preset directory under root, creating it if needed."""
d = root / "conductor" / "directives" / "presets"
d.mkdir(parents=True, exist_ok=True)
return d
class TestParsePresetSections:
"""Test the new section-based parser that extracts Inherits, Directives, Non-directive context."""
def test_parse_returns_three_sections(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Inherits
baseline
## Directives to warm
Read each file below before any action.
- foo: conductor/directives/foo/v1.md
- bar: conductor/directives/bar/v1.md
## Non-directive context
Read each file below as full documents.
- project rules: AGENTS.md
## Notes
test
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == "baseline"
assert len(sections.directives) == 2
assert sections.directives[0] == ("foo", Path("conductor/directives/foo/v1.md"))
assert sections.directives[1] == ("bar", Path("conductor/directives/bar/v1.md"))
assert len(sections.non_directive_context) == 1
assert sections.non_directive_context[0] == ("project rules", Path("AGENTS.md"))
def test_parse_preset_without_inherits(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- foo: conductor/directives/foo/v1.md
## Non-directive context
- doc: AGENTS.md
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == ""
assert len(sections.directives) == 1
assert len(sections.non_directive_context) == 1
def test_parse_preset_without_non_directive_context(self, tmp_path: Path):
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- foo: conductor/directives/foo/v1.md
""", encoding="utf-8")
sections = parse_preset_sections(preset)
assert sections.inherits == ""
assert len(sections.directives) == 1
assert sections.non_directive_context == []
class TestResolveInheritance:
"""Test that resolve_inheritance merges parent directives into child, dedup on name."""
def test_child_directives_plus_parent_directives(self, tmp_path: Path):
pdir = _preset_dir(tmp_path)
(pdir / "parent.md").write_text("""# Preset: parent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
(pdir / "child.md").write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- beta: conductor/directives/beta/v1.md
- gamma: conductor/directives/gamma/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(pdir / "child.md", root=tmp_path)
names = [n for n, _ in merged.directives]
assert set(names) == {"alpha", "beta", "gamma"}
assert names.count("beta") == 1
assert len(merged.non_directive_context) == 0
def test_child_wins_on_directive_conflict(self, tmp_path: Path):
pdir = _preset_dir(tmp_path)
(pdir / "parent.md").write_text("""# Preset: parent
## Directives to warm
- shared: conductor/directives/shared_parent/v1.md
""", encoding="utf-8")
(pdir / "child.md").write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- shared: conductor/directives/shared_child/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(pdir / "child.md", root=tmp_path)
paths = [p for _, p in merged.directives]
assert Path("conductor/directives/shared_child/v1.md") in paths
assert Path("conductor/directives/shared_parent/v1.md") not in paths
def test_non_directive_context_merges_dedup_on_path(self, tmp_path: Path):
pdir = _preset_dir(tmp_path)
(pdir / "parent.md").write_text("""# Preset: parent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
## Non-directive context
- rules: AGENTS.md
- product: conductor/product.md
""", encoding="utf-8")
(pdir / "child.md").write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- beta: conductor/directives/beta/v1.md
## Non-directive context
- rules: AGENTS.md
- tech: conductor/tech-stack.md
""", encoding="utf-8")
merged = resolve_inheritance(pdir / "child.md", root=tmp_path)
paths = [p for _, p in merged.non_directive_context]
assert Path("AGENTS.md") in paths
assert Path("conductor/product.md") in paths
assert Path("conductor/tech-stack.md") in paths
assert paths.count(Path("AGENTS.md")) == 1
def test_three_level_inheritance(self, tmp_path: Path):
pdir = _preset_dir(tmp_path)
(pdir / "grandparent.md").write_text("""# Preset: grandparent
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
""", encoding="utf-8")
(pdir / "parent.md").write_text("""# Preset: parent
## Inherits
grandparent
## Directives to warm
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
(pdir / "child.md").write_text("""# Preset: child
## Inherits
parent
## Directives to warm
- gamma: conductor/directives/gamma/v1.md
""", encoding="utf-8")
merged = resolve_inheritance(pdir / "child.md", root=tmp_path)
names = [n for n, _ in merged.directives]
assert "alpha" in names
assert "beta" in names
assert "gamma" in names
def test_inheritance_loop_detected(self, tmp_path: Path):
pdir = _preset_dir(tmp_path)
(pdir / "a.md").write_text("""# Preset: a
## Inherits
b
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
""", encoding="utf-8")
(pdir / "b.md").write_text("""# Preset: b
## Inherits
a
## Directives to warm
- beta: conductor/directives/beta/v1.md
""", encoding="utf-8")
with pytest.raises(ValueError, match="inheritance loop"):
resolve_inheritance(pdir / "a.md", root=tmp_path)
class TestAggregateWithNonDirectiveContext:
"""Integration: aggregate_directives returns non-directive files in a separate section."""
def test_aggregate_includes_non_directive_section(self, tmp_path: Path):
directives_dir = tmp_path / "conductor" / "directives" / "alpha"
directives_dir.mkdir(parents=True)
(directives_dir / "v1.md").write_text("# Alpha rule\n\nDo the thing.\n", encoding="utf-8")
context_file = tmp_path / "AGENTS.md"
context_file.write_text("# Project Rules\n\nRule 1.\n", encoding="utf-8")
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- alpha: conductor/directives/alpha/v1.md
## Non-directive context
- project rules: AGENTS.md
""", encoding="utf-8")
rendered = aggregate_directives(str(preset), project_root=str(tmp_path))
assert "alpha" in rendered
assert "Alpha rule" in rendered
assert "NON-DIRECTIVE CONTEXT" in rendered
assert "Project Rules" in rendered
assert rendered.index("alpha") < rendered.index("NON-DIRECTIVE CONTEXT")
def test_aggregate_non_directive_section_after_directives(self, tmp_path: Path):
directives_dir = tmp_path / "conductor" / "directives" / "beta"
directives_dir.mkdir(parents=True)
(directives_dir / "v1.md").write_text("# Beta rule\n\nDo beta.\n", encoding="utf-8")
context_file = tmp_path / "conductor" / "product.md"
context_file.parent.mkdir(parents=True, exist_ok=True)
context_file.write_text("# Product\n\nVision.\n", encoding="utf-8")
preset = tmp_path / "test_preset.md"
preset.write_text("""# Preset: test
## Directives to warm
- beta: conductor/directives/beta/v1.md
## Non-directive context
- product: conductor/product.md
""", encoding="utf-8")
rendered = aggregate_directives(str(preset), project_root=str(tmp_path))
assert "Beta rule" in rendered
assert "Product" in rendered
assert rendered.index("Beta rule") < rendered.index("NON-DIRECTIVE CONTEXT")
assert "product" in rendered.lower() or "Product" in rendered