Files
hermes-agent/tests/tools/test_confirmation_daemon.py

191 lines
6.2 KiB
Python

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