| """ |
| 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 |
| }) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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.""" |
| |
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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_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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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()) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 404, 405] |
|
|
| if response.status_code == 200: |
| data = response.json() |
| |
| 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}"} |
| ) |
|
|
| |
| 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]: |
| |
| session = db_session.query(BrowserSession).filter( |
| BrowserSession.session_id == session_id |
| ).first() |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 404, 405] |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| 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}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 408, 500, 404, 405] |
|
|