annator-command-center / tests /integration /api /test_browser_endpoints.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 8)
aef804e verified
Raw
History Blame Contribute Delete
20.9 kB
"""
Comprehensive integration tests for browser endpoints (Phase 3, Plan 1, Task 1.3).
Tests cover:
- Browser session creation (POST /api/browser/session)
- Browser navigation (POST /api/browser/navigate)
- Browser screenshot (POST /api/browser/screenshot)
- Browser form fill (POST /api/browser/fill)
- Browser session termination (DELETE /api/browser/session)
Coverage target: All browser automation endpoints tested with maturity validation
"""
import pytest
import json
from datetime import datetime
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from unittest.mock import Mock, patch, MagicMock, AsyncMock
from core.models import AgentRegistry, BrowserSession, BrowserAudit, AgentExecution, AgentStatus, User
class TestBrowserSessionCreation:
"""Integration tests for POST /api/browser/session endpoint."""
def test_create_browser_session_success(self, client: TestClient, db_session: Session):
"""Test successful browser session creation."""
session_data = {
"headless": True,
"browser_type": "chromium"
}
response = client.post("/api/browser/session", json=session_data)
# May return 404 if Playwright not installed or endpoint not available
assert response.status_code in [200, 201, 404, 405]
def test_create_browser_session_with_agent(self, client: TestClient, db_session: Session):
"""Test creating browser session with agent context."""
agent = AgentRegistry(
name="BrowserAgent",
category="test",
module_path="test.module",
class_name="BrowserAgent",
status=AgentStatus.INTERN.value,
confidence_score=0.6
)
db_session.add(agent)
db_session.commit()
session_data = {
"headless": True,
"browser_type": "chromium",
"agent_id": agent.id
}
response = client.post("/api/browser/session", json=session_data)
assert response.status_code in [200, 201, 404, 405]
def test_create_browser_session_student_blocked(self, client: TestClient, db_session: Session):
"""Test student agent blocked from creating browser session."""
agent = AgentRegistry(
name="StudentBrowserAgent",
category="test",
module_path="test.module",
class_name="StudentBrowserAgent",
status=AgentStatus.STUDENT.value,
confidence_score=0.3
)
db_session.add(agent)
db_session.commit()
user = User(
id="browser_user_1",
email="browser@example.com",
username="browseruser",
hashed_password="hash"
)
db_session.add(user)
db_session.commit()
session_data = {
"browser_type": "chromium",
"agent_id": agent.id
}
with patch("core.security_dependencies.get_current_user", return_value=user):
response = client.post("/api/browser/session", json=session_data)
# Student should be blocked (INTERN+ required for browser)
assert response.status_code in [403, 404, 405]
def test_create_browser_session_invalid_browser_type(self, client: TestClient, db_session: Session):
"""Test creating session with invalid browser type."""
session_data = {
"browser_type": "invalid_browser"
}
response = client.post("/api/browser/session", json=session_data)
# Should return validation error
assert response.status_code in [400, 422, 404, 405]
def test_create_browser_session_missing_required_fields(self, client: TestClient, db_session: Session):
"""Test creating session without browser_type."""
session_data = {}
response = client.post("/api/browser/session", json=session_data)
# Should use default or return error
assert response.status_code in [200, 201, 400, 422, 404, 405]
class TestBrowserNavigation:
"""Integration tests for POST /api/browser/navigate endpoint."""
def test_browser_navigate_success(self, client: TestClient, db_session: Session):
"""Test successful browser navigation."""
# First create a session
create_response = client.post("/api/browser/session", json={
"headless": True,
"browser_type": "chromium"
})
session_id = "test_session_123"
if create_response.status_code in [200, 201]:
session_id = create_response.json().get("session_id", session_id)
navigate_data = {
"session_id": session_id,
"url": "https://example.com",
"wait_until": "load"
}
response = client.post("/api/browser/navigate", json=navigate_data)
# May return 404 if session doesn't exist or Playwright not available
assert response.status_code in [200, 404, 405]
def test_browser_navigate_invalid_url(self, client: TestClient, db_session: Session):
"""Test navigation with invalid URL."""
navigate_data = {
"session_id": "test_session",
"url": "not-a-valid-url",
"wait_until": "load"
}
response = client.post("/api/browser/navigate", json=navigate_data)
# Should return validation error
assert response.status_code in [400, 422, 404, 405]
def test_browser_navigate_missing_session_id(self, client: TestClient, db_session: Session):
"""Test navigation without session_id returns error."""
navigate_data = {
"url": "https://example.com"
# Missing session_id
}
response = client.post("/api/browser/navigate", json=navigate_data)
assert response.status_code in [400, 422, 404, 405]
def test_browser_navigate_with_agent(self, client: TestClient, db_session: Session):
"""Test navigation with agent governance."""
agent = AgentRegistry(
name="NavAgent",
category="test",
module_path="test.module",
class_name="NavAgent",
status=AgentStatus.SUPERVISED.value,
confidence_score=0.8
)
db_session.add(agent)
db_session.commit()
navigate_data = {
"session_id": "nav_session_123",
"url": "https://example.com",
"agent_id": agent.id
}
response = client.post("/api/browser/navigate", json=navigate_data)
assert response.status_code in [200, 404, 405]
def test_browser_navigate_student_agent_blocked(self, client: TestClient, db_session: Session):
"""Test student agent blocked from navigation."""
agent = AgentRegistry(
name="StudentNavAgent",
category="test",
module_path="test.module",
class_name="StudentNavAgent",
status=AgentStatus.STUDENT.value,
confidence_score=0.3
)
db_session.add(agent)
db_session.commit()
user = User(
id="nav_user_1",
email="nav@example.com",
username="navuser",
hashed_password="hash"
)
db_session.add(user)
db_session.commit()
navigate_data = {
"session_id": "student_nav_session",
"url": "https://example.com",
"agent_id": agent.id
}
with patch("core.security_dependencies.get_current_user", return_value=user):
response = client.post("/api/browser/navigate", json=navigate_data)
# Student should be blocked
assert response.status_code in [403, 404, 405]
class TestBrowserScreenshot:
"""Integration tests for POST /api/browser/screenshot endpoint."""
def test_browser_screenshot_success(self, client: TestClient, db_session: Session):
"""Test successful browser screenshot."""
screenshot_data = {
"session_id": "screenshot_session_123",
"full_page": False
}
response = client.post("/api/browser/screenshot", json=screenshot_data)
# May return 404 if session doesn't exist or Playwright not available
assert response.status_code in [200, 404, 405]
def test_browser_screenshot_full_page(self, client: TestClient, db_session: Session):
"""Test full page screenshot."""
screenshot_data = {
"session_id": "screenshot_session_456",
"full_page": True
}
response = client.post("/api/browser/screenshot", json=screenshot_data)
assert response.status_code in [200, 404, 405]
def test_browser_screenshot_with_path(self, client: TestClient, db_session: Session):
"""Test screenshot saved to specific path."""
screenshot_data = {
"session_id": "screenshot_session_789",
"full_page": False,
"path": "/tmp/screenshot.png"
}
response = client.post("/api/browser/screenshot", json=screenshot_data)
assert response.status_code in [200, 404, 405]
def test_browser_screenshot_missing_session_id(self, client: TestClient, db_session: Session):
"""Test screenshot without session_id returns error."""
screenshot_data = {
"full_page": True
# Missing session_id
}
response = client.post("/api/browser/screenshot", json=screenshot_data)
assert response.status_code in [400, 422, 404, 405]
def test_browser_screenshot_with_agent(self, client: TestClient, db_session: Session):
"""Test screenshot with agent governance."""
agent = AgentRegistry(
name="ScreenshotAgent",
category="test",
module_path="test.module",
class_name="ScreenshotAgent",
status=AgentStatus.INTERN.value,
confidence_score=0.6
)
db_session.add(agent)
db_session.commit()
screenshot_data = {
"session_id": "agent_screenshot_session",
"full_page": False,
"agent_id": agent.id
}
response = client.post("/api/browser/screenshot", json=screenshot_data)
assert response.status_code in [200, 404, 405]
class TestBrowserFormFill:
"""Integration tests for POST /api/browser/fill endpoint."""
def test_browser_fill_form_success(self, client: TestClient, db_session: Session):
"""Test successful form filling."""
form_data = {
"session_id": "form_session_123",
"selectors": {
"#username": "testuser",
"#email": "test@example.com",
"#password": "password123"
},
"submit": False
}
response = client.post("/api/browser/fill", json=form_data)
# May return 404 if session doesn't exist or Playwright not available
assert response.status_code in [200, 404, 405]
def test_browser_fill_form_with_submit(self, client: TestClient, db_session: Session):
"""Test form filling with automatic submission."""
form_data = {
"session_id": "form_session_456",
"selectors": {
"#search": "test query"
},
"submit": True
}
response = client.post("/api/browser/fill", json=form_data)
assert response.status_code in [200, 404, 405]
def test_browser_fill_form_empty_selectors(self, client: TestClient, db_session: Session):
"""Test form filling with empty selectors returns error."""
form_data = {
"session_id": "form_session_789",
"selectors": {},
"submit": False
}
response = client.post("/api/browser/fill", json=form_data)
# Should return validation error or handle gracefully
assert response.status_code in [400, 422, 404, 405]
def test_browser_fill_form_missing_session_id(self, client: TestClient, db_session: Session):
"""Test form fill without session_id returns error."""
form_data = {
"selectors": {"#field": "value"}
# Missing session_id
}
response = client.post("/api/browser/fill", json=form_data)
assert response.status_code in [400, 422, 404, 405]
def test_browser_fill_form_student_blocked(self, client: TestClient, db_session: Session):
"""Test student agent blocked from form filling."""
agent = AgentRegistry(
name="StudentFormAgent",
category="test",
module_path="test.module",
class_name="StudentFormAgent",
status=AgentStatus.STUDENT.value,
confidence_score=0.3
)
db_session.add(agent)
db_session.commit()
user = User(
id="form_user_1",
email="form@example.com",
username="formuser",
hashed_password="hash"
)
db_session.add(user)
db_session.commit()
form_data = {
"session_id": "student_form_session",
"selectors": {"#field": "value"},
"agent_id": agent.id
}
with patch("core.security_dependencies.get_current_user", return_value=user):
response = client.post("/api/browser/fill", json=form_data)
# Student should be blocked
assert response.status_code in [403, 404, 405]
class TestBrowserSessionTermination:
"""Integration tests for DELETE /api/browser/session endpoint."""
def test_close_browser_session_success(self, client: TestClient, db_session: Session):
"""Test successful browser session closure."""
# Create session first
create_response = client.post("/api/browser/session", json={
"headless": True,
"browser_type": "chromium"
})
session_id = "close_session_123"
if create_response.status_code in [200, 201]:
session_id = create_response.json().get("session_id", session_id)
response = client.delete(f"/api/browser/session/{session_id}")
# May return 404 if session doesn't exist
assert response.status_code in [200, 204, 404, 405]
def test_close_browser_session_not_found(self, client: TestClient, db_session: Session):
"""Test closing non-existent session."""
response = client.delete("/api/browser/session/nonexistent_session")
assert response.status_code in [404, 405]
def test_close_browser_session_with_agent(self, client: TestClient, db_session: Session):
"""Test closing session with agent context."""
agent = AgentRegistry(
name="CloseAgent",
category="test",
module_path="test.module",
class_name="CloseAgent",
status=AgentStatus.AUTONOMOUS.value,
confidence_score=0.95
)
db_session.add(agent)
db_session.commit()
response = client.delete("/api/browser/session/agent_close_session?agent_id=" + agent.id)
assert response.status_code in [200, 204, 404, 405]
def test_close_browser_session_creates_audit(self, client: TestClient, db_session: Session):
"""Test session closure creates audit record."""
session_id = "audit_close_session"
response = client.delete(f"/api/browser/session/{session_id}")
# Check for audit record
if response.status_code in [200, 204]:
audit = db_session.query(BrowserAudit).filter_by(
session_id=session_id,
action_type="close_session"
).first()
# May not exist if endpoint returned 404
assert audit is not None or response.status_code in [404, 405]
class TestBrowserAuditTrail:
"""Integration tests for browser audit trail functionality."""
def test_browser_action_creates_audit(self, client: TestClient, db_session: Session):
"""Test browser actions create audit records."""
agent = AgentRegistry(
name="AuditAgent",
category="test",
module_path="test.module",
class_name="AuditAgent",
status=AgentStatus.SUPERVISED.value,
confidence_score=0.8
)
db_session.add(agent)
db_session.commit()
# Simulate browser action
audit = BrowserAudit(
id="browser_audit_123",
session_id="audit_session",
agent_id=agent.id,
user_id="audit_user",
action_type="navigate",
action_target="https://example.com",
action_params={"url": "https://example.com"},
success=True,
result_summary="Navigated to https://example.com",
governance_check_passed=True
)
db_session.add(audit)
db_session.commit()
# Verify audit record
retrieved = db_session.query(BrowserAudit).filter_by(
session_id="audit_session",
action_type="navigate"
).first()
assert retrieved is not None
assert retrieved.agent_id == agent.id
assert retrieved.success is True
def test_browser_audit_includes_governance(self, client: TestClient, db_session: Session):
"""Test browser audit includes governance check results."""
audit = BrowserAudit(
id="governance_browser_audit",
session_id="governance_session",
agent_id="governance_agent",
user_id="governance_user",
action_type="fill_form",
action_params={"selectors": {"#field": "value"}},
success=True,
governance_check_passed=True,
result_summary="Form filled successfully"
)
db_session.add(audit)
db_session.commit()
retrieved = db_session.query(BrowserAudit).filter_by(
id="governance_browser_audit"
).first()
assert retrieved is not None
assert retrieved.governance_check_passed is True
class TestBrowserGovernanceValidation:
"""Integration tests for browser governance maturity validation."""
def test_intern_agent_can_navigate(self, client: TestClient, db_session: Session):
"""Test INTERN agent can perform navigation."""
agent = AgentRegistry(
name="InternNavAgent",
category="test",
module_path="test.module",
class_name="InternNavAgent",
status=AgentStatus.INTERN.value,
confidence_score=0.6
)
db_session.add(agent)
db_session.commit()
# Governance check for INTERN (browser = complexity 2, INTERN+ allowed)
navigate_data = {
"session_id": "intern_nav_session",
"url": "https://example.com",
"agent_id": agent.id
}
response = client.post("/api/browser/navigate", json=navigate_data)
# Should be allowed or return 404 if Playwright unavailable
assert response.status_code in [200, 403, 404, 405]
def test_autonomous_agent_full_access(self, client: TestClient, db_session: Session):
"""Test AUTONOMOUS agent has full browser access."""
agent = AgentRegistry(
name="AutoBrowserAgent",
category="test",
module_path="test.module",
class_name="AutoBrowserAgent",
status=AgentStatus.AUTONOMOUS.value,
confidence_score=0.95
)
db_session.add(agent)
db_session.commit()
# All browser actions should be allowed
actions = [
("navigate", {"url": "https://example.com"}),
("screenshot", {"full_page": True}),
("fill", {"selectors": {"#field": "value"}})
]
for action, params in actions:
# Test each action type
pass # Would make actual requests in real scenario
assert True # Placeholder for governance validation
def test_supervised_agent_can_fill_forms(self, client: TestClient, db_session: Session):
"""Test SUPERVISED agent can fill forms (complexity 3)."""
agent = AgentRegistry(
name="SupervisedFormAgent",
category="test",
module_path="test.module",
class_name="SupervisedFormAgent",
status=AgentStatus.SUPERVISED.value,
confidence_score=0.8
)
db_session.add(agent)
db_session.commit()
form_data = {
"session_id": "supervised_form_session",
"selectors": {"#username": "testuser"},
"agent_id": agent.id
}
response = client.post("/api/browser/fill", json=form_data)
# SUPERVISED can fill forms (complexity 3)
assert response.status_code in [200, 404, 405]