Files
awoooi/apps/api/tests/test_ai_agent_automation_backlog_snapshot.py
Your Name 4f0787f869
All checks were successful
CD Pipeline / tests (push) Successful in 1m31s
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / build-and-deploy (push) Successful in 5m12s
CD Pipeline / post-deploy-checks (push) Successful in 2m21s
feat(governance): 顯示任務批准邊界與進度彙總
2026-06-05 09:23:41 +08:00

187 lines
6.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import json
import pytest
from src.services.ai_agent_automation_backlog_snapshot import (
load_latest_ai_agent_automation_backlog_snapshot,
)
def test_load_latest_backlog_snapshot_reads_newest_file(tmp_path):
older = _snapshot(generated_at="2026-06-03T00:00:00+08:00", completion=72)
newer = _snapshot(generated_at="2026-06-04T00:00:00+08:00", completion=76)
(tmp_path / "ai_agent_automation_backlog_2026-06-03.json").write_text(
json.dumps(older),
encoding="utf-8",
)
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(newer),
encoding="utf-8",
)
loaded = load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
assert loaded["generated_at"] == "2026-06-04T00:00:00+08:00"
assert loaded["program_status"]["overall_completion_percent"] == 76
assert loaded["rollups"]["total_items"] == 1
assert loaded["progress_summary"]["overall_percent"] == 0
assert loaded["item_approval_boundary_rollup"]["total_items"] == 1
assert loaded["approval_boundaries"]["sdk_installation_allowed"] is False
def test_load_backlog_snapshot_requires_read_only_mode(tmp_path):
snapshot = _snapshot()
snapshot["program_status"]["read_only_mode"] = False
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="read_only_mode"):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def test_load_backlog_snapshot_requires_blocked_approval_boundaries(tmp_path):
snapshot = _snapshot()
snapshot["approval_boundaries"]["paid_api_call_allowed"] = True
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="approval boundaries"):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def test_load_backlog_snapshot_requires_total_rollup_consistency(tmp_path):
snapshot = _snapshot()
snapshot["rollups"]["total_items"] = 2
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="total_items"):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def test_load_backlog_snapshot_requires_item_approval_boundaries(tmp_path):
snapshot = _snapshot()
del snapshot["backlog_items"][0]["approval_boundary"]
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="approval_boundary"):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def test_load_backlog_snapshot_requires_progress_summary_consistency(tmp_path):
snapshot = _snapshot()
snapshot["progress_summary"]["overall_percent"] = 99
(tmp_path / "ai_agent_automation_backlog_2026-06-04.json").write_text(
json.dumps(snapshot),
encoding="utf-8",
)
with pytest.raises(ValueError, match="overall_percent"):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def test_load_backlog_snapshot_fails_when_missing(tmp_path):
with pytest.raises(FileNotFoundError):
load_latest_ai_agent_automation_backlog_snapshot(tmp_path)
def _snapshot(
*,
generated_at: str = "2026-06-04T00:00:00+08:00",
completion: int = 76,
) -> dict:
return {
"schema_version": "ai_agent_automation_backlog_v1",
"generated_at": generated_at,
"source_inventory_snapshot_ref": "inventory.json",
"program_status": {
"overall_completion_percent": completion,
"current_priority": "P1",
"current_task_id": "P1-302",
"next_task_id": "P1-303",
"read_only_mode": True,
},
"rollups": {
"total_items": 1,
"by_priority": {"P1": 1},
"by_status": {"planned": 1},
"by_gate_status": {"read_only_allowed": 1},
"by_owner_agent": {"hermes": 1},
},
"progress_summary": {
"overall_percent": 0,
"done_items": 0,
"planned_items": 1,
"total_items": 1,
"formula": "round(done_items / total_items * 100)status=done 才計入完成。",
"by_priority": [
{
"priority": "P1",
"completion_percent": 0,
"done_items": 0,
"total_items": 1,
}
],
"by_workstream": [
{
"workstream_id": "WS2",
"display_name": "自動化待辦",
"completion_percent": 0,
"done_items": 0,
"total_items": 1,
"next_task_id": "P1-303",
}
],
},
"backlog_items": [
{
"item_id": "AUTO-P1-303",
"priority": "P1",
"status": "planned",
"workstream_id": "WS2",
"source_asset_id": "awoooi_api",
"source_signal_kind": "inventory_gap",
"title": "建立自動化待辦只讀 API",
"owner_agent": "hermes",
"recommended_action": "建立 read-only API。",
"action_class": "execute_read_only",
"gate_status": "read_only_allowed",
"risk_level": "medium",
"evidence_refs": ["docs/schemas/ai_agent_automation_backlog_v1.schema.json"],
"acceptance_criteria": ["API 只讀"],
"approval_boundary": {
"mode": "read_only_allowed",
"display_summary": "只讀呈現 committed snapshot不授權任何寫入。",
"allowed_actions": ["讀取 committed snapshot", "顯示治理 UI"],
"blocked_actions": ["production_write", "paid_api_call", "destructive_operation"],
"requires_operator_approval_for": ["任何非只讀操作"],
},
"next_review": "P1-303",
}
],
"item_approval_boundary_rollup": {
"total_items": 1,
"by_mode": {"read_only_allowed": 1},
"items_requiring_explicit_approval": [],
"items_with_blocked_operations": ["AUTO-P1-303"],
},
"approval_boundaries": {
"sdk_installation_allowed": False,
"paid_api_call_allowed": False,
"shadow_or_canary_allowed": False,
"production_routing_allowed": False,
"destructive_operation_allowed": False,
},
}