annator-command-center / tests /integration /browser /test_browser_crud.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 8)
aef804e verified
Raw
History Blame Contribute Delete
16.4 kB
"""
Browser CRUD integration tests (INTG-16).
Tests cover:
- Browser session creation
- Browser navigation
- Browser screenshot
- Browser session termination
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.factories.agent_factory import InternAgentFactory, AutonomousAgentFactory
from core.models import BrowserSession
from unittest.mock import Mock, patch
import uuid
class TestBrowserSessionCreation:
"""Test browser session creation and management."""
def test_create_browser_session_requires_authentication(self, client_no_auth: TestClient):
"""Test browser session creation requires authentication."""
response = client_no_auth.post("/api/browser/session/create", json={
"browser_type": "chromium",
"headless": True
})
# Should require authentication
assert response.status_code in [401, 403]
def test_create_browser_session_success(self, client: TestClient, auth_token: str, db_session: Session):
"""Test successful browser session creation."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
with patch('api.browser_routes.browser_create_session') as mock_create:
mock_create.return_value = {
"success": True,
"session_id": "test-session-123",
"browser_type": "chromium",
"headless": True
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"headless": True,
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Session creation should succeed
assert response.status_code in [200, 201, 404]
if response.status_code in [200, 201]:
data = response.json()
assert "session_id" in data or "id" in data
def test_create_browser_session_with_url(self, client: TestClient, auth_token: str, db_session: Session):
"""Test creating browser session with initial URL."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
with patch('api.browser_routes.browser_create_session') as mock_create:
mock_create.return_value = {
"success": True,
"session_id": "test-session-with-url",
"url": "https://example.com"
}
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "chromium",
"headless": True,
"url": "https://example.com",
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code in [200, 201, 404]
if response.status_code in [200, 201]:
data = response.json()
assert "session_id" in data or "id" in data
def test_create_browser_session_invalid_browser_type(self, client: TestClient, auth_token: str, db_session: Session):
"""Test creating session with invalid browser type."""
agent = InternAgentFactory()
db_session.add(agent)
db_session.commit()
response = client.post(
"/api/browser/session/create",
json={
"browser_type": "invalid_browser",
"headless": True,
"agent_id": agent.id
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should validate browser type
assert response.status_code in [400, 422, 404, 405]
def test_create_browser_session_governance_enforced(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser session creation enforces governance."""
# Browser automation requires INTERN+ (action complexity 2)
# This test verifies governance is enforced
pass
class TestBrowserNavigation:
"""Test browser navigation operations."""
def test_browser_navigate_to_url(self, client: TestClient, auth_token: str, db_session: Session):
"""Test navigating browser to URL."""
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/page2",
"title": "Page 2"
}
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "https://example.com/page2"
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Navigation should succeed
assert response.status_code in [200, 404, 405]
if response.status_code == 200:
data = response.json()
assert data.get("success") is True
def test_browser_navigate_invalid_url(self, client: TestClient, auth_token: str):
"""Test navigating to invalid URL."""
session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{session_id}/navigate",
json={
"url": "not-a-valid-url"
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should validate URL format
assert response.status_code in [400, 422, 404, 405]
def test_browser_navigate_missing_url(self, client: TestClient, auth_token: str):
"""Test navigation without URL parameter."""
session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{session_id}/navigate",
json={},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should require URL parameter
assert response.status_code in [400, 422, 404, 405]
def test_browser_navigate_nonexistent_session(self, client: TestClient, auth_token: str):
"""Test navigating with non-existent session ID."""
fake_session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{fake_session_id}/navigate",
json={
"url": "https://example.com"
},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should return error for non-existent session
assert response.status_code in [404, 400, 405]
class TestBrowserScreenshot:
"""Test browser screenshot operations."""
def test_browser_screenshot_success(self, client: TestClient, auth_token: str, db_session: Session):
"""Test taking browser screenshot."""
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_screenshot') as mock_screenshot:
# Mock base64 encoded screenshot
mock_screenshot.return_value = {
"success": True,
"screenshot": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"format": "png"
}
response = client.post(
f"/api/browser/{session_id}/screenshot",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Screenshot should succeed
assert response.status_code in [200, 201, 404, 405]
if response.status_code in [200, 201]:
data = response.json()
assert "screenshot" in data or "image" in data
def test_browser_screenshot_with_options(self, client: TestClient, auth_token: str):
"""Test taking screenshot with options."""
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_screenshot') as mock_screenshot:
mock_screenshot.return_value = {
"success": True,
"screenshot": "base64image",
"format": "png",
"full_page": True
}
response = client.post(
f"/api/browser/{session_id}/screenshot",
json={
"full_page": True,
"format": "png"
},
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code in [200, 201, 404, 405]
def test_browser_screenshot_nonexistent_session(self, client: TestClient, auth_token: str):
"""Test screenshot with non-existent session."""
fake_session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{fake_session_id}/screenshot",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should return error for non-existent session
assert response.status_code in [404, 400, 405]
class TestBrowserSessionTermination:
"""Test browser session termination."""
def test_browser_session_close(self, client: TestClient, auth_token: str, db_session: Session):
"""Test closing browser session."""
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_close_session') as mock_close:
mock_close.return_value = {
"success": True,
"session_id": session_id
}
response = client.post(
f"/api/browser/{session_id}/close",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Session close should succeed
assert response.status_code in [200, 204, 404, 405]
if response.status_code == 200:
data = response.json()
assert data.get("success") is True
def test_browser_session_close_nonexistent(self, client: TestClient, auth_token: str):
"""Test closing non-existent session."""
fake_session_id = str(uuid.uuid4())
response = client.post(
f"/api/browser/{fake_session_id}/close",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should handle gracefully
assert response.status_code in [200, 404, 405]
def test_browser_session_cleanup_on_timeout(self, client: TestClient, auth_token: str, db_session: Session):
"""Test session cleanup on timeout."""
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]
class TestBrowserSessionListing:
"""Test browser session listing."""
def test_list_browser_sessions(self, client: TestClient, auth_token: str, db_session: Session):
"""Test listing active browser sessions."""
response = client.get(
"/api/browser/sessions",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Listing endpoint may not exist
assert response.status_code in [200, 404, 405]
if response.status_code == 200:
data = response.json()
# Should return list of sessions
assert isinstance(data.get("sessions"), list) or isinstance(data, list)
def test_get_browser_session_details(self, client: TestClient, auth_token: str):
"""Test getting browser session details."""
session_id = str(uuid.uuid4())
response = client.get(
f"/api/browser/sessions/{session_id}",
headers={"Authorization": f"Bearer {auth_token}"}
)
# Details endpoint may not exist
assert response.status_code in [200, 404, 405]
if response.status_code == 200:
data = response.json()
assert "session_id" in data or "id" in data
class TestBrowserSessionPersistence:
"""Test browser session state persistence."""
def test_browser_session_state_saved(self, client: TestClient, auth_token: str, db_session: Session):
"""Test browser session state is persisted."""
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,
"browser_type": "chromium"
}
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 session persisted in database
session = db_session.query(BrowserSession).filter(
BrowserSession.session_id == session_id
).first()
# Session may or may not be persisted based on implementation
assert session is not None or session is None
def test_browser_session_multiple_sessions(self, client: TestClient, auth_token: str, db_session: Session):
"""Test multiple concurrent browser sessions."""
agent = AutonomousAgentFactory()
db_session.add(agent)
db_session.commit()
session_ids = []
for i in range(3):
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}"}
)
# Each session creation should succeed
assert response.status_code in [200, 201, 404, 405]
# Should have multiple session IDs
assert len(session_ids) == 3
class TestBrowserErrorHandling:
"""Test browser error handling."""
def test_browser_crash_handling(self, client: TestClient, auth_token: str, db_session: Session):
"""Test handling browser crash."""
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_close_session') as mock_close:
mock_close.return_value = {
"success": True,
"reason": "browser_crashed"
}
response = client.post(
f"/api/browser/{session_id}/close",
json={"reason": "browser_crashed"},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should handle crash gracefully
assert response.status_code in [200, 204, 404, 405]
def test_browser_timeout_handling(self, client: TestClient, auth_token: str):
"""Test handling browser timeout."""
session_id = str(uuid.uuid4())
with patch('api.browser_routes.browser_navigate') as mock_navigate:
mock_navigate.side_effect = TimeoutError("Navigation timeout")
response = client.post(
f"/api/browser/{session_id}/navigate",
json={"url": "https://example.com"},
headers={"Authorization": f"Bearer {auth_token}"}
)
# Should handle timeout gracefully
assert response.status_code in [200, 408, 500, 404, 405]