49b9bb6724
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Waiting to run
75 lines
3.6 KiB
Python
75 lines
3.6 KiB
Python
"""Run every story's ``main`` over the in-process (transport × era × variant) matrix."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import importlib
|
||
import inspect
|
||
from typing import Any
|
||
|
||
import anyio
|
||
import pytest
|
||
|
||
from tests.examples.conftest import MANIFEST, STORIES, STORIES_DIR, Hosted, Leg, story_cfg
|
||
|
||
pytestmark = pytest.mark.anyio
|
||
|
||
|
||
async def test_story(leg: Leg, cfg: dict[str, Any], hosted: Hosted, client_module: Any) -> None:
|
||
kwargs: dict[str, Any] = {"mode": leg.mode}
|
||
if cfg["needs_http"]:
|
||
kwargs["http"] = hosted.http
|
||
with anyio.fail_after(cfg["timeout_s"]):
|
||
if cfg["multi_connection"]:
|
||
await client_module.main(hosted.targets, **kwargs)
|
||
else:
|
||
await client_module.main(hosted.targets(), **kwargs)
|
||
|
||
|
||
def test_manifest_matches_filesystem() -> None:
|
||
"""Manifest [story.*] / [deferred] keys and on-disk story directories agree exactly."""
|
||
dirs = {d.name for d in STORIES_DIR.iterdir() if d.is_dir() and not d.name.startswith(("_", "."))}
|
||
runnable = {d for d in dirs if (STORIES_DIR / d / "client.py").exists()}
|
||
in_manifest = set(STORIES)
|
||
assert runnable == in_manifest, {"only_on_disk": runnable - in_manifest, "only_in_manifest": in_manifest - runnable}
|
||
# README-only stub dirs must be exactly the [deferred] table.
|
||
deferred_manifest = set(MANIFEST.get("deferred", {}))
|
||
assert dirs - runnable == deferred_manifest, {
|
||
"stub_dirs_missing_from_manifest": (dirs - runnable) - deferred_manifest,
|
||
"deferred_entries_missing_dir": deferred_manifest - (dirs - runnable),
|
||
}
|
||
assert runnable.isdisjoint(deferred_manifest), "deferred stories must not have a client.py"
|
||
|
||
|
||
_ERAS = {"dual", "modern", "legacy", "dual-in-body"}
|
||
_TRANSPORTS = {"in-memory", "http-asgi"}
|
||
_SERVER_EXPORTS = {"factory", "app"}
|
||
|
||
|
||
def test_manifest_schema_valid() -> None:
|
||
"""Declared manifest values are mutually consistent with the story files."""
|
||
for name in STORIES:
|
||
cfg = story_cfg(name)
|
||
assert "-" not in name, f"{name!r}: story directories must be underscored"
|
||
assert cfg["era"] in _ERAS, f"{name!r}: era={cfg['era']!r} not in {_ERAS}"
|
||
assert cfg["server_export"] in _SERVER_EXPORTS, f"{name!r}: server_export={cfg['server_export']!r}"
|
||
assert set(cfg["transports"]) <= _TRANSPORTS, f"{name!r}: transports={cfg['transports']!r}"
|
||
assert (STORIES_DIR / name / "__init__.py").exists(), f"{name!r}: missing __init__.py"
|
||
if cfg["server_export"] == "factory":
|
||
assert (STORIES_DIR / name / "server.py").exists(), f"{name!r}: missing server.py"
|
||
else:
|
||
assert "in-memory" not in cfg["transports"], f"{name!r}: server_export='app' cannot run in-memory"
|
||
if cfg["needs_http"]:
|
||
assert cfg["transports"] == ["http-asgi"], f"{name!r}: needs_http requires transports=['http-asgi']"
|
||
ll = STORIES_DIR / name / "server_lowlevel.py"
|
||
assert cfg["lowlevel"] == ll.exists(), f"{name!r}: lowlevel={cfg['lowlevel']} vs server_lowlevel.py on disk"
|
||
|
||
|
||
@pytest.mark.parametrize("name", sorted(STORIES))
|
||
def test_main_signature_matches_manifest(name: str) -> None:
|
||
"""``main``'s first parameter is ``target``/``targets`` per ``multi_connection``; ``http`` iff ``needs_http``."""
|
||
cfg = story_cfg(name)
|
||
params = list(inspect.signature(importlib.import_module(f"stories.{name}.client").main).parameters)
|
||
first = "targets" if cfg["multi_connection"] else "target"
|
||
assert params[0] == first, f"{name}: first param is {params[0]!r}, expected {first!r}"
|
||
assert ("http" in params) == cfg["needs_http"], f"{name}: 'http' param vs needs_http={cfg['needs_http']}"
|