mindXtrain / tests /test_chronos_client.py
Gregory-L's picture
fork mindXtrain from GitHub (Professor-Codephreak/mindXtrain@661bd41) as the mindX-specific line
dfb775d verified
Raw History Blame Contribute Delete
7.12 kB
"""Coach chronos_client + the three /coach/api/diagnostics/* endpoints
it powers. Tests stay independent of a running mindX backend by
patching httpx.AsyncClient via MockTransport — same approach used by
test_ollama_backend.py.
"""
from __future__ import annotations
import httpx
import pytest
from fastapi.testclient import TestClient
from mindxtrain.operator import app as operator_app
from mindxtrain.operator.coach import chronos_client
client = TestClient(operator_app.app)
# ---- chronos_client direct ----------------------------------------------
@pytest.mark.asyncio
async def test_now_returns_promised_time_shape(monkeypatch):
body = {
"unix_18dp": "1779169999.123456789",
"utc": "2026-05-19T05:53:19.123456789+00:00",
"consensus": "correlated",
"confidence_ms": 42.0,
"sources": {"cpu": {"unix": 1779169999.0}},
"anchor_count_24h": 12,
"promised_by": "chronos.agent",
}
transport = httpx.MockTransport(lambda req: httpx.Response(200, json=body))
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
pt = await chronos_client.now()
assert pt["consensus"] == "correlated"
assert pt["confidence_ms"] == 42.0
assert pt["anchor_count_24h"] == 12
assert pt["promised_by"] == "chronos.agent"
@pytest.mark.asyncio
async def test_now_degrades_on_connection_error(monkeypatch):
def _raise(*_a, **_kw):
raise httpx.ConnectError("mindX down")
transport = httpx.MockTransport(_raise)
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
pt = await chronos_client.now()
# The contract: same keys present, consensus flips to "unavailable".
assert pt["consensus"] == "unavailable"
assert pt["confidence_ms"] == 0.0
assert pt["anchor_count_24h"] == 0
assert pt["promised_by"] == "chronos.agent"
assert "error" in pt["sources"]
@pytest.mark.asyncio
async def test_now_fills_missing_keys_from_partial_response(monkeypatch):
"""Defensive against upstream shape drift — caller never KeyErrors."""
transport = httpx.MockTransport(
lambda req: httpx.Response(200, json={"unix_18dp": "1.0"}),
)
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
pt = await chronos_client.now()
assert pt["unix_18dp"] == "1.0"
assert pt["consensus"] == "unavailable" # default fill
assert pt["anchor_count_24h"] == 0
assert pt["sources"] == {}
@pytest.mark.asyncio
async def test_anchors_returns_list_and_n(monkeypatch):
body = {"anchors": [{"id": 1, "chain": "algorand"}], "n": 1}
transport = httpx.MockTransport(lambda req: httpx.Response(200, json=body))
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
out = await chronos_client.anchors(limit=10)
assert out["n"] == 1
assert out["anchors"][0]["chain"] == "algorand"
@pytest.mark.asyncio
async def test_anchors_returns_empty_on_failure(monkeypatch):
def _raise(*_a, **_kw):
raise httpx.ConnectError("nope")
transport = httpx.MockTransport(_raise)
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
out = await chronos_client.anchors(limit=10)
assert out["n"] == 0
assert out["anchors"] == []
assert "error" in out
@pytest.mark.asyncio
async def test_drift_returns_history_shape(monkeypatch):
body = {
"hours": 24, "bucket_count": 2,
"buckets": [{"ts_unix": 1000, "n": 3, "drift_mean_ms": 5.0, "drift_std_ms": 1.0}],
"drift_std_ms": 1.5, "drift_max_abs_ms": 8.0, "anchor_count": 5,
}
transport = httpx.MockTransport(lambda req: httpx.Response(200, json=body))
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
out = await chronos_client.drift(hours=24)
assert out["bucket_count"] == 2
assert out["anchor_count"] == 5
@pytest.mark.asyncio
async def test_base_url_env_override(monkeypatch):
"""MINDX_BASE_URL must shape the request URL."""
monkeypatch.setenv("MINDX_BASE_URL", "http://example.test:9999")
seen: dict[str, str] = {}
def _handler(req):
seen["url"] = str(req.url)
return httpx.Response(200, json={"hours": 24, "buckets": []})
transport = httpx.MockTransport(_handler)
orig = httpx.AsyncClient
monkeypatch.setattr(
httpx, "AsyncClient",
lambda *a, **kw: orig(*a, **{**kw, "transport": transport}),
)
await chronos_client.drift()
assert "example.test:9999" in seen["url"]
# ---- Coach endpoint integration -----------------------------------------
def test_coach_chronos_endpoint_merges_three_calls(monkeypatch):
"""/coach/api/diagnostics/chronos aggregates time + anchors + drift."""
async def _fake_now(**_kw):
return {"consensus": "correlated", "confidence_ms": 1.0, "promised_by": "x"}
async def _fake_anchors(**_kw):
return {"anchors": [{"id": 1}], "n": 1}
async def _fake_drift(**_kw):
return {"hours": 24, "anchor_count": 1, "buckets": []}
monkeypatch.setattr(chronos_client, "now", _fake_now)
monkeypatch.setattr(chronos_client, "anchors", _fake_anchors)
monkeypatch.setattr(chronos_client, "drift", _fake_drift)
r = client.get("/coach/api/diagnostics/chronos")
assert r.status_code == 200
body = r.json()
assert body["promised_time"]["consensus"] == "correlated"
assert body["anchor_count"] == 1
assert body["drift_history"]["anchor_count"] == 1
def test_measurement_confidence_endpoint_returns_band():
"""psutil vs ps cross-check endpoint — should classify on a live host."""
r = client.get("/coach/api/diagnostics/measurement-confidence")
assert r.status_code == 200
body = r.json()
# Band is one of the four known categories.
assert body["confidence_band"] in {"tight", "loose", "divergent", "unknown"}
if body["ok"]:
# Deltas are non-negative absolute values.
assert body["cpu_delta_pp"] >= 0
assert body["rss_delta_mb"] >= 0
def test_cli_samplers_endpoint_returns_sampler_records():
"""When mindX cli_time_samplers.py is reachable, all 6 records come through."""
r = client.get("/coach/api/diagnostics/cli-samplers")
assert r.status_code == 200
body = r.json()
# Either ok=True (mindX checkout present) or ok=False with a reason.
assert "ok" in body and "samplers" in body
if body["ok"]:
# Six samplers expected.
assert set(body["samplers"]) == {
"nano_wall", "ntp_status", "uptime_load",
"proc_snapshot", "cpu_jiffies", "monotonic_boot",
}