60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
|
|
"""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
|