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>
123 lines
4.3 KiB
Python
123 lines
4.3 KiB
Python
"""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 == []
|