feat(wiki): concept schema loader + concepts.yaml (F-12)

Adds wiki/concepts.yaml (5 curated seeds: discrete-conformal-map,
circle-packing, lobachevsky-function, discrete-yamabe-flow,
hyperideal-tetrahedron) and codex/wiki.py with full load_concepts()
implementation (Concept dataclass, YAML parser, graceful empty list).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-06-13 20:22:51 +02:00
parent c6eaf999a1
commit aee00515f4
6 changed files with 1489 additions and 0 deletions

122
tests/wiki/test_concepts.py Normal file
View File

@@ -0,0 +1,122 @@
"""Tests for load_concepts() — YAML parsing of wiki/concepts.yaml."""
from __future__ import annotations
import textwrap
from pathlib import Path
import pytest
from codex.wiki import Concept, load_concepts
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
FIXTURE_YAML = textwrap.dedent(
"""\
concepts:
- slug: discrete-conformal-map
title: Discrete Conformal Map
aliases: [discrete conformal equivalence, discrete uniformization]
emphasis: Fokus auf die variationelle Charakterisierung (BPS 2015).
- slug: circle-packing
title: Circle Packing
aliases: [Koebe-Andreev-Thurston, circle pattern]
- slug: lobachevsky-function
title: Lobachevsky Function
aliases: [Milnor Lobachevsky, Clausen function]
emphasis: Verwendung in hyperbolischen Volumenformeln (Springborn 2008).
- slug: discrete-yamabe-flow
title: Discrete Yamabe Flow
aliases: [discrete Ricci flow, Chow-Luo]
- slug: hyperideal-tetrahedron
title: Hyperideal Tetrahedron
aliases: [hyperbolic volume, Schläfli, Ushijima]
"""
)
@pytest.fixture
def concepts_yaml(tmp_path: Path) -> Path:
"""Write fixture YAML to a temp file and return the path."""
p = tmp_path / "concepts.yaml"
p.write_text(FIXTURE_YAML, encoding="utf-8")
return p
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
def test_load_concepts_returns_all_concepts(concepts_yaml: Path) -> None:
"""All five concept entries are returned."""
concepts = load_concepts(str(concepts_yaml))
assert len(concepts) == 5
def test_load_concepts_types(concepts_yaml: Path) -> None:
"""Every returned item is a Concept instance."""
concepts = load_concepts(str(concepts_yaml))
for c in concepts:
assert isinstance(c, Concept)
def test_load_concepts_first_slug_and_title(concepts_yaml: Path) -> None:
"""First concept has correct slug and title."""
concepts = load_concepts(str(concepts_yaml))
assert concepts[0].slug == "discrete-conformal-map"
assert concepts[0].title == "Discrete Conformal Map"
def test_load_concepts_aliases_are_lists(concepts_yaml: Path) -> None:
"""Aliases are parsed as a list of strings."""
concepts = load_concepts(str(concepts_yaml))
for c in concepts:
assert isinstance(c.aliases, list)
for alias in c.aliases:
assert isinstance(alias, str)
def test_load_concepts_first_has_two_aliases(concepts_yaml: Path) -> None:
"""First concept has exactly two aliases."""
concepts = load_concepts(str(concepts_yaml))
assert concepts[0].aliases == ["discrete conformal equivalence", "discrete uniformization"]
def test_load_concepts_emphasis_present(concepts_yaml: Path) -> None:
"""Concepts with emphasis field have it parsed correctly."""
concepts = load_concepts(str(concepts_yaml))
dcm = next(c for c in concepts if c.slug == "discrete-conformal-map")
assert dcm.emphasis is not None
assert "variationelle" in dcm.emphasis
def test_load_concepts_emphasis_absent_is_none(concepts_yaml: Path) -> None:
"""Concepts without emphasis field have emphasis=None."""
concepts = load_concepts(str(concepts_yaml))
cp = next(c for c in concepts if c.slug == "circle-packing")
assert cp.emphasis is None
def test_load_concepts_slugs_unique(concepts_yaml: Path) -> None:
"""All slugs are distinct."""
concepts = load_concepts(str(concepts_yaml))
slugs = [c.slug for c in concepts]
assert len(slugs) == len(set(slugs))
def test_load_concepts_unicode_in_aliases(concepts_yaml: Path) -> None:
"""Aliases with non-ASCII characters (Schläfli) are parsed without error."""
concepts = load_concepts(str(concepts_yaml))
ht = next(c for c in concepts if c.slug == "hyperideal-tetrahedron")
assert any("Schläfli" in a for a in ht.aliases)
def test_load_concepts_empty_yaml(tmp_path: Path) -> None:
"""An empty concepts list returns an empty list (no crash)."""
p = tmp_path / "concepts.yaml"
p.write_text("concepts: []\n", encoding="utf-8")
concepts = load_concepts(str(p))
assert concepts == []