Files
llm-wiki/harness/tests/test_moc_indexer.py
T

234 lines
12 KiB
Python

from __future__ import annotations
from pathlib import Path
import hashlib
import json
import sys
import tempfile
import unittest
RUNTIME = Path(__file__).resolve().parents[1] / "runtime"
sys.path.insert(0, str(RUNTIME))
import moc_indexer # noqa: E402
class MocIndexerTest(unittest.TestCase):
def _cutover(self, root: Path, paths: list[Path]) -> dict[Path, Path]:
mapping: dict[Path, Path] = {}
entries = []
for legacy in paths:
relative = legacy.relative_to(root).as_posix()
category = legacy.parent.name
canonical_rel = f"vault/10-projects/proj/{category}/{legacy.name}"
canonical = root / canonical_rel
canonical.parent.mkdir(parents=True, exist_ok=True)
content = legacy.read_bytes()
canonical.write_bytes(content)
legacy.write_text(f"---\ncanonical_path: {canonical_rel}\n---\n", encoding="utf-8")
entries.append({
"legacy_path": relative,
"canonical_path": canonical_rel,
"sha256": hashlib.sha256(content).hexdigest(),
})
mapping[legacy] = canonical
manifest = root / "harness/source/vault-layout.json"
manifest.parent.mkdir(parents=True)
manifest.write_text(json.dumps({
"schema_version": "vault-layout/v1",
"mode": "canonical",
"migration_manifest": {"schema_version": "vault-migration/v1", "entries": entries},
}), encoding="utf-8")
return mapping
def test_canonical_edges_sort_children_and_preserve_manual_cluster(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
project = root / "raw/project-notes/proj.md"
project.parent.mkdir(parents=True)
project.write_text("# P\n## Cluster / 묶음\n- [[raw/errors/manual]]\n", encoding="utf-8")
branch_dir = root / "raw/branch-notes"
branch_dir.mkdir(parents=True)
for slug in ("feature-zeta-child-contract-runtime", "feature-alpha-child-contract-runtime"):
(branch_dir / f"{slug}.md").write_text(
f"---\nid: {slug}\nproject: proj\nparent_branch:\ncontract_packet: 1\n---\n# B\n",
encoding="utf-8",
)
updates, stats = moc_indexer.build_updates(root)
self.assertEqual(stats["structured_children"], 2)
project.write_text(updates[project], encoding="utf-8")
text = project.read_text(encoding="utf-8")
self.assertLess(text.index("feature-alpha"), text.index("feature-zeta"))
self.assertIn("[[raw/errors/manual]]", text)
self.assertEqual(moc_indexer.build_updates(root)[0], {})
def test_relation_config_generates_multiple_reverse_views_and_multiline_daily_edges(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
project = root / "raw/project-notes/proj.md"
project.parent.mkdir(parents=True)
project.write_text("# Project\n## Cluster / 묶음\nproject manual\n", encoding="utf-8")
branch = root / "raw/branch-notes/feature-sample-parent-contract.md"
branch.parent.mkdir(parents=True)
branch.write_text(
"---\nproject: proj\nparent_branch:\ncontract_packet: 1\n---\n# Branch\n## Cluster / 묶음\nbranch manual\n",
encoding="utf-8",
)
official = root / "raw/official-docs/source.md"
official.parent.mkdir(parents=True)
official.write_text("---\nrelated_branches: [feature-sample-parent-contract]\n---\n# Source\n", encoding="utf-8")
error = root / "raw/errors/error.md"
error.parent.mkdir(parents=True)
error.write_text("---\nrelated_branches: [feature-sample-parent-contract]\n---\n# Error\n", encoding="utf-8")
daily = root / "raw/daily-notes/2026-07-20.md"
daily.parent.mkdir(parents=True)
daily.write_text("---\nbranches: [\n feature-sample-parent-contract\n]\n---\n# Daily\n", encoding="utf-8")
updates, stats = moc_indexer.build_updates(root)
for path, text in updates.items():
path.write_text(text, encoding="utf-8")
project_text = project.read_text(encoding="utf-8")
branch_text = branch.read_text(encoding="utf-8")
self.assertIn("<!-- GENERATED: branches:start -->", project_text)
self.assertIn("[[raw/branch-notes/feature-sample-parent-contract]]", project_text)
self.assertIn("<!-- GENERATED: sources:start -->", branch_text)
self.assertIn("[[raw/official-docs/source]]", branch_text)
self.assertIn("<!-- GENERATED: errors:start -->", branch_text)
self.assertIn("[[raw/errors/error]]", branch_text)
self.assertIn("<!-- GENERATED: daily-notes:start -->", branch_text)
self.assertIn("[[raw/daily-notes/2026-07-20]]", branch_text)
self.assertIn("project manual", project_text)
self.assertIn("branch manual", branch_text)
self.assertEqual(stats["canonical_edges"], 4)
self.assertEqual(moc_indexer.build_updates(root)[0], {})
def test_canonical_mode_indexes_manifest_paths_and_excludes_stubs(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
project = root / "raw/project-notes/proj.md"
project.parent.mkdir(parents=True)
project.write_text("# P\n## Cluster / 묶음\n", encoding="utf-8")
branch = root / "raw/branch-notes/feature-sample-contract.md"
branch.parent.mkdir(parents=True)
branch.write_text(
"---\nproject: proj\nparent_branch:\ncontract_packet: 1\n---\n# B\n",
encoding="utf-8",
)
mapping = self._cutover(root, [project, branch])
updates, stats = moc_indexer.build_updates(root)
canonical_project = mapping[project]
self.assertEqual(set(updates), {canonical_project})
self.assertIn(
"[[raw/branch-notes/feature-sample-contract]]",
updates[canonical_project],
)
self.assertEqual(stats["mode"], "canonical")
self.assertEqual(stats["namespace"], "canonical")
canonical_project.write_text(updates[canonical_project], encoding="utf-8")
self.assertEqual(moc_indexer.build_updates(root)[0], {})
def test_invalid_or_duplicate_generated_markers_fail_closed(self) -> None:
with self.assertRaises(moc_indexer.MocError) as raised:
moc_indexer._replace_or_insert_marker(
"<!-- GENERATED: sources:start -->\n<!-- GENERATED: sources:start -->\n<!-- GENERATED: sources:end -->\n",
set(),
"sources",
"hub.md",
)
self.assertEqual(raised.exception.code, "INVALID_GENERATED_BLOCK")
def test_all_named_raw_relations_support_project_and_multi_parent_views(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
project = root / "raw/project-notes/proj.md"
project.parent.mkdir(parents=True)
project.write_text("# Project\n## Cluster / 묶음\nmanual project\n", encoding="utf-8")
branches = []
for slug in ("feature-parent-one", "feature-parent-two"):
branch = root / f"raw/branch-notes/{slug}.md"
branch.parent.mkdir(parents=True, exist_ok=True)
branch.write_text("# Branch\n## Cluster / 묶음\nmanual branch\n", encoding="utf-8")
branches.append(branch)
fixtures = [
("raw/official-docs/source.md", "related_branches: [feature-parent-one, feature-parent-two]\nrelated_projects: [proj]"),
("raw/interviews/question.md", "related_branches: [feature-parent-one]\nrelated_projects: [proj]"),
("raw/lectures/course.md", "related_branches: [feature-parent-one]\nrelated_projects: [proj]"),
("raw/job-postings/job.md", "related_branches: [feature-parent-one]\nrelated_projects: [proj]"),
("raw/blog-topics/topic.md", "related_branches: [feature-parent-one]\nrelated_projects: [proj]"),
]
for relative, fields in fixtures:
path = root / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(f"---\n{fields}\n---\n# Child\n", encoding="utf-8")
updates, stats = moc_indexer.build_updates(root)
for path, text in updates.items():
path.write_text(text, encoding="utf-8")
project_text = project.read_text(encoding="utf-8")
first_text = branches[0].read_text(encoding="utf-8")
second_text = branches[1].read_text(encoding="utf-8")
for marker in ("sources", "interviews", "lectures", "job-postings", "blog-topics"):
self.assertIn(f"<!-- GENERATED: {marker}:start -->", project_text)
self.assertIn(f"<!-- GENERATED: {marker}:start -->", first_text)
self.assertIn("[[raw/official-docs/source]]", second_text)
self.assertIn("manual project", project_text)
self.assertGreaterEqual(stats["canonical_edges"], 11)
self.assertEqual(moc_indexer.build_updates(root)[0], {})
def test_deleted_last_child_clears_generated_view_and_preserves_manual_content(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
branch = root / "raw/branch-notes/feature-parent.md"
branch.parent.mkdir(parents=True)
branch.write_text("# Branch\n## Cluster / 묶음\nmanual\n", encoding="utf-8")
source = root / "raw/official-docs/source.md"
source.parent.mkdir(parents=True)
source.write_text("---\nrelated_branches: [feature-parent]\n---\n# Source\n", encoding="utf-8")
for path, text in moc_indexer.build_updates(root)[0].items():
path.write_text(text, encoding="utf-8")
source.unlink()
updates, _stats = moc_indexer.build_updates(root)
rendered = updates[branch]
self.assertIn("<!-- GENERATED: sources:start -->\n<!-- GENERATED: sources:end -->", rendered)
self.assertNotIn("raw/official-docs/source", rendered)
self.assertIn("manual", rendered)
def test_canonical_derived_relations_support_nested_and_multiple_parents(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
concept = root / "wiki/concepts/concept.md"
project = root / "wiki/projects/proj/design.md"
concept.parent.mkdir(parents=True)
project.parent.mkdir(parents=True)
concept.write_text("# Concept\nmanual concept\n", encoding="utf-8")
project.write_text("# Project\nmanual project\n", encoding="utf-8")
children = [
("wiki/interview/interview.md", "derived-interviews"),
("wiki/portfolio/portfolio.md", "derived-portfolios"),
("wiki/blog/blog.md", "derived-blogs"),
]
for relative, _marker in children:
child = root / relative
child.parent.mkdir(parents=True, exist_ok=True)
child.write_text(
"---\ncanonical_sources: [wiki/concepts/concept, wiki/projects/proj/design]\n---\n# Derived\n",
encoding="utf-8",
)
updates, stats = moc_indexer.build_updates(root)
concept_text = updates[concept]
project_text = updates[project]
for relative, marker in children:
link = Path(relative).with_suffix("").as_posix()
self.assertIn(f"<!-- GENERATED: {marker}:start -->", concept_text)
self.assertIn(f"[[{link}]]", concept_text)
self.assertIn(f"[[{link}]]", project_text)
self.assertEqual(stats["canonical_edges"], 6)
if __name__ == "__main__":
unittest.main()