"""ORCH-091 — Group 2 (D2/D3): rollback reflection + stage-metric summation. Covers TC-05..TC-08 from 04-test-plan.yaml. The render path is pure DB (no network); a temp SQLite holds tasks + agent_runs. TC-05 / AC-4 — rollback deploy-staging->development: Development active (🔄), Testing/Внедрение NOT shown ✅, Анализ/Архитектура stay ✅. TC-06 / AC-5 — stage line sums ALL of an agent's runs (ORCH-069 developer 3 runs ≈ $3.98), not the last run. TC-07 / AC-5 — task totals (💰/🔢/⏱) converge with SUM(agent_runs). TC-08 / AC-7 — render_task_tracker never raises on broken/partial rows. """ import os import tempfile os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") _test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_rollback_metrics.db") os.environ["ORCH_DB_PATH"] = _test_db import pytest # noqa: E402 import src.db as db_module # noqa: E402 from src.db import init_db, get_db # noqa: E402 from src import notifications as N # noqa: E402 from src.usage import fmt_cost, fmt_tokens, _input_total # noqa: E402 @pytest.fixture(autouse=True) def setup_db(monkeypatch): monkeypatch.setattr(db_module.settings, "db_path", _test_db, raising=False) if os.path.exists(_test_db): os.unlink(_test_db) init_db() # Render-only: keep the live overlay off (offline core under test). monkeypatch.setattr(N._get_settings(), "tracker_live_status", False, raising=False) yield if os.path.exists(_test_db): os.unlink(_test_db) def _mk_task(stage="development", wid="ORCH-091", title="rollback/metrics", created=None, updated=None): conn = get_db() cur = conn.execute( "INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title) " "VALUES (?,?,?,?,?,?)", ("p1", wid, "orchestrator", "feature/ORCH-091-x", stage, title), ) tid = cur.lastrowid if created or updated: conn.execute( "UPDATE tasks SET created_at=COALESCE(?, created_at), " "updated_at=COALESCE(?, updated_at) WHERE id=?", (created, updated, tid), ) conn.commit() conn.close() return tid def _mk_run(tid, agent, started, finished, *, model="tokenator/claude-opus-4-8", in_tok=10, out_tok=5, cache_read=0, cache_creation=0, cost=0.0, effort=None, exit_code=0): conn = get_db() conn.execute( "INSERT INTO agent_runs (task_id, agent, started_at, finished_at, " "exit_code, input_tokens, output_tokens, cache_read_tokens, " "cache_creation_tokens, cost_usd, model, effort) " "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", (tid, agent, started, finished, exit_code, in_tok, out_tok, cache_read, cache_creation, cost, model, effort), ) conn.commit() conn.close() def _stage_line(text, label): """The single '✅