feat: architecture conformance checks in CI and metrics feedback loop
- cloud/arch_conformance.py: contract-version (+1 only, approved change record), forbidden-directory (own dir only, .. and symlink escapes resolved), forbidden-import layer bans audited against the baseline (foundation/execution/driver, server sqlite3 Row adapters deliberately allowed), G2 frozen-stack swap guard; skill-project one-call check. - cloud/metrics.py: first-pass/defect/rework rates over terminal tasks, unknown rates as None (never 0), #212 dual-column per-task cost (total None when incomplete), pure feedback rules (tighten/route-with-extra-audit/recalibrate, auto_apply False), golden-set gate: unapproved sample set is never decision-ready. - GET /metrics-report (authenticated, member-scoped, same as /metrics): attempts/defects are None until an attempt ledger exists, so rates stay unknown rather than fabricated. - Tests: 29 arch + 15 metrics + 3 endpoint, each check with positive and negative cases; repo-level scan pins zero layer violations on the baseline so a future violation fails CI via the existing host-verify discover run (no workflow change).
This commit is contained in:
1 parent
4c38e3c452
commit
38a10db0c8
7 files changed
+1201
No files matched your search
@@ -0,0 +1,221 @@
|
||||
"""Tests for cloud.arch_conformance: architecture consistency checks.
|
||||
|
||||
Every check has a positive and a negative case; the repo-level test
|
||||
pins that the current tree passes the layer bans, so a future
|
||||
violation fails CI (the existing host-verify workflow runs
|
||||
``discover -s cloud/tests``; a failure here is a CI failure).
|
||||
|
||||
Only the standard library; no real model, no network, no secrets.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from cloud.arch_conformance import ( # noqa: E402
|
||||
check_contract_version, check_frozen_stack, check_layer_imports,
|
||||
check_skill_project, check_task_writes, is_within,
|
||||
scan_cloud_package, scan_imports)
|
||||
|
||||
|
||||
class TestContractVersion(unittest.TestCase):
|
||||
def test_equal_versions_pass(self):
|
||||
self.assertTrue(check_contract_version("v1", "v1")["ok"])
|
||||
|
||||
def test_approved_step_bump_passes(self):
|
||||
change = {"approved": True, "backward_compatible": True,
|
||||
"affected_clients": ["web"]}
|
||||
self.assertTrue(
|
||||
check_contract_version("v1", "v2", change=change)["ok"])
|
||||
|
||||
def test_breaking_bump_needs_regression_task(self):
|
||||
change = {"approved": True, "backward_compatible": False,
|
||||
"affected_clients": ["web"]}
|
||||
verdict = check_contract_version("v1", "v2", change=change)
|
||||
self.assertFalse(verdict["ok"])
|
||||
self.assertTrue(any("regression" in r for r in verdict["reasons"]))
|
||||
|
||||
def test_breaking_bump_with_regression_task_passes(self):
|
||||
change = {"approved": True, "backward_compatible": False,
|
||||
"affected_clients": ["web"], "regression_task": "t-1"}
|
||||
self.assertTrue(
|
||||
check_contract_version("v1", "v2", change=change)["ok"])
|
||||
|
||||
def test_unapproved_bump_fails(self):
|
||||
change = {"approved": False, "backward_compatible": True,
|
||||
"affected_clients": ["web"]}
|
||||
self.assertFalse(
|
||||
check_contract_version("v1", "v2", change=change)["ok"])
|
||||
|
||||
def test_bump_without_change_record_fails(self):
|
||||
self.assertFalse(check_contract_version("v1", "v2")["ok"])
|
||||
|
||||
def test_skip_bump_fails_even_when_approved(self):
|
||||
change = {"approved": True, "backward_compatible": True,
|
||||
"affected_clients": ["web"]}
|
||||
verdict = check_contract_version("v1", "v3", change=change)
|
||||
self.assertFalse(verdict["ok"])
|
||||
|
||||
def test_unparseable_version_fails(self):
|
||||
self.assertFalse(check_contract_version("v1", "draft")["ok"])
|
||||
|
||||
|
||||
class TestFrozenStack(unittest.TestCase):
|
||||
def test_unchanged_stack_passes(self):
|
||||
frozen = {"web": "React", "ios": "SwiftUI"}
|
||||
self.assertTrue(check_frozen_stack(frozen, dict(frozen))["ok"])
|
||||
|
||||
def test_new_client_is_addition_not_swap(self):
|
||||
frozen = {"web": "React"}
|
||||
current = {"web": "React", "ios": "SwiftUI"}
|
||||
self.assertTrue(check_frozen_stack(frozen, current)["ok"])
|
||||
|
||||
def test_swap_without_approval_fails(self):
|
||||
verdict = check_frozen_stack({"web": "React"}, {"web": "Vue"})
|
||||
self.assertFalse(verdict["ok"])
|
||||
self.assertEqual(verdict["swapped"], ["web"])
|
||||
|
||||
def test_removed_client_without_approval_fails(self):
|
||||
self.assertFalse(
|
||||
check_frozen_stack({"web": "React", "ios": "SwiftUI"},
|
||||
{"web": "React"})["ok"])
|
||||
|
||||
def test_swap_with_approved_change_passes(self):
|
||||
change = {"approved": True, "backward_compatible": True,
|
||||
"affected_clients": ["web"]}
|
||||
self.assertTrue(
|
||||
check_frozen_stack({"web": "React"}, {"web": "Vue"},
|
||||
change=change)["ok"])
|
||||
|
||||
|
||||
class TestDirectoryGuard(unittest.TestCase):
|
||||
def test_inside_passes_and_outside_fails(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "work")
|
||||
os.makedirs(own)
|
||||
self.assertTrue(is_within(own, os.path.join(own, "a.txt")))
|
||||
self.assertTrue(is_within(own, own))
|
||||
self.assertFalse(is_within(own, os.path.join(tmp, "other")))
|
||||
|
||||
def test_dotdot_escape_fails(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "work")
|
||||
os.makedirs(own)
|
||||
escape = os.path.join(own, "..", "secret.txt")
|
||||
self.assertFalse(is_within(own, escape))
|
||||
self.assertEqual(check_task_writes(own, [escape]), [escape])
|
||||
|
||||
def test_symlink_escape_fails(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "work")
|
||||
outside = os.path.join(tmp, "outside")
|
||||
os.makedirs(own)
|
||||
os.makedirs(outside)
|
||||
link = os.path.join(own, "link")
|
||||
try:
|
||||
os.symlink(outside, link)
|
||||
except OSError:
|
||||
self.skipTest("symlinks unavailable")
|
||||
self.assertFalse(is_within(own, link))
|
||||
|
||||
def test_mixed_writes_report_only_violations(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "work")
|
||||
os.makedirs(own)
|
||||
good = os.path.join(own, "ok.txt")
|
||||
bad = os.path.join(tmp, "bad.txt")
|
||||
self.assertEqual(check_task_writes(own, [good, bad]), [bad])
|
||||
|
||||
|
||||
class TestLayerImports(unittest.TestCase):
|
||||
def test_foundation_importing_server_fails(self):
|
||||
verdict = check_layer_imports(
|
||||
{"db": "from cloud.server import foo\n"})
|
||||
self.assertEqual(len(verdict), 1)
|
||||
self.assertEqual(verdict[0]["rule"],
|
||||
"foundation_must_not_import_orchestration")
|
||||
|
||||
def test_foundation_importing_store_passes(self):
|
||||
# db importing nothing, models importing nothing: the baseline
|
||||
# shape. A foundation module importing another foundation
|
||||
# module is not an upward import.
|
||||
self.assertEqual(check_layer_imports(
|
||||
{"models": "import os\n", "queue": "from cloud.models import x\n"}),
|
||||
[])
|
||||
|
||||
def test_sandbox_importing_db_fails(self):
|
||||
verdict = check_layer_imports(
|
||||
{"sandbox": "from cloud.db import connect\n"})
|
||||
self.assertEqual(len(verdict), 1)
|
||||
|
||||
def test_sandbox_stdlib_only_passes(self):
|
||||
self.assertEqual(check_layer_imports(
|
||||
{"sandbox": "import os\nimport subprocess\n"}), [])
|
||||
|
||||
def test_service_opening_sqlite_directly_fails(self):
|
||||
verdict = check_layer_imports(
|
||||
{"server": "import sqlite3\nconn = sqlite3.connect('x.db')\n"})
|
||||
self.assertTrue(any(v["rule"] == "service_must_not_open_driver_"
|
||||
"connection_directly" for v in verdict))
|
||||
|
||||
def test_service_sqlite_row_adapter_passes(self):
|
||||
# Deliberate narrow ban: Row/exception adapters on a
|
||||
# cloud.db connection are adaptation, not a bypass.
|
||||
self.assertEqual(check_layer_imports(
|
||||
{"server": "import sqlite3\nconn.row_factory = sqlite3.Row\n"
|
||||
"try:\n pass\nexcept sqlite3.IntegrityError:\n"
|
||||
" pass\n"}), [])
|
||||
|
||||
def test_service_importing_psycopg_fails(self):
|
||||
verdict = check_layer_imports(
|
||||
{"worker": "import psycopg\n"})
|
||||
self.assertTrue(any(v["rule"] == "service_must_not_open_driver_"
|
||||
"connection_directly" for v in verdict))
|
||||
|
||||
def test_driver_adjacent_modules_exempt(self):
|
||||
self.assertEqual(check_layer_imports(
|
||||
{"backup": "import sqlite3\nsqlite3.connect('x.db')\n",
|
||||
"migrations": "import sqlite3\nsqlite3.connect('y.db')\n"}), [])
|
||||
|
||||
def test_scan_imports_sees_from_cloud_form(self):
|
||||
scanned = scan_imports("from cloud.worker import Worker\n")
|
||||
self.assertIn("worker", scanned["cloud"])
|
||||
|
||||
|
||||
class TestRepoLevelScan(unittest.TestCase):
|
||||
def test_current_tree_has_zero_layer_violations(self):
|
||||
repo_root = Path(__file__).resolve().parents[2]
|
||||
violations = scan_cloud_package(repo_root)
|
||||
self.assertEqual(violations, [])
|
||||
|
||||
|
||||
class TestSkillProjectFixture(unittest.TestCase):
|
||||
def test_frozen_project_passes_all_checks(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "apps", "web")
|
||||
os.makedirs(own)
|
||||
verdict = check_skill_project(
|
||||
tmp, "v1", "v1", {"web": "React"}, {"web": "React"},
|
||||
own, [os.path.join(own, "App.tsx")])
|
||||
self.assertTrue(verdict["ok"])
|
||||
|
||||
def test_drifting_project_fails_each_changed_check(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
own = os.path.join(tmp, "apps", "web")
|
||||
os.makedirs(own)
|
||||
verdict = check_skill_project(
|
||||
tmp, "v1", "v2", {"web": "React"}, {"web": "Vue"},
|
||||
own, [os.path.join(tmp, "apps", "ios", "leak.txt")])
|
||||
self.assertFalse(verdict["ok"])
|
||||
self.assertFalse(verdict["contract"]["ok"])
|
||||
self.assertFalse(verdict["stack"]["ok"])
|
||||
self.assertFalse(verdict["directory"]["ok"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,186 @@
|
||||
"""Tests for cloud.metrics: collection, dual-column cost, feedback.
|
||||
|
||||
Every metric has a positive case and an unknown/edge case; unknown
|
||||
is never reported as zero, and an unapproved golden set never makes
|
||||
a report decision-ready.
|
||||
|
||||
Only the standard library; no real model, no network, no secrets.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from cloud.arch_conformance import check_contract_version # noqa: E402
|
||||
from cloud.metrics import ( # noqa: E402
|
||||
build_report, compute_metrics, evaluate_feedback)
|
||||
|
||||
|
||||
def _record(task_id, status, attempts=None, defects=None, category=None):
|
||||
return {"id": task_id, "status": status, "attempts": attempts,
|
||||
"defects": defects, "defect_category": category}
|
||||
|
||||
|
||||
class TestComputeMetrics(unittest.TestCase):
|
||||
def test_first_pass_rework_defect_rates(self):
|
||||
records = [
|
||||
_record("a", "completed", attempts=1, defects=0),
|
||||
_record("b", "merged", attempts=3, defects=2,
|
||||
category="contract"),
|
||||
_record("c", "failed", attempts=2, defects=1,
|
||||
category="contract"),
|
||||
_record("d", "queued", attempts=1), # not terminal: excluded
|
||||
_record("e", "interrupted", attempts=1), # not terminal
|
||||
]
|
||||
m = compute_metrics(records)
|
||||
self.assertEqual(m["terminal_tasks"], 3)
|
||||
self.assertAlmostEqual(m["first_pass_rate"], 1 / 3)
|
||||
self.assertAlmostEqual(m["rework_rate"], 2 / 3)
|
||||
self.assertAlmostEqual(m["defect_rate"], 2 / 3)
|
||||
self.assertEqual(m["defect_by_category"], {"contract": 2})
|
||||
|
||||
def test_unknown_attempts_and_defects_are_none_not_zero(self):
|
||||
records = [_record("a", "completed"),
|
||||
_record("b", "failed")]
|
||||
m = compute_metrics(records)
|
||||
self.assertIsNone(m["first_pass_rate"])
|
||||
self.assertIsNone(m["rework_rate"])
|
||||
self.assertIsNone(m["defect_rate"])
|
||||
self.assertFalse(m["first_pass_known"])
|
||||
self.assertFalse(m["rework_known"])
|
||||
self.assertFalse(m["defect_known"])
|
||||
|
||||
def test_empty_set_rates_are_none(self):
|
||||
m = compute_metrics([])
|
||||
self.assertEqual(m["terminal_tasks"], 0)
|
||||
self.assertIsNone(m["first_pass_rate"])
|
||||
self.assertIsNone(m["defect_rate"])
|
||||
self.assertIsNone(m["total_usd_micros"])
|
||||
self.assertFalse(m["cost_complete"])
|
||||
|
||||
|
||||
class TestDualColumnCost(unittest.TestCase):
|
||||
def test_complete_cost_totals(self):
|
||||
records = [_record("a", "completed", attempts=1)]
|
||||
costs = {"a": {"priced_usd_micros": 5000,
|
||||
"unpriced_actual_tokens": 0, "has_rows": True}}
|
||||
m = compute_metrics(records, costs)
|
||||
self.assertEqual(m["total_usd_micros"], 5000)
|
||||
self.assertTrue(m["cost_complete"])
|
||||
|
||||
def test_unpriced_cost_never_folds_into_zero_or_total(self):
|
||||
records = [_record("a", "completed", attempts=1)]
|
||||
costs = {"a": {"priced_usd_micros": 5000,
|
||||
"unpriced_actual_tokens": 700, "has_rows": True}}
|
||||
m = compute_metrics(records, costs)
|
||||
self.assertIsNone(m["total_usd_micros"])
|
||||
self.assertEqual(m["priced_usd_micros_partial"], 5000)
|
||||
self.assertFalse(m["cost_complete"])
|
||||
self.assertEqual(m["per_task_cost"][0]["unpriced_actual_tokens"],
|
||||
700)
|
||||
|
||||
def test_missing_cost_rows_make_total_unknown(self):
|
||||
records = [_record("a", "completed", attempts=1)]
|
||||
m = compute_metrics(records, costs={})
|
||||
self.assertIsNone(m["total_usd_micros"])
|
||||
self.assertFalse(m["cost_complete"])
|
||||
|
||||
|
||||
class TestFeedbackRules(unittest.TestCase):
|
||||
def test_defect_breach_tightens_check(self):
|
||||
m = compute_metrics([
|
||||
_record("a", "completed", attempts=1),
|
||||
_record("b", "failed", attempts=1, defects=1,
|
||||
category="contract"),
|
||||
])
|
||||
actions = evaluate_feedback(
|
||||
m, thresholds={"defect_rate": 0.20,
|
||||
"first_pass_rate_min": 0.50,
|
||||
"check_overturn_rate": 0.30})
|
||||
tighten = [a for a in actions if a["kind"] == "tighten_check"]
|
||||
self.assertEqual(len(tighten), 1)
|
||||
self.assertEqual(tighten[0]["target"], "contract")
|
||||
self.assertFalse(tighten[0]["auto_apply"])
|
||||
|
||||
def test_no_breach_no_action(self):
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
actions = evaluate_feedback(
|
||||
m, thresholds={"defect_rate": 0.20,
|
||||
"first_pass_rate_min": 0.50,
|
||||
"check_overturn_rate": 0.30})
|
||||
self.assertEqual(actions, [])
|
||||
|
||||
def test_low_first_pass_routes_with_extra_audit(self):
|
||||
m = compute_metrics([
|
||||
_record("a", "completed", attempts=3),
|
||||
_record("b", "failed", attempts=2),
|
||||
])
|
||||
actions = evaluate_feedback(
|
||||
m, thresholds={"defect_rate": 0.20,
|
||||
"first_pass_rate_min": 0.50,
|
||||
"check_overturn_rate": 0.30})
|
||||
self.assertTrue(any(a["kind"] == "route_with_extra_audit"
|
||||
for a in actions))
|
||||
|
||||
def test_high_overturn_recalibrates_without_auto_apply(self):
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
actions = evaluate_feedback(
|
||||
m, thresholds={"defect_rate": 0.20,
|
||||
"first_pass_rate_min": 0.50,
|
||||
"check_overturn_rate": 0.30},
|
||||
check_stats=[{"check_id": "forbidden-dir", "violations": 10,
|
||||
"overturned": 5}])
|
||||
recal = [a for a in actions if a["kind"] == "recalibrate_check"]
|
||||
self.assertEqual(len(recal), 1)
|
||||
self.assertFalse(recal[0]["auto_apply"])
|
||||
|
||||
def test_low_overturn_no_recalibration(self):
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
actions = evaluate_feedback(
|
||||
m, check_stats=[{"check_id": "forbidden-dir",
|
||||
"violations": 10, "overturned": 1}])
|
||||
self.assertEqual(
|
||||
[a for a in actions if a["kind"] == "recalibrate_check"], [])
|
||||
|
||||
|
||||
class TestGoldenSetAndStandardsLoop(unittest.TestCase):
|
||||
def test_unapproved_golden_set_is_not_decision_ready(self):
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
report = build_report(m, golden={"tasks": ["a", "b"]})
|
||||
self.assertFalse(report["decision_ready"])
|
||||
self.assertEqual(report["golden_set"]["status"],
|
||||
"pending_human_approval")
|
||||
|
||||
def test_approved_golden_set_is_decision_ready(self):
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
report = build_report(
|
||||
m, golden={"tasks": ["a"], "approved_by": "human",
|
||||
"approved_at": "2026-10-31"},
|
||||
thresholds_source="human_approved")
|
||||
self.assertTrue(report["decision_ready"])
|
||||
self.assertEqual(report["thresholds_source"], "human_approved")
|
||||
|
||||
def test_sample_only_set_must_not_be_presented_as_golden(self):
|
||||
# The set that exists today is sample-only: no approval fields
|
||||
# exist for it, so any report built from it stays pending.
|
||||
m = compute_metrics([_record("a", "completed", attempts=1)])
|
||||
report = build_report(m, golden={"tasks": ["sample-1"]})
|
||||
self.assertFalse(report["decision_ready"])
|
||||
self.assertIn("NOT human-approved", report["decision_note"])
|
||||
|
||||
def test_standards_change_goes_through_contract_protocol(self):
|
||||
# The loop closes through the same protocol: an audit-standard
|
||||
# version bump without approval fails; with approval it passes.
|
||||
self.assertFalse(
|
||||
check_contract_version("v1", "v2")["ok"])
|
||||
self.assertTrue(check_contract_version(
|
||||
"v1", "v2",
|
||||
change={"approved": True, "backward_compatible": True,
|
||||
"affected_clients": ["all"]})["ok"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,120 @@
|
||||
"""Endpoint test for GET /metrics-report.
|
||||
|
||||
Only the standard library http.client; a temporary SQLite DB and a
|
||||
temporary port; no real model, no network, no secrets.
|
||||
|
||||
Covers: unauthenticated -> 401, empty project -> report shape with
|
||||
unknown rates (None, not 0) and pending golden set, and a completed
|
||||
task with a priced cost row -> terminal count and dual-column cost.
|
||||
"""
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from cloud.auth import AuthStore
|
||||
from cloud.cost import CostStore
|
||||
from cloud.server import create_server
|
||||
except ImportError:
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
from cloud.auth import AuthStore
|
||||
from cloud.cost import CostStore
|
||||
from cloud.server import create_server
|
||||
|
||||
|
||||
class MetricsReportEndpointTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.db_path = os.path.join(self.tmp.name, "control.db")
|
||||
self.auth = AuthStore(self.db_path)
|
||||
self.owner_id, self.owner_key = self.auth.create_user("owner")
|
||||
self.server = create_server(self.db_path, "127.0.0.1", 0)
|
||||
self.port = self.server.server_address[1]
|
||||
self.thread = threading.Thread(
|
||||
target=self.server.serve_forever, daemon=True)
|
||||
self.thread.start()
|
||||
status, self.project = self._req(
|
||||
"POST", "/projects", {"name": "demo"}, self.owner_key)
|
||||
self.assertEqual(status, 201)
|
||||
|
||||
def tearDown(self):
|
||||
self.server.shutdown()
|
||||
self.server.server_close()
|
||||
self.thread.join(timeout=5)
|
||||
try:
|
||||
self.auth.conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.tmp.cleanup()
|
||||
|
||||
def _req(self, method, path, body=None, key=None):
|
||||
conn = http.client.HTTPConnection("127.0.0.1", self.port, timeout=10)
|
||||
hdrs = {}
|
||||
payload = None
|
||||
if body is not None:
|
||||
payload = json.dumps(body).encode("utf-8")
|
||||
hdrs["Content-Type"] = "application/json"
|
||||
if key is not None:
|
||||
hdrs["X-API-Key"] = key
|
||||
try:
|
||||
conn.request(method, path, body=payload, headers=hdrs)
|
||||
resp = conn.getresponse()
|
||||
data = resp.read()
|
||||
finally:
|
||||
conn.close()
|
||||
try:
|
||||
parsed = json.loads(data.decode("utf-8")) if data else None
|
||||
except ValueError:
|
||||
parsed = None
|
||||
return resp.status, parsed
|
||||
|
||||
def test_unauthenticated_is_401(self):
|
||||
status, _ = self._req("GET", "/metrics-report")
|
||||
self.assertEqual(status, 401)
|
||||
|
||||
def test_empty_project_report_is_pending_and_unknown(self):
|
||||
status, report = self._req(
|
||||
"GET", "/metrics-report", key=self.owner_key)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertFalse(report["decision_ready"])
|
||||
self.assertEqual(report["golden_set"]["status"],
|
||||
"pending_human_approval")
|
||||
self.assertIsNone(report["metrics"]["first_pass_rate"])
|
||||
self.assertIsNone(report["metrics"]["defect_rate"])
|
||||
self.assertIsNone(report["metrics"]["total_usd_micros"])
|
||||
self.assertEqual(report["thresholds_source"],
|
||||
"default_unapproved")
|
||||
|
||||
def test_completed_task_with_priced_cost_row(self):
|
||||
from cloud.db import connect as db_connect
|
||||
conn = db_connect(self.db_path)
|
||||
try:
|
||||
conn.execute(
|
||||
"INSERT INTO tasks(id, project_id, status) VALUES(?, ?, ?)",
|
||||
("t-1", self.project["id"], "completed"))
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
store = CostStore(self.db_path)
|
||||
store.record("t-1", self.project["id"], "gpt-6-sol",
|
||||
input_tokens=1000, output_tokens=100,
|
||||
usd_micros=5000, source="actual")
|
||||
status, report = self._req(
|
||||
"GET", "/metrics-report", key=self.owner_key)
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(report["metrics"]["terminal_tasks"], 1)
|
||||
# Attempts are not in the schema: first-pass stays unknown,
|
||||
# it must not be fabricated as 0 or 1 from status alone.
|
||||
self.assertIsNone(report["metrics"]["first_pass_rate"])
|
||||
self.assertEqual(report["metrics"]["total_usd_micros"], 5000)
|
||||
self.assertTrue(report["metrics"]["cost_complete"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user