Files
proxmox-api-simulator/tests/unit/test_contract_model.py
T
2026-07-13 00:27:07 +03:00

93 lines
2.8 KiB
Python

"""Determinism and validation checks for normalized contracts."""
import json
from datetime import UTC, datetime
from pathlib import Path
import pytest
from hypothesis import given
from hypothesis import strategies as st
from pydantic import ValidationError
from app.contracts.model import Snapshot, canonical_json
from app.contracts.normalize import normalize_snapshot
from app.contracts.source import ApiViewerParser
FIXTURE = Path(__file__).parents[1] / "fixtures" / "api-viewer" / "pve-9.2.3-version.json"
RETRIEVED_AT = datetime(2026, 7, 12, 20, 8, 59, tzinfo=UTC)
def make_snapshot() -> Snapshot:
raw = FIXTURE.read_bytes()
parsed = ApiViewerParser().parse(raw)
snapshot, _ = normalize_snapshot(
parsed, raw=raw, source_version="9.2.3", retrieved_at=RETRIEVED_AT
)
return snapshot
def test_normalization_is_deterministic_and_round_trips() -> None:
first = make_snapshot()
second = make_snapshot()
assert first.canonical_bytes() == second.canonical_bytes()
assert first.checksum() == second.checksum()
assert Snapshot.model_validate_json(first.canonical_bytes()) == first
assert first.paths[0].methods[0].checksum == second.paths[0].methods[0].checksum
def test_snapshot_validates_declared_counts() -> None:
data = make_snapshot().model_dump(mode="json")
data["method_count"] = 99
with pytest.raises(ValidationError, match="method_count"):
Snapshot.model_validate(data)
def test_unknown_schema_fields_are_retained() -> None:
raw = json.dumps(
[
{
"path": "/future",
"info": {
"GET": {
"name": "future",
"returns": {"type": "string", "futureKeyword": {"x": 1}},
}
},
}
]
).encode()
snapshot, _ = normalize_snapshot(
ApiViewerParser().parse(raw),
raw=raw,
source_version="test",
retrieved_at=RETRIEVED_AT,
)
assert snapshot.paths[0].methods[0].returns.extra["futureKeyword"] == {"x": 1}
def test_nullable_source_collections_normalize_as_empty() -> None:
raw = (
b'[{"path":"/nullable","info":{"GET":{"parameters":{"properties":null},'
b'"returns":{"type":"string","enum":null}}}}]'
)
snapshot, _ = normalize_snapshot(
ApiViewerParser().parse(raw),
raw=raw,
source_version="test",
retrieved_at=RETRIEVED_AT,
)
method = snapshot.paths[0].methods[0]
assert method.parameters == ()
assert method.returns.enum == ()
@given(st.dictionaries(st.text(min_size=1), st.integers(), max_size=10))
def test_canonical_json_is_independent_of_mapping_order(values: dict[str, int]) -> None:
reversed_values = dict(reversed(tuple(values.items())))
assert canonical_json(values) == canonical_json(reversed_values)