"""Dual-side contract test (docs/11 §3.2): the same golden fixtures the TS side validates must validate against the generated pydantic models, and the same invalid fixtures must be rejected. Disagreement between sides fails CI.""" import importlib from pathlib import Path import pytest from pydantic import BaseModel, ValidationError ROOT = Path(__file__).resolve().parents[2] / "contracts" def model_for(name: str) -> type[BaseModel]: module = importlib.import_module(f"vpp_contracts.{name}") class_name = "".join(part.capitalize() for part in name.split("_")) return getattr(module, class_name) def cases(dirname: str) -> list[tuple[str, Path]]: out = [] for schema_dir in sorted((ROOT / dirname).iterdir()): for f in sorted(schema_dir.glob("*.json")): out.append((schema_dir.name, f)) return out @pytest.mark.parametrize("name,path", cases("fixtures"), ids=lambda p: str(p)) def test_golden_fixture_validates(name: str, path: Path) -> None: # JSON-mode validation — exactly how a skill service receives these objects. model_for(name).model_validate_json(path.read_text()) @pytest.mark.parametrize("name,path", cases("fixtures-invalid"), ids=lambda p: str(p)) def test_invalid_fixture_rejected(name: str, path: Path) -> None: with pytest.raises(ValidationError): model_for(name).model_validate_json(path.read_text())