Files
dashboards/tests/conftest.py
T
ducoterra b389bb4a94 Wire the live-test gate and pin the toolchain to 3.14
README advertised `uv run pytest -m live --live`, but there was no --live
option and no `live` marker registered — the command errored out, so the
one workflow that catches an upstream renaming a series did not exist.

- conftest adds --live and skips `live`-marked tests without it, so the
  default run stays hermetic (258 passed, 6 skipped, no network).
- tests/test_live_fleet.py drives the real code paths (collectors.*,
  NutMonitor.collect) against the fleet, both engines, and upsd. Each
  skips when its source is unset, and none of them may write — a battery
  test on live hardware is not a unit test. The host test polls twice
  because a rate has no second point on the first scrape.
- The agent's real-/proc test stays in the default run and is explicitly
  not `live`: it touches only the box running pytest.
- dev-tunnels.sh grows DEV_FORWARDS for services already listening
  remotely, which is how upsd gets reachable without opening 3493;
  pidfiles are now host+port so one host can carry two tunnels.
- requires-python, ruff and pyright were still on 3.13 while the venv and
  Containerfile are 3.14, so the lock resolved for a stack nobody deploys.

LLM-Generated: true
2026-10-16 18:55:02 -04:00

95 lines
3.0 KiB
Python

"""Shared fixtures: real captures for shape, fakes for behaviour.
Anything that must match an upstream (an agent snapshot, an engine's /metrics text, the NUT
wire protocol) is asserted against a verbatim capture in tests/fixtures/. Everything that
needs a *failure* — a dead host, a refused command, a counter reset — is a fake here.
The `live` marker is the bridge to real hardware: those tests dial the fleet, the engines,
or upsd, so they SKIP unless you pass `--live` and have set the env vars they read (see
tests/test_live_fleet.py). That keeps `uv run pytest` hermetic and offline while still
letting `uv run pytest -m live --live` catch an upstream that renamed something.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import Any
import pytest
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--live",
action="store_true",
default=False,
help="run tests marked `live` against the real fleet/engines/upsd (they skip otherwise)",
)
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
if config.getoption("--live"):
return
skip = pytest.mark.skip(reason="needs --live and a reachable target (see tests/test_live_fleet.py)")
for item in items:
if "live" in item.keywords:
item.add_marker(skip)
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
sys.path.insert(0, str(SRC))
from dashboards.rates import RateEstimator # noqa: E402
from dashboards.store import SampleStore # noqa: E402
FIXTURES = Path(__file__).resolve().parent / "fixtures"
@pytest.fixture
def db(tmp_path: Path) -> Any:
store = SampleStore(tmp_path / "t.sqlite3")
try:
yield store
finally:
store.close()
@pytest.fixture
def store(db: Any) -> Any:
"""Alias: some tests read better with `store`, others with `db`. Same object."""
return db
@pytest.fixture
def agent_snapshot() -> dict[str, Any]:
"""A real driveripper snapshot (Threadripper + R9700, 48 logical CPUs)."""
return json.loads((FIXTURES / "agent_driveripper.json").read_text())
@pytest.fixture
def agent_snapshots() -> dict[str, dict[str, Any]]:
"""Every captured host, keyed by name — fleet code paths need >1 host to mean anything."""
out: dict[str, dict[str, Any]] = {}
for path in sorted(FIXTURES.glob("agent_*.json")):
out[path.stem.removeprefix("agent_")] = json.loads(path.read_text())
return out
@pytest.fixture
def metrics_text() -> Any:
"""metrics_text('gufo') / metrics_text('vllm') -> the captured exposition verbatim."""
def load(engine: str) -> str:
path = FIXTURES / f"metrics_{engine}.txt"
if not path.exists():
pytest.skip(f"missing fixture {path.name} — run scripts/capture-fixtures.sh --with-llm")
return path.read_text()
return load
@pytest.fixture
def rates() -> RateEstimator:
return RateEstimator(span=10.0)