Skip to content

Commit f28d0cc

Browse files
committed
tests/interaction: drive connect parametrization from @requirement marks
pytest_generate_tests now reads each connect-taking test's stacked @requirement marks, looks them up in REQUIREMENTS, and parametrizes the connect fixture with compute_cells() output. The fixture body unpacks the (transport, spec_version) tuple and returns the bare factory. 411 connect-parametrized cells with byte-identical [transport] node ids; no behaviour change while SPEC_VERSIONS has a single entry. test_coverage.py gains 10 unit tests for compute_cells/cell_id covering intersection semantics, wildcard arm-exclusion / known-failure matching, TRANSPORT_SPEC_VERSIONS gating, and the transports-field-is-metadata-only guarantee. _requirements.py is at 100% line+branch coverage from test_coverage.py alone. Claude-Session: https://claude.ai/code/session_01NF95tmzF6RhVPktJdL6fK5
1 parent 696777d commit f28d0cc

2 files changed

Lines changed: 124 additions & 6 deletions

File tree

tests/interaction/conftest.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
"""Shared fixtures for the interaction suite."""
1+
"""Shared fixtures for the interaction suite.
2+
3+
The ``connect`` fixture is parametrized per-test from the ``@requirement`` marks the test
4+
carries: ``pytest_generate_tests`` looks up each cited requirement in the manifest and computes
5+
the (transport, spec_version) cells via :func:`compute_cells`, applying arm exclusions, version
6+
bounds, and known-failure xfails declaratively.
7+
"""
28

39
import pytest
410

511
from tests.interaction._connect import Connect, connect_in_memory, connect_over_sse, connect_over_streamable_http
12+
from tests.interaction._requirements import REQUIREMENTS, compute_cells
613

714
_FACTORIES: dict[str, Connect] = {
815
"in-memory": connect_in_memory,
@@ -11,13 +18,21 @@
1118
}
1219

1320

14-
@pytest.fixture(params=sorted(_FACTORIES))
21+
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
22+
"""Parametrize ``connect`` from the test's stacked ``@requirement`` marks."""
23+
if "connect" not in metafunc.fixturenames:
24+
return
25+
requirements = [REQUIREMENTS[mark.args[0]] for mark in metafunc.definition.iter_markers("requirement")]
26+
metafunc.parametrize("connect", compute_cells(requirements), indirect=True)
27+
28+
29+
@pytest.fixture
1530
def connect(request: pytest.FixtureRequest) -> Connect:
16-
"""The transport-parametrized connection factory: a test using it runs once per transport.
31+
"""The transport-parametrized connection factory: a test using it runs once per matrix cell.
1732
1833
Tests that are tied to one transport (the wire-recording tests, the bare-ClientSession tests,
1934
the transport-specific tests under transports/) do not use this fixture and connect directly.
2035
"""
21-
transport_name = request.param
22-
assert isinstance(transport_name, str)
23-
return _FACTORIES[transport_name]
36+
transport, _spec_version = request.param
37+
assert isinstance(transport, str)
38+
return _FACTORIES[transport]

tests/interaction/test_coverage.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616
import pytest
1717

1818
from tests.interaction._requirements import (
19+
CONNECTABLE_TRANSPORTS,
1920
REQUIREMENTS,
2021
ArmExclusion,
2122
KnownFailure,
2223
Requirement,
2324
SpecVersion,
25+
Transport,
26+
cell_id,
27+
compute_cells,
2428
covered_by,
2529
requirement,
2630
)
@@ -154,3 +158,102 @@ def test_requirement_with_empty_version_range_is_rejected() -> None:
154158
"""A requirement whose added_in is not strictly earlier than its removed_in fails at construction."""
155159
with pytest.raises(ValueError, match="must be earlier than"):
156160
Requirement(source="sdk", behavior="x", added_in="2025-11-25", removed_in="2025-11-25")
161+
162+
163+
def _req(
164+
*,
165+
added_in: SpecVersion | None = None,
166+
removed_in: SpecVersion | None = None,
167+
transports: tuple[Transport, ...] | None = None,
168+
arm_exclusions: tuple[ArmExclusion, ...] = (),
169+
known_failures: tuple[KnownFailure, ...] = (),
170+
) -> Requirement:
171+
"""Build a synthetic Requirement for compute_cells() unit tests."""
172+
return Requirement(
173+
source="sdk",
174+
behavior="x",
175+
added_in=added_in,
176+
removed_in=removed_in,
177+
transports=transports,
178+
arm_exclusions=arm_exclusions,
179+
known_failures=known_failures,
180+
)
181+
182+
183+
def test_compute_cells_with_no_requirements_yields_full_grid() -> None:
184+
"""An empty requirement list yields one cell per connectable transport at the single active spec version."""
185+
cells = compute_cells([])
186+
assert [c.id for c in cells] == ["in-memory", "sse", "streamable-http"]
187+
assert [c.values for c in cells] == [
188+
(("in-memory", "2025-11-25"),),
189+
(("sse", "2025-11-25"),),
190+
(("streamable-http", "2025-11-25"),),
191+
]
192+
193+
194+
def test_compute_cells_intersects_stacked_version_ranges() -> None:
195+
"""Stacked requirements intersect their [added_in, removed_in) windows: a cell survives only if all admit it."""
196+
cells = compute_cells(
197+
[_req(removed_in="2026-07-28"), _req(added_in="2025-11-25")],
198+
spec_versions=("2025-11-25", "2026-07-28"),
199+
)
200+
assert [c.id for c in cells] == ["in-memory-2025-11-25", "sse-2025-11-25", "streamable-http-2025-11-25"]
201+
202+
203+
def test_compute_cells_drops_era_locked_transport_outside_its_versions() -> None:
204+
"""A transport listed in TRANSPORT_SPEC_VERSIONS only appears for the spec versions it serves."""
205+
cells = compute_cells([], spec_versions=("2025-11-25", "2026-07-28"))
206+
assert [c.id for c in cells] == [
207+
"in-memory-2025-11-25",
208+
"sse-2025-11-25",
209+
"streamable-http-2025-11-25",
210+
"in-memory-2026-07-28",
211+
"streamable-http-2026-07-28",
212+
]
213+
214+
215+
def test_compute_cells_honours_arm_exclusion_from_any_stacked_requirement() -> None:
216+
"""An arm exclusion on any stacked requirement drops the matching cell even when other requirements have none."""
217+
cells = compute_cells([_req(), _req(arm_exclusions=(ArmExclusion(reason="requires-session", transport="sse"),))])
218+
assert [c.id for c in cells] == ["in-memory", "streamable-http"]
219+
220+
221+
def test_compute_cells_wildcard_arm_exclusion_drops_every_cell() -> None:
222+
"""An arm exclusion with both transport and spec_version unset matches every cell, leaving none."""
223+
cells = compute_cells([_req(arm_exclusions=(ArmExclusion(reason="requires-session"),))])
224+
assert cells == []
225+
226+
227+
def test_compute_cells_marks_known_failure_as_strict_xfail() -> None:
228+
"""A known failure attaches a strict xfail mark to exactly the matching cell and leaves others unmarked."""
229+
cells = compute_cells([_req(known_failures=(KnownFailure(note="broken on sse", transport="sse"),))])
230+
by_id = {c.id: c for c in cells}
231+
assert set(by_id) == {"in-memory", "sse", "streamable-http"}
232+
assert by_id["sse"].marks[0].name == "xfail"
233+
assert by_id["sse"].marks[0].kwargs == {"reason": "broken on sse", "strict": True}
234+
assert by_id["in-memory"].marks == ()
235+
assert by_id["streamable-http"].marks == ()
236+
237+
238+
def test_compute_cells_wildcard_known_failure_marks_every_cell() -> None:
239+
"""A known failure with both transport and spec_version unset marks every emitted cell as strict xfail."""
240+
cells = compute_cells([_req(known_failures=(KnownFailure(note="all broken"),))])
241+
assert len(cells) == 3
242+
assert all(c.marks[0].name == "xfail" for c in cells)
243+
assert all(c.marks[0].kwargs == {"reason": "all broken", "strict": True} for c in cells)
244+
245+
246+
def test_compute_cells_ignores_transports_field() -> None:
247+
"""Requirement.transports is descriptive metadata only and does not filter the cell grid."""
248+
cells = compute_cells([_req(transports=("stdio",))])
249+
assert [c.id for c in cells] == list(CONNECTABLE_TRANSPORTS)
250+
251+
252+
def test_cell_id_omits_version_when_single_spec_version() -> None:
253+
"""With one active spec version the cell id is just the transport name, keeping today's node ids byte-identical."""
254+
assert cell_id("sse", "2025-11-25") == "sse"
255+
256+
257+
def test_cell_id_appends_version_when_multiple_spec_versions() -> None:
258+
"""With more than one active spec version the cell id gains a -<version> suffix."""
259+
assert cell_id("sse", "2025-11-25", spec_versions=("2025-11-25", "2026-07-28")) == "sse-2025-11-25"

0 commit comments

Comments
 (0)