from __future__ import annotations import json import pytest from src.services.dependency_risk_policy import load_latest_dependency_risk_policy def test_load_latest_dependency_risk_policy_reads_newest_file(tmp_path): older = _snapshot(generated_at="2026-06-03T00:00:00+08:00", completion=97) newer = _snapshot(generated_at="2026-06-04T00:00:00+08:00", completion=98) (tmp_path / "dependency_risk_policy_2026-06-03.json").write_text( json.dumps(older), encoding="utf-8", ) (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(newer), encoding="utf-8", ) loaded = load_latest_dependency_risk_policy(tmp_path) assert loaded["generated_at"] == "2026-06-04T00:00:00+08:00" assert loaded["program_status"]["overall_completion_percent"] == 98 assert loaded["rollups"]["total_rules"] == 4 assert loaded["operation_boundaries"]["external_cve_lookup_allowed"] is False def test_dependency_risk_policy_requires_read_only_mode(tmp_path): snapshot = _snapshot() snapshot["program_status"]["read_only_mode"] = False (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="read_only_mode"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_requires_blocked_operations(tmp_path): snapshot = _snapshot() snapshot["operation_boundaries"]["package_upgrade_allowed"] = True (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="operation boundaries"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_requires_total_rule_consistency(tmp_path): snapshot = _snapshot() snapshot["rollups"]["total_rules"] = 999 (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="total_rules"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_requires_severity_rollup_consistency(tmp_path): snapshot = _snapshot() snapshot["rollups"]["by_severity"]["high"] = 999 (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="by_severity.high"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_requires_status_rollup_consistency(tmp_path): snapshot = _snapshot() snapshot["rollups"]["by_status"]["action_required"] = 999 (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="by_status.action_required"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_requires_rule_id_rollup_consistency(tmp_path): snapshot = _snapshot() snapshot["rollups"]["action_required_rule_ids"] = [] (tmp_path / "dependency_risk_policy_2026-06-04.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="action_required_rule_ids"): load_latest_dependency_risk_policy(tmp_path) def test_dependency_risk_policy_fails_when_missing(tmp_path): with pytest.raises(FileNotFoundError): load_latest_dependency_risk_policy(tmp_path) def _snapshot( *, generated_at: str = "2026-06-04T00:00:00+08:00", completion: int = 98, ) -> dict: return { "schema_version": "dependency_risk_policy_v1", "generated_at": generated_at, "program_status": { "overall_completion_percent": completion, "current_priority": "P1", "current_task_id": "P1-204", "next_task_id": "P1-205", "read_only_mode": True, }, "source_refs": ["docs/evaluations/package_supply_chain_inventory_2026-06-04.json"], "risk_taxonomy": { "severity_levels": [ { "severity": "critical", "definition": "known exploited", "default_gate": "approval", }, { "severity": "high", "definition": "runtime exposure", "default_gate": "approval", }, { "severity": "medium", "definition": "drift", "default_gate": "monitor", }, { "severity": "low", "definition": "accepted", "default_gate": "monitor", }, ], "statuses": ["accepted", "action_required", "planned_next", "blocked"], "policy_states": [ "monitor_only", "approval_package_required", "external_lookup_required", "blocked_until_approval", ], }, "rollups": { "total_rules": 4, "by_severity": {"critical": 1, "high": 1, "medium": 1, "low": 1}, "by_status": {"action_required": 1, "planned_next": 2, "accepted": 1}, "action_required_rule_ids": ["python_manifest_authority_drift"], "planned_next_rule_ids": [ "cve_critical_known_exploited", "license_strong_copyleft_or_unknown", ], "accepted_rule_ids": ["js_lockfile_currently_in_sync"], }, "severity_rules": [ _rule("cve_critical_known_exploited", "cve", "critical", "planned_next"), _rule("license_strong_copyleft_or_unknown", "license", "high", "planned_next"), _rule("python_manifest_authority_drift", "python", "medium", "action_required"), _rule("js_lockfile_currently_in_sync", "javascript", "low", "accepted"), ], "domain_policies": [ { "policy_id": "python_dependency_policy", "domain": "python", "status": "action_required", "owner_agent": "openclaw", "policy_summary": "policy", "allowed_now": ["read_only_report"], "blocked_now": ["package_upgrade"], "required_next_gate": "approval", "evidence_refs": ["apps/api/pyproject.toml"], } ], "action_queue": [ { "task_id": "P1-205", "priority": "P1", "status": "planned_next", "owner_agent": "hermes", "title": "建立定期依賴漂移檢查", "blocked_operations": ["package_upgrade"], "acceptance_criteria": ["只讀"], } ], "operation_boundaries": { "read_only_policy_allowed": True, "external_cve_lookup_allowed": False, "external_license_lookup_allowed": False, "package_installation_allowed": False, "package_upgrade_allowed": False, "lockfile_write_allowed": False, "docker_build_allowed": False, "image_pull_allowed": False, "image_rebuild_allowed": False, "registry_push_allowed": False, "paid_api_call_allowed": False, "shadow_or_canary_allowed": False, "production_routing_allowed": False, }, "approval_boundaries": { "sdk_installation_allowed": False, "paid_api_call_allowed": False, "shadow_or_canary_allowed": False, "production_routing_allowed": False, "destructive_operation_allowed": False, }, } def _rule(rule_id: str, domain: str, severity: str, status: str) -> dict: return { "rule_id": rule_id, "domain": domain, "severity": severity, "status": status, "trigger": "trigger", "current_evidence": "evidence", "required_gate": "approval", "blocked_operations": ["package_upgrade"], "owner_agent": "openclaw", "role_contract": "contract", "evidence_refs": ["docs/evaluations/package_supply_chain_inventory_2026-06-04.json"], "next_action": "next", }