"""Tests for tools.confirmation_daemon — Human Confirmation Firewall.""" import pytest import time from tools.confirmation_daemon import ( ConfirmationDaemon, ConfirmationRequest, ConfirmationStatus, RiskLevel, classify_action, _is_whitelisted, _DEFAULT_WHITELIST, ) class TestClassifyAction: """Test action risk classification.""" def test_crypto_tx_is_critical(self): assert classify_action("crypto_tx") == RiskLevel.CRITICAL def test_sign_transaction_is_critical(self): assert classify_action("sign_transaction") == RiskLevel.CRITICAL def test_send_email_is_high(self): assert classify_action("send_email") == RiskLevel.HIGH def test_send_message_is_medium(self): assert classify_action("send_message") == RiskLevel.MEDIUM def test_access_calendar_is_low(self): assert classify_action("access_calendar") == RiskLevel.LOW def test_unknown_action_is_medium(self): assert classify_action("unknown_action_xyz") == RiskLevel.MEDIUM class TestWhitelist: """Test whitelist auto-approval.""" def test_self_email_is_whitelisted(self): whitelist = dict(_DEFAULT_WHITELIST) payload = {"from": "me@test.com", "to": "me@test.com"} assert _is_whitelisted("send_email", payload, whitelist) is True def test_non_whitelisted_recipient_not_approved(self): whitelist = dict(_DEFAULT_WHITELIST) payload = {"to": "random@stranger.com"} assert _is_whitelisted("send_email", payload, whitelist) is False def test_whitelisted_contact_approved(self): whitelist = { "send_message": {"targets": ["alice", "bob"]}, } assert _is_whitelisted("send_message", {"to": "alice"}, whitelist) is True assert _is_whitelisted("send_message", {"to": "charlie"}, whitelist) is False def test_no_whitelist_entry_means_not_whitelisted(self): whitelist = {} assert _is_whitelisted("crypto_tx", {"amount": 1.0}, whitelist) is False class TestConfirmationRequest: """Test the request data model.""" def test_defaults(self): req = ConfirmationRequest( request_id="test-1", action="send_email", description="Test email", risk_level="high", payload={}, ) assert req.status == ConfirmationStatus.PENDING.value assert req.created_at > 0 assert req.expires_at > req.created_at def test_is_pending(self): req = ConfirmationRequest( request_id="test-2", action="send_email", description="Test", risk_level="high", payload={}, expires_at=time.time() + 300, ) assert req.is_pending is True def test_is_expired(self): req = ConfirmationRequest( request_id="test-3", action="send_email", description="Test", risk_level="high", payload={}, expires_at=time.time() - 10, ) assert req.is_expired is True assert req.is_pending is False def test_to_dict(self): req = ConfirmationRequest( request_id="test-4", action="send_email", description="Test", risk_level="medium", payload={"to": "a@b.com"}, ) d = req.to_dict() assert d["request_id"] == "test-4" assert d["action"] == "send_email" assert "is_pending" in d class TestConfirmationDaemon: """Test the daemon logic (without HTTP layer).""" def test_auto_approve_low_risk(self): daemon = ConfirmationDaemon() req = daemon.request( action="access_calendar", description="Read today's events", risk_level="low", ) assert req.status == ConfirmationStatus.AUTO_APPROVED.value def test_whitelisted_auto_approves(self): daemon = ConfirmationDaemon() daemon._whitelist = {"send_message": {"targets": ["alice"]}} req = daemon.request( action="send_message", description="Message alice", payload={"to": "alice"}, ) assert req.status == ConfirmationStatus.AUTO_APPROVED.value def test_non_whitelisted_goes_pending(self): daemon = ConfirmationDaemon() daemon._whitelist = {} req = daemon.request( action="send_email", description="Email to stranger", payload={"to": "stranger@test.com"}, risk_level="high", ) assert req.status == ConfirmationStatus.PENDING.value assert req.is_pending is True def test_approve_response(self): daemon = ConfirmationDaemon() daemon._whitelist = {} req = daemon.request( action="send_email", description="Email test", risk_level="high", ) result = daemon.respond(req.request_id, approved=True, decided_by="human") assert result.status == ConfirmationStatus.APPROVED.value assert result.decided_by == "human" def test_deny_response(self): daemon = ConfirmationDaemon() daemon._whitelist = {} req = daemon.request( action="crypto_tx", description="Send 1 ETH", risk_level="critical", ) result = daemon.respond( req.request_id, approved=False, decided_by="human", reason="Too risky" ) assert result.status == ConfirmationStatus.DENIED.value assert result.reason == "Too risky" def test_get_pending(self): daemon = ConfirmationDaemon() daemon._whitelist = {} daemon.request(action="send_email", description="Test 1", risk_level="high") daemon.request(action="send_email", description="Test 2", risk_level="high") pending = daemon.get_pending() assert len(pending) >= 2 def test_get_history(self): daemon = ConfirmationDaemon() req = daemon.request( action="access_calendar", description="Test", risk_level="low" ) history = daemon.get_history() assert len(history) >= 1 assert history[0]["action"] == "access_calendar"