ouroboros/tests/test_evolution_status.py
Ouroboros 93bca33c35 fix(p1): apply converged review-wave findings for the rows-memo phase
Accepted batch from the P1 review wave (3x SAFE):
- drop the write-only _LedgerRowsMemo.records retention; the memo now holds
  only the final-rows dict + resume fingerprint (O(final rows))
- refuse to resume from a newline-less crash tail: _ledger_resume_state
  returns a non-resumable fingerprint (st_ino=-2) when the last byte is not
  a newline, so a later append that glues onto the unterminated line can
  never make a warm memo diverge from a fresh replay (regression test
  reproduces the probe; fails before the fix, passes after)
- budget_remaining structural self-check: a passed projection is accepted
  only when its limit_usd equals the self-read limit; otherwise self-compute
  (closes the settings-hot-reload race)
- unit pins for both de-triplication seams (api_state kwarg pass/withhold,
  budget_remaining use/fallback)
- ARCH ledger paragraph precision: mtime consulted only at unchanged size,
  prefix integrity from append-only discipline, O(final rows) memory bound
- thread-activity backfill marks its once-per-process guard only after a
  successful pass, so transient failures retry on the next reconcile tick
  (retry test added)
2026-08-08 07:03:34 +03:00

173 lines
6 KiB
Python

"""Tests for evolution/consciousness status snapshots."""
import json
from unittest.mock import MagicMock, patch
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.testclient import TestClient
def test_evolution_status_waits_for_owner_chat(monkeypatch):
from supervisor import queue as queue_module
monkeypatch.setattr(queue_module, "PENDING", [])
monkeypatch.setattr(queue_module, "RUNNING", {})
monkeypatch.setattr(
queue_module,
"load_state",
lambda: {
"evolution_mode_enabled": True,
"owner_chat_id": None,
"evolution_cycle": 3,
"evolution_consecutive_failures": 0,
"last_evolution_task_at": "",
},
)
monkeypatch.setattr(queue_module, "budget_remaining", lambda st, **_kwargs: 25.0)
snapshot = queue_module.get_evolution_status_snapshot()
assert snapshot["status"] == "waiting_for_owner_chat"
assert snapshot["enabled"] is True
assert snapshot["owner_chat_bound"] is False
def test_evolution_status_reports_waiting_for_idle(monkeypatch):
from supervisor import queue as queue_module
monkeypatch.setattr(queue_module, "PENDING", [{"id": "task-1", "type": "task"}])
monkeypatch.setattr(queue_module, "RUNNING", {})
monkeypatch.setattr(
queue_module,
"load_state",
lambda: {
"evolution_mode_enabled": True,
"owner_chat_id": 7,
"evolution_cycle": 4,
"evolution_consecutive_failures": 0,
"last_evolution_task_at": "",
},
)
monkeypatch.setattr(queue_module, "budget_remaining", lambda st, **_kwargs: 25.0)
snapshot = queue_module.get_evolution_status_snapshot()
assert snapshot["status"] == "waiting_for_idle"
assert snapshot["pending_count"] == 1
def test_evolution_status_reports_budget_stop_when_disabled_after_run(monkeypatch):
from supervisor import queue as queue_module
monkeypatch.setattr(queue_module, "PENDING", [])
monkeypatch.setattr(queue_module, "RUNNING", {})
monkeypatch.setattr(
queue_module,
"load_state",
lambda: {
"evolution_mode_enabled": False,
"owner_chat_id": 7,
"evolution_cycle": 6,
"evolution_consecutive_failures": 0,
"last_evolution_task_at": "2026-03-31T10:00:00Z",
},
)
monkeypatch.setattr(queue_module, "budget_remaining", lambda st, **_kwargs: 1.25)
snapshot = queue_module.get_evolution_status_snapshot()
assert snapshot["status"] == "budget_stopped"
assert snapshot["budget_remaining_usd"] == 1.25
def test_consciousness_status_snapshot_exposes_runtime_fields():
from ouroboros.consciousness import BackgroundConsciousness
with patch.object(BackgroundConsciousness, "_build_registry", return_value=MagicMock()):
consciousness = BackgroundConsciousness(
drive_root=MagicMock(),
repo_dir=MagicMock(),
event_queue=None,
owner_chat_id_fn=lambda: 1,
)
consciousness.pause()
consciousness._next_wakeup_sec = 180
snapshot = consciousness.status_snapshot()
assert snapshot["paused"] is True
assert snapshot["next_wakeup_sec"] == 180
assert snapshot["last_idle_reason"] == "paused_by_active_task"
def test_evolution_data_strips_legacy_checkpoint_result_status(tmp_path, monkeypatch):
from ouroboros.evolution_checkpoints import CHECKPOINTS_REL
from ouroboros.gateway import control
repo = tmp_path / "repo"
repo.mkdir()
checkpoint_path = tmp_path / CHECKPOINTS_REL
checkpoint_path.parent.mkdir(parents=True, exist_ok=True)
checkpoint_path.write_text(
json.dumps({
"task_id": "evo-legacy",
"status": "completed",
"result_status": "failed",
"reason_code": "legacy_error",
"loop_outcome": {
"result_status": "failed",
"compat_result_status": "failed",
},
})
+ "\n",
encoding="utf-8",
)
async def fake_collect_metrics(*_args, **_kwargs):
return []
monkeypatch.setattr("ouroboros.utils.collect_evolution_metrics", fake_collect_metrics)
control._evo_cache.clear()
control._evo_task = None
app = Starlette(routes=[Route("/api/evolution-data", endpoint=control.api_evolution_data)])
app.state.drive_root = tmp_path
app.state.repo_dir = repo
payload = TestClient(app).get("/api/evolution-data?force=1").json()
checkpoint = payload["checkpoints"][0]
assert "result_status" not in checkpoint
assert "result_status" not in checkpoint["loop_outcome"]
assert "compat_result_status" not in checkpoint["loop_outcome"]
assert checkpoint["outcome_axes"]["execution"]["status"] == "failed"
def test_budget_remaining_uses_valid_projection_and_self_computes_on_limit_mismatch(monkeypatch):
"""De-triplication seam pin: a passed projection is used verbatim (zero
ledger reads) ONLY when its limit_usd equals the limit budget_remaining
reads itself; a mismatched limit (settings hot-reload race, wrong-limit
caller) falls through to self-computation."""
from ouroboros import usage_accounting as ua
from supervisor import state
monkeypatch.setattr(state, "TOTAL_BUDGET_LIMIT", 10.0)
calls = {"projection": 0, "import": 0}
def _projection(_root, **_kwargs):
calls["projection"] += 1
return {"limit_usd": 10.0, "remaining_known_usd": 3.75}
monkeypatch.setattr(ua, "usage_projection", _projection)
monkeypatch.setattr(
ua, "ensure_legacy_imported",
lambda _root: calls.__setitem__("import", calls["import"] + 1),
)
valid = {"limit_usd": 10.0, "remaining_known_usd": 4.25}
assert state.budget_remaining({}, projection=valid) == 4.25
assert calls == {"projection": 0, "import": 0}
stale = {"limit_usd": 5.0, "remaining_known_usd": 4.25}
assert state.budget_remaining({}, projection=stale) == 3.75
assert calls == {"projection": 1, "import": 1}