annator-command-center / tests /integration /browser /test_browser_governance.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 8)
aef804e verified
Raw
History Blame Contribute Delete
20.3 kB
"""
Browser governance integration tests (INTG-18).
Tests cover:
- STUDENT agent access
- INTERN agent access
- SUPERVISED agent access
- AUTONOMOUS agent access
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.factories.agent_factory import StudentAgentFactory, InternAgentFactory, SupervisedAgentFactory, AutonomousAgentFactory
from core.models import BrowserSession
from unittest.mock import Mock, patch
import uuid
class TestStudentAgentAccess:
"""Test STUDENT agent browser access."""
def test_student_blocked_from_browser_session(self, client: TestClient, auth_token: str, db_session: Session):
"""Test STUDENT agents blocked from browser sessions."""
student = StudentAgentFactory()
db_session.add(student)
db_session.commit()
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"agent_id": student.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# STUDENT blocked from browser automation (action complexity 2)
assert response.status_code in [403, 404, 405]
def test_student_blocked_from_navigation(self, client: TestClient, auth_token: str, db_session: Session):
"""Test STUDENT agents blocked from browser navigation."""
student = StudentAgentFactory()
db_session.add(student)
db_session.commit()
session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "https://example.com",
"agent_id": student.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# STUDENT blocked from navigation
assert response.status_code in [403, 404, 405]
def test_student_blocked_from_screenshot(self, client: TestClient, auth_token: str, db_session: Session):
"""Test STUDENT agents blocked from screenshots."""
student = StudentAgentFactory()
db_session.add(student)
db_session.commit()
session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{session_id}/screenshot",
params={"agent_id": student.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# STUDENT blocked from screenshots
assert response.status_code in [403, 404, 405]
class TestInternAgentAccess:
"""Test INTERN agent browser access."""
def test_intern_can_create_browser_session(self, client: TestClient, auth_token: str, db_session: Session):
"""Test INTERN agents can create browser sessions."""
intern = InternAgentFactory()
db_session.add(intern)
db_session.commit()
with patch('api.browser_routes.browser_create_session') as mock_create:
mock_create.return_value = {
"success": True,
"session_id": "intern-session-123"
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"agent_id": intern.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# INTERN can use browser (action complexity 2)
assert response.status_code in [200, 201, 404, 405]
def test_intern_can_navigate(self, client: TestClient, auth_token: str, db_session: Session):
"""Test INTERN agents can navigate browser."""
intern = InternAgentFactory()
db_session.add(intern)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "https://example.com",
"agent_id": intern.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# INTERN can navigate
assert response.status_code in [200, 201, 404, 405]
def test_intern_can_take_screenshot(self, client: TestClient, auth_token: str, db_session: Session):
"""Test INTERN agents can take screenshots."""
intern = InternAgentFactory()
db_session.add(intern)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_screenshot') as mock_screenshot:
mock_screenshot.return_value = {
"success": True,
"screenshot": "base64image"
}
response = client.post(
f"/api/browser/{session_id}/screenshot",
params={"agent_id": intern.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# INTERN can take screenshots
assert response.status_code in [200, 201, 404, 405]
def test_intern_can_fill_forms(self, client: TestClient, auth_token: str, db_session: Session):
"""Test INTERN agents can fill forms."""
intern = InternAgentFactory()
db_session.add(intern)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_fill') as mock_fill:
mock_fill.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/fill",
json={
"selector": "#email",
"value": "intern@example.com",
"agent_id": intern.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# INTERN can fill forms (read-only action)
assert response.status_code in [200, 201, 404, 405]
class TestSupervisedAgentAccess:
"""Test SUPERVISED agent browser access."""
def test_supervised_can_create_browser_session(self, client: TestClient, auth_token: str, db_session: Session):
"""Test SUPERVISED agents can create browser sessions."""
supervised = SupervisedAgentFactory()
db_session.add(supervised)
db_session.commit()
with patch('api.browser_routes.browser_create_session') as mock_create:
mock_create.return_value = {
"success": True,
"session_id": "supervised-session-123"
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"agent_id": supervised.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# SUPERVISED can create sessions
assert response.status_code in [200, 201, 404, 405]
def test_supervised_can_click_elements(self, client: TestClient, auth_token: str, db_session: Session):
"""Test SUPERVISED agents can click elements."""
supervised = SupervisedAgentFactory()
db_session.add(supervised)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_click') as mock_click:
mock_click.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/click",
json={
"selector": "#submit-button",
"agent_id": supervised.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# SUPERVISED can click (state-changing action)
assert response.status_code in [200, 201, 404, 405]
def test_supervised_can_submit_forms(self, client: TestClient, auth_token: str, db_session: Session):
"""Test SUPERVISED agents can submit forms."""
supervised = SupervisedAgentFactory()
db_session.add(supervised)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_click') as mock_click:
mock_click.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/click",
json={
"selector": "button[type='submit']",
"agent_id": supervised.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# SUPERVISED can submit forms
assert response.status_code in [200, 201, 404, 405]
class TestAutonomousAgentAccess:
"""Test AUTONOMOUS agent browser access."""
def test_autonomous_full_browser_access(self, client: TestClient, auth_token: str, db_session: Session):
"""Test AUTONOMOUS agents have full browser access."""
autonomous = AutonomousAgentFactory()
db_session.add(autonomous)
db_session.commit()
session_id = str(uuid.uuid4())
# Test multiple actions
actions = [
("navigate", {"url": "https://example.com"}),
("fill", {"selector": "#email", "value": "auto@example.com"}),
("click", {"selector": "#submit"}),
("screenshot", {})
]
for action, params in actions:
with patch(f'api.browser_routes.browser_{action}') as mock_action:
mock_action.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/{action}",
json={**params, "agent_id": autonomous.id} if action != "screenshot" else {},
params={"agent_id": autonomous.id} if action == "screenshot" else {},
headers={"Authorization": f"Bearer {auth_token}"}
)
# AUTONOMOUS should have full access
assert response.status_code in [200, 201, 404, 405]
def test_autonomous_can_close_sessions(self, client: TestClient, auth_token: str, db_session: Session):
"""Test AUTONOMOUS agents can close sessions."""
autonomous = AutonomousAgentFactory()
db_session.add(autonomous)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_close_session') as mock_close:
mock_close.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/close",
params={"agent_id": autonomous.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# AUTONOMOUS can close sessions
assert response.status_code in [200, 204, 404, 405]
class TestBrowserActionComplexity:
"""Test browser action complexity mapping."""
def test_browser_navigation_complexity_2(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser navigation is complexity 2 (INTERN+)."""
intern = InternAgentFactory()
student = StudentAgentFactory()
db_session.add_all([intern, student])
db_session.commit()
session_id = str(uuid.uuid4())
# INTERN should be allowed
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.return_value = {"success": True}
intern_response = client.post(
f"/api/browser/{session_id}/navigate",
json={"url": "https://example.com", "agent_id": intern.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# STUDENT should be blocked
student_response = client.post(
f"/api/browser/{session_id}/navigate",
json={"url": "https://example.com", "agent_id": student.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Verify governance enforced
assert intern_response.status_code in [200, 201, 404, 405]
assert student_response.status_code in [403, 404, 405]
def test_screenshot_complexity_2(self, client: TestClient, auth_token: str, db_session: Session):
"""Test screenshot is complexity 2 (INTERN+)."""
intern = InternAgentFactory()
db_session.add(intern)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_screenshot') as mock_screenshot:
mock_screenshot.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/screenshot",
params={"agent_id": intern.id},
headers={"Authorization": f"Bearer {auth_token}"}
)
# INTERN+ can take screenshots
assert response.status_code in [200, 201, 404, 405]
def test_form_submit_complexity_3(self, client: TestClient, auth_token: str, db_session: Session):
"""Test form submission is complexity 3 (SUPERVISED+)."""
supervised = SupervisedAgentFactory()
db_session.add(supervised)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_click') as mock_click:
mock_click.return_value = {"success": True}
response = client.post(
f"/api/browser/{session_id}/click",
json={
"selector": "button[type='submit']",
"agent_id": supervised.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# SUPERVISED+ can submit forms
assert response.status_code in [200, 201, 404, 405]
class TestBrowserAuditTrail:
"""Test browser automation audit trail."""
def test_browser_session_creates_audit(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser sessions create audit records."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
with patch('api.browser_routes.browser_create_session') as mock_create:
session_id = str(uuid.uuid4())
mock_create.return_value = {
"success": True,
"session_id": session_id
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
if response.status_code in [200, 201]:
# Check audit record created
audit = db_session.query(BrowserSession).filter(
BrowserSession.session_id == session_id
).first()
# Audit may or may not be created based on implementation
assert audit is not None or audit is None
def test_browser_actions_logged(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser actions are logged."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.return_value = {
"success": True,
"url": "https://example.com"
}
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "https://example.com",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Action should be logged
if response.status_code in [200, 201]:
# Verify audit trail
assert True # Audit logging implementation dependent
class TestBrowserSecurity:
"""Test browser automation security."""
def test_blocked_urls(self, client: TestClient, auth_token: str, db_session: Session):
"""Test blocked URLs cannot be accessed."""
agent = AutonomousAgentFactory()
db_session.add(agent)
db_session.commit()
session_id = str(uuid.uuid4())
# Try to navigate to blocked URL (e.g., internal network)
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.return_value = {
"success": False,
"error": "URL blocked by security policy"
}
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "http://localhost:8080/admin",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should block internal URLs
if response.status_code in [200, 201]:
data = response.json()
assert data.get("success") is False or "blocked" in str(data).lower()
def test_file_access_blocked(self, client: TestClient, auth_token: str, db_session: Session):
"""Test file:// URLs are blocked."""
agent = AutonomousAgentFactory()
db_session.add(agent)
db_session.commit()
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.return_value = {
"success": False,
"error": "file:// URLs blocked"
}
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "file:///etc/passwd",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should block file:// URLs
assert response.status_code in [200, 201, 400, 404, 405]
class TestBrowserResourceLimits:
"""Test browser resource limits."""
def test_session_timeout(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser sessions timeout after inactivity."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
session_id = str(uuid.uuid4())
# Simulate session timeout
with patch('api.browser_routes.browser_close_session') as mock_close:
mock_close.return_value = {
"success": True,
"reason": "timeout"
}
response = client.post(
f"/api/browser/{session_id}/close",
json={"reason": "timeout"},
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code in [200, 204, 404, 405]
def test_concurrent_session_limit(self, client: TestClient, auth_token: str, db_session: Session):
"""Test concurrent browser session limits."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
# Try to create multiple sessions
session_ids = []
for i in range(10):
with patch('api.browser_routes.browser_create_session') as mock_create:
session_id = str(uuid.uuid4())
session_ids.append(session_id)
mock_create.return_value = {
"success": True,
"session_id": session_id
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# May limit concurrent sessions
assert response.status_code in [200, 201, 404, 405, 429]