"""docs/LEVELS.md must document exactly the fields the models have. The doc is the content author's contract; the Pydantic models are its executable form. This test parses every ``### `` section's *Field* table (the first table whose header row starts with ``Field``, so the HazardSpec placement table is not mistaken for a field list) and requires the back-quoted field names to equal ``model_fields`` — no undocumented field, no documented ghost. A planted-drift case proves the extraction can fail. """ from __future__ import annotations from pathlib import Path import pytest from pydantic import BaseModel from undercooked.sim.level import ( HazardSpec, LevelDef, OrdersSpec, StationLimits, TileSpec, ) from undercooked.sim.recipe import Ingredient, Recipe, RecipeBook, Step pytestmark = pytest.mark.unit DOC_PATH = Path(__file__).resolve().parents[2] / "docs" / "LEVELS.md" MODELS: tuple[type[BaseModel], ...] = ( Ingredient, Step, Recipe, RecipeBook, TileSpec, OrdersSpec, HazardSpec, StationLimits, LevelDef, ) def _section(lines: list[str], model_name: str) -> list[str]: """Lines of the `### model_name` section, up to the next heading.""" heading = f"### {model_name}" collected: list[str] = [] in_section = False for line in lines: if line.startswith("#"): if in_section: break in_section = line.strip() == heading continue if in_section: collected.append(line) return collected def _table_fields(section: list[str]) -> set[str]: """Back-quoted first-column entries of the section's `Field` table.""" start = None for index, line in enumerate(section): stripped = line.strip() if stripped.startswith("|") and stripped.strip("|").split("|")[0].strip() == "Field": start = index break if start is None: return set() fields: set[str] = set() for line in section[start + 2 :]: # skip header + |---| separator stripped = line.strip() if not stripped.startswith("|"): break first = stripped.strip("|").split("|")[0].strip() if len(first) > 2 and first.startswith("`") and first.endswith("`"): fields.add(first[1:-1]) return fields @pytest.fixture(scope="module") def doc_lines() -> list[str]: assert DOC_PATH.is_file(), f"{DOC_PATH} is missing" return DOC_PATH.read_text(encoding="utf-8").splitlines() @pytest.mark.parametrize("model", MODELS, ids=lambda model: model.__name__) def test_every_model_has_an_exact_field_table(doc_lines: list[str], model: type[BaseModel]) -> None: documented = _table_fields(_section(doc_lines, model.__name__)) assert documented, f"docs/LEVELS.md has no Field table for {model.__name__}" assert documented == set(model.model_fields), ( f"docs/LEVELS.md vs {model.__name__}: " f"undocumented={sorted(set(model.model_fields) - documented)}, " f"not-in-model={sorted(documented - set(model.model_fields))}" ) def test_the_comparison_catches_planted_drift(doc_lines: list[str]) -> None: planted = [ line.replace("| `penalty` |", "| `reward` |") for line in doc_lines ] documented = _table_fields(_section(planted, "OrdersSpec")) assert "reward" in documented assert documented != set(OrdersSpec.model_fields)