Agentic-Reliability-Framework-API / tests /test_routes_governance_execute.py
petter2025's picture
Upload folder using huggingface_hub
3623a88 verified
Raw
History Blame Contribute Delete
13.9 kB
"""
Tests for POST /intents/{id}/execute and POST /admin/executions/{id}/resolve.
arf_enterprise is not installed in this test environment (by design -- it's
an optional, proprietary package; see routes_governance.py's
ENTERPRISE_EXECUTOR_AVAILABLE guard). The 501 "not available"/"not enabled"
paths are real, unconditional behavior and tested as such. For the
success/pending/error paths, the enterprise classes referenced in
routes_governance.py (EnterpriseExecutor, FakeCloudActuator,
PendingApprovalError, EnterpriseExecutionError, EnterpriseSafetyError) are
monkeypatched with lightweight stand-ins -- this tests arf-api's own glue
code (existence/tenant check, exception-to-HTTP-status mapping, response
shaping), not EnterpriseExecutor's internals, which are already covered by
the enterprise repo's own test suite.
"""
import pytest
from app.database.models_intents import TenantDB
import app.api.routes_governance as routes_governance
import app.api.routes_admin as routes_admin
TENANT_ID = "test-tenant"
@pytest.fixture(autouse=True)
def seed_tenant(db_session):
tenant = db_session.query(TenantDB).filter_by(id=TENANT_ID).first()
if not tenant:
db_session.add(TenantDB(id=TENANT_ID, name="Test Tenant"))
db_session.commit()
def _evaluate_intent(client):
payload = {
"intent_type": "provision_resource",
"environment": "prod",
"resource_type": "database",
"region": "eastus",
"size": "Standard",
"estimated_cost": 1200,
"policy_violations": [],
"requester": "alice",
"provenance": {},
"configuration": {},
}
resp = client.post(
"/api/v1/intents/evaluate", json=payload, headers={"X-Tenant-ID": TENANT_ID}
)
assert resp.status_code == 200, resp.text
data = resp.json()
# data["intent_id"] (top level, set by evaluate_intent_endpoint right
# before returning: result["intent_id"] = deterministic_id) is what
# save_evaluated_intent actually persisted to IntentDB.deterministic_id.
# data["healing_intent"]["intent_id"] is a separate, independently
# generated id belonging to the HealingIntent object itself -- using it
# here instead was the exact bug that made every test in this file 404.
return data["intent_id"], data["healing_intent"]
class _FakePendingApprovalError(Exception):
def __init__(self, message, level, approval_required, approval_id=None):
super().__init__(message)
self.level = level
self.approval_required = approval_required
self.approval_id = approval_id
class _FakeExecutionError(Exception):
pass
class _FakeSafetyError(Exception):
pass
class _FakeConfig:
"""Stands in for EnterpriseConfig, which is None in the import fallback
whenever arf_enterprise isn't installed -- as it isn't in CI, since it's
a private-repo package deliberately kept out of requirements.txt.
Records what it was constructed with so a test can assert that
ARF_TRUSTED_SIGNING_KEYS actually reaches the executor. Without that
the trust store could silently go back to empty and every signed intent
would be rejected as "Untrusted signing key" with nothing failing here.
"""
last_trusted_signing_keys = None
def __init__(self, trusted_signing_keys=None, **kwargs):
self.trusted_signing_keys = trusted_signing_keys
type(self).last_trusted_signing_keys = trusted_signing_keys
class _FakeExecutor:
"""Stands in for EnterpriseExecutor. Behavior is selected via a
class-level `mode` set by each test before the request is made."""
mode = "success"
last_config = None
def __init__(self, config=None, actuator=None, approval_store=None,
on_verified_outcome=None):
self._on_verified_outcome = on_verified_outcome
type(self).last_config = config
async def execute(self, intent, human_approved=False, admin_approved=False):
if self.mode == "success":
if self._on_verified_outcome:
self._on_verified_outcome(intent, True, {"observed": {"status": "running"}})
return {"status": "success", "verified": {"status": "running"}, "compensating_action": None}
if self.mode == "pending":
raise _FakePendingApprovalError(
"needs human approval", level="HumanInLoop", approval_required="human",
approval_id="appr_test123",
)
if self.mode == "execution_error":
raise _FakeExecutionError("ladder denied this intent")
if self.mode == "safety_error":
raise _FakeSafetyError("blast radius exceeded")
raise RuntimeError(f"unhandled test mode: {self.mode}")
@pytest.fixture
def enterprise_execution_enabled(monkeypatch):
monkeypatch.setattr(routes_governance, "ENTERPRISE_EXECUTOR_AVAILABLE", True)
monkeypatch.setattr(routes_governance, "ARF_ENABLE_EXECUTION", True)
monkeypatch.setattr(routes_governance, "EnterpriseExecutor", _FakeExecutor)
monkeypatch.setattr(routes_governance, "EnterpriseConfig", _FakeConfig)
monkeypatch.setattr(routes_governance, "FakeCloudActuator", lambda: None)
monkeypatch.setattr(routes_governance, "PendingApprovalError", _FakePendingApprovalError)
monkeypatch.setattr(routes_governance, "EnterpriseExecutionError", _FakeExecutionError)
monkeypatch.setattr(routes_governance, "EnterpriseSafetyError", _FakeSafetyError)
_FakeExecutor.mode = "success"
yield
_FakeExecutor.mode = "success"
def test_execute_returns_501_when_enterprise_package_not_available(client):
resp = client.post(
"/api/v1/intents/does-not-matter/execute",
json={"healing_intent": {}},
)
assert resp.status_code == 501
assert "not installed" in resp.json()["detail"]
def test_execute_returns_501_when_not_enabled(client, monkeypatch):
monkeypatch.setattr(routes_governance, "ENTERPRISE_EXECUTOR_AVAILABLE", True)
monkeypatch.setattr(routes_governance, "ARF_ENABLE_EXECUTION", False)
resp = client.post(
"/api/v1/intents/does-not-matter/execute",
json={"healing_intent": {}},
)
assert resp.status_code == 501
assert "not enabled" in resp.json()["detail"]
def test_execute_returns_404_for_unknown_intent(client, enterprise_execution_enabled):
resp = client.post(
"/api/v1/intents/does-not-exist-at-all/execute",
json={"healing_intent": {}},
)
assert resp.status_code == 404
def test_execute_success_path(client, enterprise_execution_enabled):
deterministic_id, healing_intent = _evaluate_intent(client)
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent, "human_approved": True},
)
assert resp.status_code == 200, resp.text
assert resp.json()["status"] == "success"
def test_trusted_signing_keys_reach_the_executor_split_not_raw(
client, enterprise_execution_enabled, monkeypatch
):
"""ARF_TRUSTED_SIGNING_KEYS holds N comma-separated fingerprints.
Passing the raw string through as a one-element list would register the
literal "abc,def" as a single key -- trusting neither real one -- and
would look identical from the outside, since both spellings produce a
non-empty list and a 200 here."""
monkeypatch.setenv("ARF_TRUSTED_SIGNING_KEYS", " abc123 , def456 ,, ")
deterministic_id, healing_intent = _evaluate_intent(client)
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent, "human_approved": True},
)
assert resp.status_code == 200, resp.text
assert _FakeConfig.last_trusted_signing_keys == ["abc123", "def456"]
assert _FakeExecutor.last_config is not None
def test_unset_trusted_signing_keys_trusts_nothing_rather_than_the_empty_string(
client, enterprise_execution_enabled, monkeypatch
):
"""Fail closed. `"".split(",")` is `[""]`, so the naive parse would
register the empty string as a trusted fingerprint -- trusting a key
nobody holds is harmless, but it makes "trusts nothing" and
"misconfigured" indistinguishable in the logs."""
monkeypatch.delenv("ARF_TRUSTED_SIGNING_KEYS", raising=False)
deterministic_id, healing_intent = _evaluate_intent(client)
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent, "human_approved": True},
)
assert resp.status_code == 200, resp.text
assert _FakeConfig.last_trusted_signing_keys == []
def test_execute_pending_approval_returns_202_with_approval_id(client, enterprise_execution_enabled):
deterministic_id, healing_intent = _evaluate_intent(client)
_FakeExecutor.mode = "pending"
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent},
)
assert resp.status_code == 202
body = resp.json()
assert body["approval_id"] == "appr_test123"
assert body["level"] == "HumanInLoop"
def test_execute_execution_error_returns_422(client, enterprise_execution_enabled):
deterministic_id, healing_intent = _evaluate_intent(client)
_FakeExecutor.mode = "execution_error"
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent, "human_approved": True},
)
assert resp.status_code == 422
assert "ladder denied" in resp.json()["detail"]
def test_execute_safety_error_returns_422(client, enterprise_execution_enabled):
deterministic_id, healing_intent = _evaluate_intent(client)
_FakeExecutor.mode = "safety_error"
resp = client.post(
f"/api/v1/intents/{deterministic_id}/execute",
json={"healing_intent": healing_intent, "human_approved": True},
)
assert resp.status_code == 422
assert "blast radius" in resp.json()["detail"]
def test_execute_belongs_to_different_tenant_returns_404(client, enterprise_execution_enabled, db_session):
"""A deterministic_id that exists but under a different tenant must not
be executable by this caller -- same tenant-scoping guarantee
record_outcome already provides for /intents/outcome."""
other_tenant = "other-tenant"
if not db_session.query(TenantDB).filter_by(id=other_tenant).first():
db_session.add(TenantDB(id=other_tenant, name="Other Tenant"))
db_session.commit()
from app.database.models_intents import IntentDB
import datetime
db_session.add(IntentDB(
deterministic_id="belongs-to-other-tenant",
tenant_id=other_tenant,
intent_type="provision_resource",
payload={},
oss_payload={},
environment="prod",
evaluated_at=datetime.datetime.utcnow(),
risk_score="0.1",
))
db_session.commit()
resp = client.post(
"/api/v1/intents/belongs-to-other-tenant/execute",
json={"healing_intent": {}},
)
assert resp.status_code == 404
# ---------------------------------------------------------------------------
# Admin resolve/list endpoints
# ---------------------------------------------------------------------------
TEST_ADMIN_KEY = "test-admin-key-for-governance-execute-tests"
class _FakeApprovalRecord:
def __init__(self, id, decision_id, intent_id, level, approval_required, requested_at):
self.id = id
self.decision_id = decision_id
self.intent_id = intent_id
self.level = level
self.approval_required = approval_required
self.requested_at = requested_at
class _FakeApprovalStore:
def __init__(self):
import datetime
self._pending = {
"appr_1": _FakeApprovalRecord(
"appr_1", "dec_1", "intent-1", "HumanInLoop", "human", datetime.datetime.utcnow()
)
}
self.resolved = []
def list_pending(self, limit=100, offset=0):
return list(self._pending.values())[:limit]
def resolve(self, approval_id, approved, resolved_by, note=None):
if approval_id not in self._pending:
return False
del self._pending[approval_id]
self.resolved.append((approval_id, approved, resolved_by, note))
return True
@pytest.fixture
def admin_with_approval_store(monkeypatch, client):
monkeypatch.setattr(routes_admin, "ADMIN_API_KEY", TEST_ADMIN_KEY)
fake_store = _FakeApprovalStore()
client.app.state.approval_store = fake_store
yield fake_store
client.app.state.approval_store = None
def test_list_pending_executions_returns_501_without_store(client, monkeypatch):
monkeypatch.setattr(routes_admin, "ADMIN_API_KEY", TEST_ADMIN_KEY)
client.app.state.approval_store = None
resp = client.get("/api/v1/admin/executions/pending", headers={"X-Admin-Key": TEST_ADMIN_KEY})
assert resp.status_code == 501
def test_list_pending_executions(client, admin_with_approval_store):
resp = client.get("/api/v1/admin/executions/pending", headers={"X-Admin-Key": TEST_ADMIN_KEY})
assert resp.status_code == 200
body = resp.json()
assert body["total"] == 1
assert body["pending"][0]["approval_id"] == "appr_1"
def test_resolve_execution_approval(client, admin_with_approval_store):
resp = client.post(
"/api/v1/admin/executions/appr_1/resolve",
headers={"X-Admin-Key": TEST_ADMIN_KEY},
json={"approved": True, "note": "looks fine"},
)
assert resp.status_code == 200
assert admin_with_approval_store.resolved == [("appr_1", True, "admin", "looks fine")]
def test_resolve_unknown_approval_returns_404(client, admin_with_approval_store):
resp = client.post(
"/api/v1/admin/executions/does-not-exist/resolve",
headers={"X-Admin-Key": TEST_ADMIN_KEY},
json={"approved": True},
)
assert resp.status_code == 404