"""S2.5 MAF version-guard — a TEST-TIME tripwire + a two-sided install pin. The offline simulation depends on MAF's PRIVATE API (the ``_inner_get_response`` keyword-only signature + ``_build_response_stream`` construction — see ``conftest`` / ``simulation``'s scripted client). A major ``agent-framework-core`` bump could change those without notice. This is NOT a runtime guard (an un-wired ``src`` helper would be green-but-dead); it is a test-time tripwire against the installed distribution PLUS a two-sided pin in ``pyproject.toml`` (install-time). Critical: the guard reads the DISTRIBUTION version via ``importlib.metadata.version("agent-framework-core")`` (verified ``1.9.0``), NOT ``agent_framework.__version__`` (verified ``0.0.0`` placeholder — reading that would make the guard permanently RED / meaningless). Modelled on ``test_smoke``'s version-assert + ``test_portfolio``'s grep-pin. """ from __future__ import annotations import importlib.metadata from pathlib import Path import pytest _DIST = "agent-framework-core" #: F15 (2026-08-30): the floor lives in ONE place and the ``pyproject`` assert below DERIVES its #: expected pin string from it. Two literals for one fact drift (the kø-(p) rule), and a drifted #: floor is a guard that stops guarding without a local diff. _MIN_MAJOR = 1 _MIN_MINOR = 18 _MIN_VERSION = f"{_MIN_MAJOR}.{_MIN_MINOR}.0" def assert_supported_maf_version(version_str: str) -> None: """Raise ``ValueError`` when ``version_str`` is outside the supported ``>=1.18.0,<2`` range, naming the private-API premises to re-verify before a bump. Pure string parse — no import side effects, so a fake version can be passed directly (the RED-proof) without touching the real install.""" parts = version_str.split(".") major = int(parts[0]) minor = int(parts[1]) if len(parts) > 1 else 0 if not (major == _MIN_MAJOR and minor >= _MIN_MINOR): raise ValueError( f"{_DIST} {version_str} is outside the supported range >={_MIN_VERSION},<2. Before " "bumping, re-verify the private-API premises the offline sim relies on: the " "_inner_get_response keyword-only signature (messages/options/stream) and the " "_build_response_stream construction. Update the pin in pyproject.toml once confirmed." ) def test_installed_maf_version_is_supported() -> None: """T-2.5d: the real installed distribution (verified 1.18.0) passes the guard — read from ``importlib.metadata.version``, NOT ``agent_framework.__version__`` (a 0.0.0 placeholder).""" assert_supported_maf_version(importlib.metadata.version(_DIST)) def test_guard_rejects_future_major() -> None: """T-2.5d: a future major (``2.0.0``) trips the guard with the actionable upgrade message — RED-proof without touching the real install.""" with pytest.raises(ValueError, match="private-API premises"): assert_supported_maf_version("2.0.0") def test_guard_rejects_too_old_minor() -> None: """T-2.5d / F15 / F16: every PREVIOUS floor now trips the guard — the pin is two-sided, and this is the arm that makes each lift measurable rather than asserted. ``1.15.0`` (the ``agent-framework-orchestrations`` 1.1.1 requirement ``core>=1.15.0``) and ``1.16.0`` (F15's floor) are rejected too, and so is ``1.17.0``, even though ``agent-framework-foundry`` 1.13.0 and ``agent-framework-openai`` 1.14.3 only require ``core>=1.17.0``: a dependency's own floor is not our floor. 1.18.0 is the version whose private-API premises were actually re-verified (F16, docs/2026-09-12-f16-maf-1180.md).""" for stale in ("1.8.0", "1.9.0", "1.15.0", "1.16.0", "1.17.0"): with pytest.raises(ValueError, match="private-API premises"): assert_supported_maf_version(stale) def test_pyproject_pins_agent_framework_core_below_2() -> None: """T-2.5d: ``pyproject.toml`` pins ``agent-framework-core>=1.18.0,<2`` — the install-time half of the guard (the test above is the test-time half). The expected string is DERIVED from ``_MIN_VERSION`` so the two halves cannot drift apart.""" pyproject = (Path(__file__).resolve().parents[1] / "pyproject.toml").read_text(encoding="utf-8") assert f"agent-framework-core>={_MIN_VERSION},<2" in pyproject