state-hub/tests/test_ops_run_projection.py
tegwick 5927591be8
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 1m0s
Prepare State Hub retirement baseline
2026-08-09 16:19:53 +02:00

119 lines
4.4 KiB
Python

from datetime import datetime, timedelta, timezone
import pytest
from api.config import settings
from api.services import ops_run_projection as projection
@pytest.fixture(autouse=True)
def reset_projection_cache() -> None:
projection.reset_ops_run_projection_cache()
@pytest.mark.asyncio
async def test_ops_run_projection_counts_and_contract(monkeypatch) -> None:
now = datetime.now(timezone.utc)
payload = {
"counts": {"open": 2, "claimed": 1, "succeeded": 9, "failed": 2},
"items": [
{
"id": "run-open",
"activity_definition_id": "definition-1",
"target_repo": "state-hub",
"state": "open",
"created_at": (now - timedelta(hours=2)).isoformat(),
"updated_at": (now - timedelta(hours=2)).isoformat(),
"result": {},
},
{
"id": "run-claimed",
"activity_definition_id": "definition-2",
"target_repo": "activity-core",
"state": "claimed",
"claim_owner": "rein-aharness@railiance01",
"lease_until": (now + timedelta(minutes=10)).isoformat(),
"attempt": 1,
"created_at": (now - timedelta(minutes=10)).isoformat(),
"updated_at": now.isoformat(),
"result": {},
},
{
"id": "run-failed",
"activity_definition_id": "definition-3",
"target_repo": "binky-control",
"state": "failed",
"created_at": (now - timedelta(hours=3)).isoformat(),
"updated_at": (now - timedelta(hours=1)).isoformat(),
"result": {"error": "executor timeout"},
},
],
}
async def fake_fetch():
return payload
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
result = await projection.get_ops_run_projection(refresh=True)
assert result.available is True
assert result.open == 2
assert result.claimed == 1
assert result.failed_24h == 1
assert result.stuck_open_or_claimed == 1
assert {item.id for item in result.items} == {"run-open", "run-claimed", "run-failed"}
failed = next(item for item in result.items if item.id == "run-failed")
assert failed.last_error == "executor timeout"
claimed = next(item for item in result.items if item.id == "run-claimed")
assert claimed.lease["owner"] == "rein-aharness@railiance01"
@pytest.mark.asyncio
async def test_ops_run_projection_serves_stale_cache_on_refresh_failure(monkeypatch) -> None:
async def initial_fetch():
return {"counts": {"open": 1}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", initial_fetch)
initial = await projection.get_ops_run_projection(refresh=True)
assert initial.available is True
async def failed_fetch():
raise RuntimeError("upstream down")
monkeypatch.setattr(projection, "_fetch_ops_runs", failed_fetch)
stale = await projection.get_ops_run_projection(refresh=True)
assert stale.available is True
assert stale.stale is True
assert "upstream down" in stale.error
@pytest.mark.asyncio
async def test_ops_run_projection_reports_unconfigured(monkeypatch) -> None:
monkeypatch.setattr(settings, "activity_core_url", None)
result = await projection.get_ops_run_projection(refresh=True)
assert result.available is False
assert "ACTIVITY_CORE_URL is not configured" in result.error
@pytest.mark.asyncio
async def test_ops_runs_summary_route(client, monkeypatch) -> None:
async def fake_fetch():
return {"counts": {"open": 3, "claimed": 2}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
response = await client.get("/ops-runs/summary", params={"refresh": "true"})
assert response.status_code == 200
assert response.json()["open"] == 3
assert response.json()["claimed"] == 2
assert (await client.post("/ops-runs/claim", json={})).status_code == 404
@pytest.mark.asyncio
async def test_state_summary_contains_ops_run_projection(client, monkeypatch) -> None:
async def fake_fetch():
return {"counts": {"open": 4}, "items": []}
monkeypatch.setattr(projection, "_fetch_ops_runs", fake_fetch)
response = await client.get("/state/summary", params={"refresh": "true"})
assert response.status_code == 200
assert response.json()["ops_runs"]["open"] == 4