""" 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