| """ |
| 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) |
| |
| 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) |
| |
| 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) |
| |
| 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) |
| |
| 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.""" |
| |
| 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) |
| |
| 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) |
| |
| 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" |
| |
| } |
| 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) |
| |
| 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) |
| |
| 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 |
| |
| } |
| 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) |
| |
| 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) |
| |
| 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"} |
| |
| } |
| 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) |
| |
| 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_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}") |
| |
| 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}") |
|
|
| |
| if response.status_code in [200, 204]: |
| audit = db_session.query(BrowserAudit).filter_by( |
| session_id=session_id, |
| action_type="close_session" |
| ).first() |
| |
| 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() |
|
|
| |
| 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() |
|
|
| |
| 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() |
|
|
| |
| navigate_data = { |
| "session_id": "intern_nav_session", |
| "url": "https://example.com", |
| "agent_id": agent.id |
| } |
| response = client.post("/api/browser/navigate", json=navigate_data) |
| |
| 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() |
|
|
| |
| actions = [ |
| ("navigate", {"url": "https://example.com"}), |
| ("screenshot", {"full_page": True}), |
| ("fill", {"selectors": {"#field": "value"}}) |
| ] |
|
|
| for action, params in actions: |
| |
| pass |
|
|
| assert True |
|
|
| 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) |
| |
| assert response.status_code in [200, 404, 405] |
|
|