"""Shared fixture loading for the envelope tests. Fixture files are data (YAML). These helpers only load and merge them; all policy logic lives in the library under test. """ from __future__ import annotations import copy from pathlib import Path from typing import Any import pytest import yaml ROOT = Path(__file__).resolve().parent.parent ENVELOPES = ROOT / "envelopes" FIXTURES = Path(__file__).resolve().parent / "fixtures" DELETE = "~delete" def load_yaml(path: Path) -> Any: with path.open(encoding="utf-8") as handle: return yaml.safe_load(handle) def deep_merge(base: Any, patch: Any) -> Any: """Return base with patch merged in; dicts merge recursively, '~delete' removes a key.""" if not isinstance(base, dict) or not isinstance(patch, dict): return copy.deepcopy(patch) merged = copy.deepcopy(base) for key, value in patch.items(): if value == DELETE: merged.pop(key, None) elif key in merged: merged[key] = deep_merge(merged[key], value) else: merged[key] = copy.deepcopy(value) return merged def decision_cases() -> list[pytest.param]: """All decision cases from fixtures/decisions/*.yaml, with file context merged per case.""" params = [] for path in sorted((FIXTURES / "decisions").glob("*.yaml")): document = load_yaml(path) file_context = document.get("context", {}) default_request = document.get("defaults", {}).get("request", {}) for case in document["cases"]: params.append( pytest.param( deep_merge(default_request, case["request"]), {**file_context, **case.get("context", {})}, case["expect"], id=f"{path.stem}: {case['name']}", ) ) return params