| """ |
| Comprehensive test suite for atom_agent_endpoints.py |
| |
| Target: 80%+ coverage for core/atom_agent_endpoints.py (2,042 lines) |
| |
| Test Classes: |
| - TestAgentExecutionEndpoint: Agent execution with streaming, governance, error handling |
| - TestAgentStatusEndpoint: Session history, status retrieval |
| - TestAgentListEndpoint: Session listing, filtering |
| - TestAgentStopEndpoint: Session management |
| - TestAgentPauseResumeEndpoint: Not applicable (no pause/resume in this file) |
| - TestStreamValidation: Streaming response validation |
| - TestGovernanceIntegration: Maturity-based access control |
| |
| Coverage Strategy: |
| 1. Test all API endpoints (8 routes) |
| 2. Test intent classification and routing |
| 3. Test governance integration (agent maturity levels) |
| 4. Test streaming functionality |
| 5. Test error handling and edge cases |
| 6. Test session management |
| 7. Test retrieval endpoints |
| |
| Author: Phase 62-03 (Test Coverage 80%) |
| Created: 2026-02-20 |
| """ |
|
|
| import json |
| import pytest |
| from unittest.mock import Mock, AsyncMock, MagicMock, patch |
| from datetime import datetime |
| from fastapi import FastAPI |
| from fastapi.testclient import TestClient |
| from sqlalchemy.orm import Session |
|
|
| |
| from core.atom_agent_endpoints import router, ChatRequest, ChatMessage |
| from core.models import AgentRegistry, AgentExecution, Workspace |
|
|
|
|
| |
| |
| |
|
|
| @pytest.fixture |
| def app(): |
| """Create test FastAPI app with atom_agent router""" |
| app = FastAPI() |
| app.include_router(router) |
| return app |
|
|
|
|
| @pytest.fixture |
| def client(app): |
| """Create test client""" |
| return TestClient(app) |
|
|
|
|
| @pytest.fixture |
| def mock_student_agent(db_session): |
| """Create a STUDENT maturity agent""" |
| agent = AgentRegistry( |
| id="student_agent_001", |
| name="Student Agent", |
| description="Learning agent", |
| maturity_level="STUDENT", |
| confidence_score=0.3, |
| capabilities=[], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
| db_session.refresh(agent) |
| return agent |
|
|
|
|
| @pytest.fixture |
| def mock_intern_agent(db_session): |
| """Create an INTERN maturity agent""" |
| agent = AgentRegistry( |
| id="intern_agent_001", |
| name="Intern Agent", |
| description="Intern agent", |
| maturity_level="INTERN", |
| confidence_score=0.6, |
| capabilities=["streaming", "presentation"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
| db_session.refresh(agent) |
| return agent |
|
|
|
|
| @pytest.fixture |
| def mock_supervised_agent(db_session): |
| """Create a SUPERVISED maturity agent""" |
| agent = AgentRegistry( |
| id="supervised_agent_001", |
| name="Supervised Agent", |
| description="Supervised agent", |
| maturity_level="SUPERVISED", |
| confidence_score=0.8, |
| capabilities=["streaming", "presentation", "state_changes"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
| db_session.refresh(agent) |
| return agent |
|
|
|
|
| @pytest.fixture |
| def mock_autonomous_agent(db_session): |
| """Create an AUTONOMOUS maturity agent""" |
| agent = AgentRegistry( |
| id="autonomous_agent_001", |
| name="Autonomous Agent", |
| description="Fully autonomous agent", |
| maturity_level="AUTONOMOUS", |
| confidence_score=0.95, |
| capabilities=["streaming", "presentation", "state_changes", "deletions"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
| db_session.refresh(agent) |
| return agent |
|
|
|
|
| @pytest.fixture |
| def sample_chat_request(): |
| """Sample chat request""" |
| return { |
| "message": "Create a workflow for daily reports", |
| "user_id": "test_user_001", |
| "session_id": None, |
| "current_page": "/workflows", |
| "context": {}, |
| "conversation_history": [], |
| "agent_id": None, |
| "workspace_id": "default" |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_session_manager(): |
| """Mock session manager""" |
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock: |
| manager = Mock() |
| manager.create_session = Mock(return_value="session_test_001") |
| manager.get_session = Mock(return_value={"session_id": "session_test_001", "user_id": "test_user_001"}) |
| manager.list_user_sessions = Mock(return_value=[ |
| {"session_id": "session_001", "user_id": "test_user_001", "last_active": "2026-02-20T10:00:00Z", "metadata": {"title": "Test Session"}} |
| ]) |
| manager.update_session_activity = Mock() |
| mock.return_value = manager |
| yield manager |
|
|
|
|
| @pytest.fixture |
| def mock_chat_history_manager(): |
| """Mock chat history manager""" |
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock: |
| manager = AsyncMock() |
| manager.get_session_history = Mock(return_value=[ |
| {"id": "msg_001", "role": "user", "text": "Hello", "created_at": "2026-02-20T10:00:00Z"}, |
| {"id": "msg_002", "role": "assistant", "text": "Hi there!", "created_at": "2026-02-20T10:00:01Z"} |
| ]) |
| manager.save_message = Mock() |
| manager.add_message = Mock() |
| mock.return_value = manager |
| yield manager |
|
|
|
|
| @pytest.fixture |
| def mock_context_manager(): |
| """Mock context manager""" |
| with patch('core.atom_agent_endpoints.get_chat_context_manager') as mock: |
| manager = AsyncMock() |
| manager.resolve_reference = AsyncMock(return_value={"id": "wf_001", "name": "Daily Report"}) |
| mock.return_value = manager |
| yield manager |
|
|
|
|
| @pytest.fixture |
| def mock_ai_service(): |
| """Mock AI service""" |
| with patch('core.atom_agent_endpoints.ai_service') as mock: |
| mock.initialize_sessions = AsyncMock() |
| mock.call_openai_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "CREATE_WORKFLOW", "entities": {"description": "daily report workflow"}}' |
| }) |
| mock.call_anthropic_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "CREATE_WORKFLOW", "entities": {"description": "daily report workflow"}}' |
| }) |
| mock.call_deepseek_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "CREATE_WORKFLOW", "entities": {"description": "daily report workflow"}}' |
| }) |
| yield mock |
|
|
|
|
| |
| |
| |
|
|
| class TestAgentExecutionEndpoint: |
| """Test agent chat execution endpoint""" |
|
|
| def test_chat_endpoint_success_with_new_session(self, client, mock_session_manager, mock_chat_history_manager, mock_ai_service): |
| """Test successful chat request creates new session""" |
| mock_session_manager.create_session.return_value = "session_test_001" |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm') as mock_classify: |
| mock_classify.return_value = { |
| "intent": "CREATE_WORKFLOW", |
| "entities": {"description": "daily report workflow"} |
| } |
|
|
| with patch('core.atom_agent_endpoints.handle_create_workflow') as mock_create: |
| mock_create.return_value = { |
| "success": True, |
| "response": {"message": "Workflow created successfully"} |
| } |
|
|
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "Create a workflow for daily reports", |
| "user_id": "test_user_001", |
| "session_id": None |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "session_id" in data |
| assert "response" in data |
|
|
| def test_chat_endpoint_with_existing_session(self, client, mock_session_manager, mock_chat_history_manager, mock_ai_service): |
| """Test chat request with existing session""" |
| with patch('core.atom_agent_endpoints.classify_intent_with_llm', new=AsyncMock(return_value={ |
| "intent": "LIST_WORKFLOWS", |
| "entities": {} |
| })): |
| with patch('core.atom_agent_endpoints.handle_list_workflows', new=AsyncMock(return_value={ |
| "success": True, |
| "response": {"message": "Found 2 workflows"} |
| })): |
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "List my workflows", |
| "user_id": "test_user_001", |
| "session_id": "session_existing_001" |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
|
|
| def test_chat_endpoint_with_invalid_payload(self, client): |
| """Test chat endpoint with invalid request payload""" |
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "", |
| "user_id": "" |
| }) |
|
|
| |
| assert response.status_code in [200, 422] |
|
|
| def test_chat_endpoint_missing_required_fields(self, client): |
| """Test chat endpoint without required fields""" |
| response = client.post("/api/atom-agent/chat", json={ |
| "user_id": "test_user_001" |
| |
| }) |
|
|
| |
| assert response.status_code == 422 |
|
|
| def test_chat_endpoint_creates_session_on_invalid_session_id(self, client, mock_session_manager, mock_chat_history_manager, mock_ai_service): |
| """Test that new session is created when specified session doesn't exist""" |
| mock_session_manager.get_session.return_value = None |
| mock_session_manager.create_session.return_value = "session_new_001" |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm') as mock_classify: |
| mock_classify.return_value = { |
| "intent": "HELP", |
| "entities": {} |
| } |
|
|
| with patch('core.atom_agent_endpoints.handle_help_request') as mock_help: |
| mock_help.return_value = { |
| "success": True, |
| "response": {"message": "I can help you"} |
| } |
|
|
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "Help", |
| "user_id": "test_user_001", |
| "session_id": "nonexistent_session" |
| }) |
|
|
| assert response.status_code == 200 |
| |
| mock_session_manager.create_session.assert_called() |
|
|
| def test_chat_endpoint_saves_interaction(self, client, mock_session_manager, mock_chat_history_manager, mock_ai_service): |
| """Test that chat interaction is saved to history""" |
| with patch('core.atom_agent_endpoints.classify_intent_with_llm', new=AsyncMock(return_value={ |
| "intent": "CREATE_TASK", |
| "entities": {"title": "Test task"} |
| })): |
| with patch('core.atom_agent_endpoints.handle_task_intent', new=AsyncMock(return_value={ |
| "success": True, |
| "response": {"message": "Task created"} |
| })): |
| with patch('core.atom_agent_endpoints.save_chat_interaction', Mock()) as mock_save: |
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "Create a task", |
| "user_id": "test_user_001", |
| "session_id": "session_001" |
| }) |
|
|
| assert response.status_code == 200 |
| |
| |
|
|
| def test_chat_endpoint_handles_exception_gracefully(self, client, mock_session_manager, mock_chat_history_manager): |
| """Test chat endpoint handles internal exceptions""" |
| with patch('core.atom_agent_endpoints.classify_intent_with_llm', side_effect=Exception("LLM error")): |
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "Test message", |
| "user_id": "test_user_001" |
| }) |
|
|
| |
| assert response.status_code == 200 |
| data = response.json() |
| assert "error" in data or data.get("success") is False |
|
|
| def test_chat_endpoint_with_conversation_history(self, client, mock_session_manager, mock_chat_history_manager, mock_ai_service): |
| """Test chat endpoint includes conversation history in LLM call""" |
| mock_session_manager.create_session.return_value = "session_001" |
|
|
| history = [ |
| {"role": "user", "content": "Create a workflow"}, |
| {"role": "assistant", "content": "I'll create that for you"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm') as mock_classify: |
| mock_classify.return_value = { |
| "intent": "CREATE_WORKFLOW", |
| "entities": {"description": "daily report"} |
| } |
|
|
| with patch('core.atom_agent_endpoints.handle_create_workflow') as mock_create: |
| mock_create.return_value = { |
| "success": True, |
| "response": {"message": "Workflow created"} |
| } |
|
|
| response = client.post("/api/atom-agent/chat", json={ |
| "message": "Make it daily", |
| "user_id": "test_user_001", |
| "conversation_history": history |
| }) |
|
|
| assert response.status_code == 200 |
| |
| mock_classify.assert_called_once() |
|
|
|
|
| |
| |
| |
|
|
| class TestIntentClassification: |
| """Test intent classification from chat messages""" |
|
|
| @pytest.mark.asyncio |
| async def test_classify_intent_create_workflow(self): |
| """Test CREATE_WORKFLOW intent classification""" |
| from core.atom_agent_endpoints import classify_intent_with_llm |
|
|
| with patch('core.atom_agent_endpoints.ai_service') as mock_ai: |
| mock_ai.call_openai_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "CREATE_WORKFLOW", "entities": {"description": "test workflow"}}' |
| }) |
|
|
| result = await classify_intent_with_llm("Create a workflow for testing", []) |
|
|
| assert result["intent"] == "CREATE_WORKFLOW" |
| assert "description" in result["entities"] |
|
|
| @pytest.mark.asyncio |
| async def test_classify_intent_list_workflows(self): |
| """Test LIST_WORKFLOWS intent classification""" |
| from core.atom_agent_endpoints import classify_intent_with_llm |
|
|
| with patch('core.atom_agent_endpoints.ai_service') as mock_ai: |
| mock_ai.call_openai_api = AsyncMock() |
| mock_ai.call_openai_api.return_value = { |
| "success": True, |
| "response": '{"intent": "LIST_WORKFLOWS", "entities": {}}' |
| } |
|
|
| result = await classify_intent_with_llm("Show me all workflows", []) |
|
|
| assert result["intent"] == "LIST_WORKFLOWS" |
|
|
| @pytest.mark.asyncio |
| async def test_classify_intent_run_workflow(self): |
| """Test RUN_WORKFLOW intent classification""" |
| from core.atom_agent_endpoints import classify_intent_with_llm |
|
|
| with patch('core.atom_agent_endpoints.ai_service') as mock_ai: |
| mock_ai.call_openai_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "RUN_WORKFLOW", "entities": {"workflow_ref": "daily report"}}' |
| }) |
|
|
| result = await classify_intent_with_llm("Run the daily report workflow", []) |
|
|
| assert result["intent"] == "RUN_WORKFLOW" |
| assert "workflow_ref" in result["entities"] |
|
|
| @pytest.mark.asyncio |
| async def test_classify_intent_schedule_workflow(self): |
| """Test SCHEDULE_WORKFLOW intent classification with time expression""" |
| from core.atom_agent_endpoints import classify_intent_with_llm |
|
|
| with patch('core.atom_agent_endpoints.ai_service') as mock_ai: |
| mock_ai.call_openai_api = AsyncMock(return_value={ |
| "success": True, |
| "response": '{"intent": "SCHEDULE_WORKFLOW", "entities": {"workflow_ref": "backup", "time_expression": "every day at 5pm"}}' |
| }) |
|
|
| result = await classify_intent_with_llm("Schedule the backup workflow every day at 5pm", []) |
|
|
| assert result["intent"] == "SCHEDULE_WORKFLOW" |
| assert "time_expression" in result["entities"] |
|
|
| @pytest.mark.asyncio |
| async def test_classify_intent_fallback_to_regex(self): |
| """Test fallback intent classification when LLM fails""" |
| from core.atom_agent_endpoints import classify_intent_with_llm |
|
|
| with patch('core.atom_agent_endpoints.ai_service') as mock_ai: |
| with patch('core.atom_agent_endpoints.get_byok_manager') as mock_byok: |
| mock_byok.return_value.get_optimal_provider.side_effect = Exception("BYOK error") |
|
|
| result = await classify_intent_with_llm("create a workflow", []) |
|
|
| |
| assert result["intent"] == "CREATE_WORKFLOW" |
|
|
| def test_fallback_intent_classification_workflow_keywords(self): |
| """Test regex-based fallback for workflow intents""" |
| from core.atom_agent_endpoints import fallback_intent_classification |
|
|
| result = fallback_intent_classification("create a workflow") |
| assert result["intent"] == "CREATE_WORKFLOW" |
|
|
| result = fallback_intent_classification("list all workflows") |
| assert result["intent"] == "LIST_WORKFLOWS" |
|
|
| result = fallback_intent_classification("run the daily report workflow") |
| assert result["intent"] == "RUN_WORKFLOW" |
|
|
| result = fallback_intent_classification("schedule workflow to run daily") |
| assert result["intent"] == "SCHEDULE_WORKFLOW" |
|
|
| def test_fallback_intent_classification_calendar_keywords(self): |
| """Test regex-based fallback for calendar intents""" |
| from core.atom_agent_endpoints import fallback_intent_classification |
|
|
| result = fallback_intent_classification("schedule a meeting") |
| assert result["intent"] == "CREATE_EVENT" |
|
|
| result = fallback_intent_classification("resolve calendar conflicts") |
| assert result["intent"] == "RESOLVE_CONFLICTS" |
|
|
| result = fallback_intent_classification("show my calendar") |
| assert result["intent"] == "LIST_EVENTS" |
|
|
| def test_fallback_intent_classification_task_keywords(self): |
| """Test regex-based fallback for task intents""" |
| from core.atom_agent_endpoints import fallback_intent_classification |
|
|
| result = fallback_intent_classification("create a task") |
| assert result["intent"] == "CREATE_TASK" |
|
|
| result = fallback_intent_classification("list my tasks") |
| assert result["intent"] == "LIST_TASKS" |
|
|
| def test_fallback_intent_classification_unknown(self): |
| """Test fallback returns UNKNOWN for unrecognized messages""" |
| from core.atom_agent_endpoints import fallback_intent_classification |
|
|
| result = fallback_intent_classification("xyzabc random text") |
| assert result["intent"] == "UNKNOWN" |
|
|
|
|
| |
| |
| |
|
|
| class TestSessionManagement: |
| """Test session listing, creation, and history retrieval""" |
|
|
| def test_list_sessions_success(self, client, mock_session_manager): |
| """Test listing all user sessions""" |
| response = client.get("/api/atom-agent/sessions?user_id=test_user_001") |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "sessions" in data |
| assert isinstance(data["sessions"], list) |
|
|
| def test_list_sessions_with_limit(self, client, mock_session_manager): |
| """Test listing sessions with custom limit""" |
| response = client.get("/api/atom-agent/sessions?user_id=test_user_001&limit=10") |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
|
|
| def test_create_new_session_success(self, client, mock_session_manager): |
| """Test creating a new chat session""" |
| mock_session_manager.create_session = Mock(return_value="session_new_001") |
|
|
| response = client.post("/api/atom-agent/sessions", json={ |
| "user_id": "test_user_001" |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "session_id" in data |
|
|
| def test_get_session_history_success(self, client, mock_session_manager, mock_chat_history_manager): |
| """Test retrieving session history""" |
| response = client.get("/api/atom-agent/sessions/session_001/history") |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "messages" in data |
| assert "count" in data |
|
|
| def test_get_session_history_nonexistent_session(self, client, mock_session_manager, mock_chat_history_manager): |
| """Test session history for non-existent session""" |
| mock_session_manager.get_session.return_value = None |
|
|
| response = client.get("/api/atom-agent/sessions/nonexistent_session/history") |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "error" in data |
|
|
| def test_get_session_history_metadata_parsing(self, client, mock_session_manager, mock_chat_history_manager): |
| """Test metadata parsing in session history""" |
| |
| mock_chat_history_manager.get_session_history.return_value = [ |
| { |
| "id": "msg_001", |
| "role": "assistant", |
| "text": "Response", |
| "created_at": "2026-02-20T10:00:00Z", |
| "metadata": '{"intent": "CREATE_WORKFLOW", "workflow_id": "wf_001"}' |
| } |
| ] |
|
|
| response = client.get("/api/atom-agent/sessions/session_001/history") |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| |
| if data["messages"]: |
| assert isinstance(data["messages"][0].get("metadata"), dict) |
|
|
| def test_get_session_history_invalid_metadata(self, client, mock_session_manager, mock_chat_history_manager): |
| """Test handling of invalid JSON metadata""" |
| |
| mock_chat_history_manager.get_session_history.return_value = [ |
| { |
| "id": "msg_001", |
| "role": "assistant", |
| "text": "Response", |
| "created_at": "2026-02-20T10:00:00Z", |
| "metadata": "invalid json {" |
| } |
| ] |
|
|
| response = client.get("/api/atom-agent/sessions/session_001/history") |
|
|
| |
| assert response.status_code == 200 |
|
|
|
|
| |
| |
| |
|
|
| class TestWorkflowHandlers: |
| """Test workflow-related intent handlers""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_create_workflow_success(self): |
| """Test successful workflow creation""" |
| from core.atom_agent_endpoints import handle_create_workflow, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_orchestrator') as mock_orch: |
| mock_orch.return_value.generate_dynamic_workflow = AsyncMock(return_value={ |
| "id": "wf_001", |
| "name": "Daily Report Workflow", |
| "nodes": [{"id": "node_1"}], |
| "connections": [], |
| "workflow_id": "wf_001" |
| }) |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=[]): |
| with patch('core.atom_agent_endpoints.save_workflows') as mock_save: |
| request = ChatRequest( |
| message="Create a daily report workflow", |
| user_id="test_user" |
| ) |
| result = await handle_create_workflow(request, {"description": "daily report"}) |
|
|
| assert result["success"] is True |
| assert "workflow_id" in result["response"] |
| assert "nodes" in result["response"] |
|
|
| @pytest.mark.asyncio |
| async def test_handle_create_workflow_failure(self): |
| """Test workflow creation failure""" |
| from core.atom_agent_endpoints import handle_create_workflow, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_orchestrator') as mock_orch: |
| mock_orch.return_value.generate_dynamic_workflow = AsyncMock(return_value=None) |
|
|
| request = ChatRequest( |
| message="Create something unclear", |
| user_id="test_user" |
| ) |
| result = await handle_create_workflow(request, {"description": "unclear"}) |
|
|
| assert result["success"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_handle_list_workflows(self): |
| """Test listing workflows""" |
| from core.atom_agent_endpoints import handle_list_workflows, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.load_workflows') as mock_load: |
| mock_load.return_value = [ |
| {"name": "Workflow 1", "workflow_id": "wf_001"}, |
| {"name": "Workflow 2", "workflow_id": "wf_002"} |
| ] |
|
|
| request = ChatRequest(message="List workflows", user_id="test_user") |
| result = await handle_list_workflows(request) |
|
|
| assert result["success"] is True |
| assert "Found 2 workflows" in result["response"]["message"] |
|
|
| @pytest.mark.asyncio |
| async def test_handle_list_workflows_empty(self): |
| """Test listing workflows when none exist""" |
| from core.atom_agent_endpoints import handle_list_workflows, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.load_workflows') as mock_load: |
| mock_load.return_value = [] |
|
|
| request = ChatRequest(message="List workflows", user_id="test_user") |
| result = await handle_list_workflows(request) |
|
|
| assert result["success"] is True |
| assert "No workflows found" in result["response"]["message"] |
|
|
| @pytest.mark.asyncio |
| async def test_handle_run_workflow_success(self): |
| """Test running a workflow successfully""" |
| from core.atom_agent_endpoints import handle_run_workflow, ChatRequest |
|
|
| mock_workflows = [ |
| {"name": "Daily Report", "workflow_id": "wf_001", "id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine') as mock_engine_class: |
| mock_engine = Mock() |
| mock_engine.execute_workflow_definition = AsyncMock(return_value={"status": "completed"}) |
| mock_engine_class.return_value = mock_engine |
|
|
| request = ChatRequest(message="Run daily report", user_id="test_user") |
| result = await handle_run_workflow(request, {"workflow_ref": "Daily Report"}) |
|
|
| assert result["success"] is True |
| |
| assert "started" in result["response"]["message"].lower() or "execution" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_run_workflow_not_found(self): |
| """Test running a non-existent workflow""" |
| from core.atom_agent_endpoints import handle_run_workflow, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=[]): |
| request = ChatRequest(message="Run nonexistent", user_id="test_user") |
| result = await handle_run_workflow(request, {"workflow_ref": "nonexistent"}) |
|
|
| assert result["success"] is False |
| assert "not found" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_run_workflow_missing_ref(self): |
| """Test running workflow without specifying which one""" |
| from core.atom_agent_endpoints import handle_run_workflow, ChatRequest |
|
|
| request = ChatRequest(message="Run a workflow", user_id="test_user") |
| result = await handle_run_workflow(request, {}) |
|
|
| assert result["success"] is False |
| assert "specify" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_schedule_workflow_success(self): |
| """Test scheduling a workflow successfully""" |
| from core.atom_agent_endpoints import handle_schedule_workflow, ChatRequest |
|
|
| mock_workflows = [ |
| {"name": "Daily Report", "workflow_id": "wf_001", "id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.parse_time_expression') as mock_parse: |
| mock_parse = AsyncMock() |
| mock_parse.return_value = { |
| "schedule_type": "cron", |
| "cron_expression": "0 9 * * 1-5", |
| "human_readable": "every weekday at 9am" |
| } |
|
|
| with patch('core.atom_agent_endpoints.workflow_scheduler') as mock_scheduler: |
| mock_scheduler.schedule_workflow_cron = Mock() |
|
|
| request = ChatRequest(message="Schedule daily report", user_id="test_user") |
| result = await handle_schedule_workflow(request, { |
| "workflow_ref": "Daily Report", |
| "time_expression": "every weekday at 9am" |
| }) |
|
|
| assert result["success"] is True |
| assert "scheduled" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_cancel_schedule_success(self): |
| """Test cancelling a workflow schedule""" |
| from core.atom_agent_endpoints import handle_cancel_schedule, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.workflow_scheduler') as mock_scheduler: |
| mock_scheduler.remove_job = Mock(return_value=True) |
|
|
| request = ChatRequest(message="Cancel schedule", user_id="test_user") |
| result = await handle_cancel_schedule(request, {"schedule_id": "job_001"}) |
|
|
| assert result["success"] is True |
| assert "cancelled" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_get_status(self): |
| """Test getting workflow status""" |
| from core.atom_agent_endpoints import handle_get_status, ChatRequest |
|
|
| request = ChatRequest(message="What's the status", user_id="test_user") |
| result = await handle_get_status(request, {}) |
|
|
| assert result["success"] is True |
| assert "status" in result["response"]["message"].lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestTaskCalendarEmailHandlers: |
| """Test task, calendar, and email intent handlers""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_create_task(self): |
| """Test creating a task""" |
| from core.atom_agent_endpoints import handle_task_intent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.create_task', new=AsyncMock(return_value={ |
| "task_id": "task_001", |
| "title": "Test Task" |
| })): |
| request = ChatRequest(message="Create a test task", user_id="test_user") |
| result = await handle_task_intent("CREATE_TASK", {"title": "Test Task"}, request) |
|
|
| assert result["success"] is True |
| assert "created" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_list_tasks(self): |
| """Test listing tasks""" |
| from core.atom_agent_endpoints import handle_task_intent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_tasks', new=AsyncMock(return_value={ |
| "tasks": [{"id": "task_001", "title": "Task 1"}] |
| })): |
| request = ChatRequest(message="List tasks", user_id="test_user") |
| result = await handle_task_intent("LIST_TASKS", {}, request) |
|
|
| assert result["success"] is True |
| assert "tasks" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_create_event(self): |
| """Test creating a calendar event""" |
| from core.atom_agent_endpoints import handle_create_event, ChatRequest |
|
|
| request = ChatRequest(message="Schedule meeting", user_id="test_user") |
| result = await handle_create_event(request, {"summary": "Team Meeting"}) |
|
|
| assert result["success"] is True |
| assert "calendar event" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_list_events(self): |
| """Test listing calendar events""" |
| from core.atom_agent_endpoints import handle_list_events, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.GoogleCalendarService') as mock_cal: |
| mock_service = AsyncMock() |
| mock_service.get_events = AsyncMock(return_value=[ |
| {"summary": "Meeting 1", "start": {"dateTime": "2026-02-20T10:00:00Z"}} |
| ]) |
| mock_cal.return_value = mock_service |
|
|
| request = ChatRequest(message="List events", user_id="test_user") |
| result = await handle_list_events(request, {}) |
|
|
| assert result["success"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_handle_send_email(self): |
| """Test preparing an email""" |
| from core.atom_agent_endpoints import handle_send_email, ChatRequest |
|
|
| request = ChatRequest(message="Send email", user_id="test_user") |
| result = await handle_send_email(request, {"recipient": "test@example.com", "subject": "Test"}) |
|
|
| assert result["success"] is True |
| assert "email" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_search_emails(self): |
| """Test searching emails""" |
| from core.atom_agent_endpoints import handle_search_emails, ChatRequest |
|
|
| request = ChatRequest(message="Search emails", user_id="test_user") |
| result = await handle_search_emails(request, {"query": "project update"}) |
|
|
| assert result["success"] is True |
|
|
|
|
| |
| |
| |
|
|
| class TestStreamValidation: |
| """Test streaming chat endpoint validation""" |
|
|
| def test_stream_endpoint_exists(self, client): |
| """Test that streaming endpoint is registered""" |
| |
| |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/chat/stream" in r.path] |
| assert len(routes) > 0, "Streaming endpoint should exist" |
|
|
| def test_chat_endpoint_request_validation(self): |
| """Test ChatRequest model validation""" |
| |
| request = ChatRequest( |
| message="Test message", |
| user_id="test_user", |
| session_id="session_001", |
| current_page="/test", |
| context={"key": "value"}, |
| conversation_history=[], |
| agent_id="agent_001" |
| ) |
| assert request.message == "Test message" |
| assert request.user_id == "test_user" |
|
|
| |
| minimal = ChatRequest( |
| message="Test", |
| user_id="user_001" |
| ) |
| assert minimal.message == "Test" |
| assert minimal.session_id is None |
|
|
| def test_chat_message_model_validation(self): |
| """Test ChatMessage model validation""" |
| msg = ChatMessage(role="user", content="Hello") |
| assert msg.role == "user" |
| assert msg.content == "Hello" |
|
|
| |
| asst = ChatMessage(role="assistant", content="Hi there!") |
| assert asst.role == "assistant" |
|
|
|
|
| |
| |
| |
|
|
| class TestGovernanceIntegration: |
| """Test governance integration with agent maturity levels""" |
|
|
| def test_student_agent_governance_check(self, db_session): |
| """Test that STUDENT agents are checked for permissions""" |
| from core.agent_governance_service import AgentGovernanceService |
|
|
| agent = AgentRegistry( |
| id="student_test", |
| name="Student Test", |
| maturity_level="STUDENT", |
| confidence_score=0.3, |
| capabilities=[], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| governance = AgentGovernanceService(db_session) |
| check = governance.can_perform_action( |
| agent_id="student_test", |
| action_type="stream_chat" |
| ) |
|
|
| |
| assert "allowed" in check |
| |
| |
|
|
| def test_intern_agent_governance_check(self, db_session): |
| """Test that INTERN agents require approval for certain actions""" |
| from core.agent_governance_service import AgentGovernanceService |
|
|
| agent = AgentRegistry( |
| id="intern_test", |
| name="Intern Test", |
| maturity_level="INTERN", |
| confidence_score=0.6, |
| capabilities=["streaming"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| governance = AgentGovernanceService(db_session) |
| check = governance.can_perform_action( |
| agent_id="intern_test", |
| action_type="stream_chat" |
| ) |
|
|
| assert "allowed" in check |
|
|
| def test_autonomous_agent_governance_check(self, db_session): |
| """Test that AUTONOMOUS agents have full access""" |
| from core.agent_governance_service import AgentGovernanceService |
|
|
| agent = AgentRegistry( |
| id="autonomous_test", |
| name="Autonomous Test", |
| maturity_level="AUTONOMOUS", |
| confidence_score=0.95, |
| capabilities=["streaming", "state_changes", "deletions"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| governance = AgentGovernanceService(db_session) |
| check = governance.can_perform_action( |
| agent_id="autonomous_test", |
| action_type="stream_chat" |
| ) |
|
|
| |
| assert check["allowed"] is True |
|
|
| def test_governance_caching(self, db_session): |
| """Test that governance checks use cache for performance""" |
| from core.agent_governance_service import AgentGovernanceService |
| import time |
|
|
| agent = AgentRegistry( |
| id="cache_test", |
| name="Cache Test", |
| maturity_level="AUTONOMOUS", |
| confidence_score=0.95, |
| capabilities=["streaming"], |
| workspace_id="default" |
| ) |
| db_session.add(agent) |
| db_session.commit() |
|
|
| governance = AgentGovernanceService(db_session) |
|
|
| |
| start1 = time.time() |
| check1 = governance.can_perform_action( |
| agent_id="cache_test", |
| action_type="stream_chat" |
| ) |
| duration1 = time.time() - start1 |
|
|
| |
| start2 = time.time() |
| check2 = governance.can_perform_action( |
| agent_id="cache_test", |
| action_type="stream_chat" |
| ) |
| duration2 = time.time() - start2 |
|
|
| assert check1 == check2 |
| |
|
|
|
|
| |
| |
| |
|
|
| class TestHybridRetrievalEndpoints: |
| """Test hybrid and baseline retrieval endpoints""" |
|
|
| def test_retrieve_hybrid_endpoint_exists(self): |
| """Test that hybrid retrieval endpoint exists""" |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/retrieve-hybrid" in r.path] |
| assert len(routes) > 0 |
|
|
| def test_retrieve_baseline_endpoint_exists(self): |
| """Test that baseline retrieval endpoint exists""" |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/retrieve-baseline" in r.path] |
| assert len(routes) > 0 |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_hybrid_success(self, client): |
| """Test successful hybrid retrieval""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_hybrid = AsyncMock(return_value=[ |
| ("ep_001", 0.95, "rerank"), |
| ("ep_002", 0.87, "rerank") |
| ]) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-hybrid", json={ |
| "query": "test query", |
| "coarse_top_k": 100, |
| "rerank_top_k": 50, |
| "use_reranking": True |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "results" in data |
| assert len(data["results"]) == 2 |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_baseline_success(self, client): |
| """Test successful baseline retrieval""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_baseline = AsyncMock(return_value=[ |
| ("ep_001", 0.85), |
| ("ep_002", 0.78) |
| ]) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-baseline", json={ |
| "query": "test query", |
| "top_k": 50 |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "results" in data |
| assert len(data["results"]) == 2 |
|
|
|
|
| |
| |
| |
|
|
| class TestExecuteGeneratedWorkflow: |
| """Test execute-generated workflow endpoint""" |
|
|
| def test_execute_generated_success(self, client): |
| """Test successful execution of generated workflow""" |
| mock_workflows = [ |
| {"id": "wf_001", "name": "Generated Workflow", "workflow_id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine') as mock_engine_class: |
| mock_engine = Mock() |
| mock_engine.execute_workflow_definition = AsyncMock(return_value={ |
| "status": "completed", |
| "output": "Workflow executed successfully" |
| }) |
| mock_engine_class.return_value = mock_engine |
|
|
| response = client.post("/api/atom-agent/execute-generated", json={ |
| "workflow_id": "wf_001", |
| "input_data": {"param1": "value1"} |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert "execution_id" in data |
| assert data["status"] == "completed" |
|
|
| def test_execute_generated_not_found(self, client): |
| """Test executing non-existent workflow""" |
| with patch('core.atom_agent_endpoints.load_workflows', return_value=[]): |
| response = client.post("/api/atom-agent/execute-generated", json={ |
| "workflow_id": "nonexistent_wf", |
| "input_data": {} |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "not found" in data["error"].lower() |
|
|
| def test_execute_generated_automation_engine_unavailable(self, client): |
| """Test execution when AutomationEngine is not available""" |
| mock_workflows = [ |
| {"id": "wf_001", "name": "Generated Workflow", "workflow_id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine', None): |
| response = client.post("/api/atom-agent/execute-generated", json={ |
| "workflow_id": "wf_001", |
| "input_data": {} |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "not available" in data["error"].lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestSystemAndSearchHandlers: |
| """Test system status and platform search handlers""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_system_status(self): |
| """Test system status handler""" |
| from core.atom_agent_endpoints import handle_system_status, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.SystemStatus') as mock_status: |
| mock_status.get_overall_status = Mock(return_value="healthy") |
| mock_status.get_system_info = Mock(return_value={ |
| "platform": {"system": "Linux"} |
| }) |
| mock_status.get_resource_usage = Mock(return_value={ |
| "cpu": {"percent": 45.2}, |
| "memory": {"percent": 62.8} |
| }) |
| mock_status.get_service_status = Mock(return_value={ |
| "database": {"status": "healthy"}, |
| "redis": {"status": "healthy"} |
| }) |
|
|
| request = ChatRequest(message="System status", user_id="test_user") |
| result = await handle_system_status(request) |
|
|
| assert result["success"] is True |
| assert "system status" in result["response"]["message"].lower() |
| assert "services" in result["response"]["data"] |
|
|
| @pytest.mark.asyncio |
| async def test_handle_platform_search(self): |
| """Test platform search handler""" |
| from core.atom_agent_endpoints import handle_platform_search, ChatRequest, SearchRequest |
|
|
| with patch('core.atom_agent_endpoints.unified_hybrid_search') as mock_search: |
| mock_response = Mock() |
| mock_response.success = True |
| mock_response.results = [ |
| Mock(text="Document 1 content", metadata={"type": "document"}) |
| ] |
| mock_response.total_count = 1 |
| mock_search = AsyncMock(return_value=mock_response) |
|
|
| with patch('core.atom_agent_endpoints.unified_hybrid_search', return_value=mock_response): |
| request = ChatRequest(message="Search for documents", user_id="test_user") |
| result = await handle_platform_search(request, {"query": "test query"}) |
|
|
| assert result["success"] is True |
| assert "results" in result["response"]["data"] |
|
|
| @pytest.mark.asyncio |
| async def test_handle_automation_insights(self): |
| """Test automation insights handler""" |
| from core.atom_agent_endpoints import handle_automation_insights, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_insight_manager') as mock_insight: |
| mock_insight_mgr = Mock() |
| mock_insight_mgr.generate_all_insights = Mock(return_value=[ |
| {"workflow_id": "wf_001", "drift_score": 0.3} |
| ]) |
| mock_insight.return_value = mock_insight_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_behavior_analyzer') as mock_behavior: |
| mock_analyzer = Mock() |
| mock_analyzer.detect_patterns = Mock(return_value=[]) |
| mock_behavior.return_value = mock_analyzer |
|
|
| request = ChatRequest(message="Show insights", user_id="test_user") |
| result = await handle_automation_insights(request) |
|
|
| assert result["success"] is True |
| assert "automation health" in result["response"]["message"].lower() or "workflows" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_knowledge_query(self): |
| """Test knowledge query handler""" |
| from core.atom_agent_endpoints import handle_knowledge_query, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_knowledge_query_manager') as mock_kq: |
| mock_mgr = Mock() |
| mock_mgr.answer_query = AsyncMock(return_value={ |
| "answer": "The answer to your question", |
| "relevant_facts": ["Fact 1", "Fact 2"] |
| }) |
| mock_kq.return_value = mock_mgr |
|
|
| request = ChatRequest(message="Who worked on project X", user_id="test_user") |
| result = await handle_knowledge_query(request, {"query": "Who worked on project X"}) |
|
|
| assert result["success"] is True |
| assert "answer" in result["response"]["message"] |
|
|
|
|
| |
| |
| |
|
|
| class TestErrorHandlingAndEdgeCases: |
| """Test error handling and edge cases""" |
|
|
| @pytest.mark.asyncio |
| async def test_chat_with_empty_message(self): |
| """Test chat with empty message""" |
| from core.atom_agent_endpoints import chat_with_agent, ChatRequest |
|
|
| request = ChatRequest(message="", user_id="test_user") |
| result = await chat_with_agent(request) |
|
|
| |
| assert result is not None |
|
|
| @pytest.mark.asyncio |
| async def test_chat_with_very_long_message(self): |
| """Test chat with very long message""" |
| from core.atom_agent_endpoints import chat_with_agent, ChatRequest |
|
|
| long_message = "test " * 10000 |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm', new=AsyncMock(return_value={ |
| "intent": "UNKNOWN", |
| "entities": {} |
| })): |
| with patch('core.atom_agent_endpoints.handle_help_request', return_value={ |
| "success": True, |
| "response": {"message": "Help"} |
| }): |
| request = ChatRequest(message=long_message, user_id="test_user") |
| result = await chat_with_agent(request) |
|
|
| assert result is not None |
|
|
| @pytest.mark.asyncio |
| async def test_handler_exception_handling(self): |
| """Test that handler exceptions are caught""" |
| from core.atom_agent_endpoints import handle_create_workflow, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_orchestrator') as mock_orch: |
| mock_orch.return_value.generate_dynamic_workflow = AsyncMock( |
| side_effect=Exception("Orchestrator error") |
| ) |
|
|
| request = ChatRequest(message="Create workflow", user_id="test_user") |
| result = await handle_create_workflow(request, {"description": "test"}) |
|
|
| |
| assert result["success"] is False |
| assert "error" in result["response"]["message"].lower() or "failed" in result["response"]["message"].lower() |
|
|
| def test_session_manager_exception_handling(self, client): |
| """Test session manager exception handling""" |
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock: |
| mock.side_effect = Exception("Session manager error") |
|
|
| response = client.get("/api/atom-agent/sessions?user_id=test_user") |
|
|
| |
| assert response.status_code == 200 |
| data = response.json() |
| assert "error" in data |
|
|
| def test_chat_history_manager_exception_handling(self, client): |
| """Test chat history manager exception handling""" |
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_session: |
| mock_session.return_value.create_session = Mock(return_value="session_001") |
| mock_session.return_value.get_session = Mock(return_value={"session_id": "session_001"}) |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_history: |
| mock_history.side_effect = Exception("History manager error") |
|
|
| response = client.get("/api/atom-agent/sessions/session_001/history") |
|
|
| |
| assert response.status_code == 200 |
| data = response.json() |
| assert "error" in data |
|
|
|
|
| |
| |
| |
|
|
| class TestContextReferenceResolution: |
| """Test context reference resolution in chat""" |
|
|
| @pytest.mark.asyncio |
| async def test_workflow_reference_resolution(self): |
| """Test resolving workflow references from context""" |
| from core.atom_agent_endpoints import chat_with_agent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_session: |
| mock_session.return_value.create_session = Mock(return_value="session_001") |
| mock_session.return_value.get_session = Mock(return_value={"session_id": "session_001"}) |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_history: |
| mock_history.get_session_history = Mock(return_value=[]) |
|
|
| with patch('core.atom_agent_endpoints.get_chat_context_manager') as mock_ctx: |
| mock_ctx.return_value.resolve_reference = AsyncMock(return_value={ |
| "id": "wf_001", |
| "name": "Daily Report" |
| }) |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm') as mock_classify: |
| mock_classify.return_value = { |
| "intent": "RUN_WORKFLOW", |
| "entities": {"workflow_ref": "that"} |
| } |
|
|
| with patch('core.atom_agent_endpoints.handle_run_workflow') as mock_run: |
| mock_run.return_value = { |
| "success": True, |
| "response": {"message": "Workflow running"} |
| } |
|
|
| request = ChatRequest( |
| message="Run that workflow", |
| user_id="test_user", |
| session_id="session_001" |
| ) |
| result = await chat_with_agent(request) |
|
|
| |
| assert result is not None |
|
|
| @pytest.mark.asyncio |
| async def test_task_reference_resolution(self): |
| """Test resolving task references from context""" |
| from core.atom_agent_endpoints import chat_with_agent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_session: |
| mock_session.return_value.get_session = Mock(return_value={"session_id": "session_001"}) |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_history: |
| mock_history.get_session_history = Mock(return_value=[]) |
|
|
| with patch('core.atom_agent_endpoints.get_chat_context_manager') as mock_ctx: |
| mock_ctx.return_value.resolve_reference = AsyncMock(return_value={ |
| "id": "task_001", |
| "name": "Follow up with client" |
| }) |
|
|
| with patch('core.atom_agent_endpoints.classify_intent_with_llm') as mock_classify: |
| mock_classify.return_value = { |
| "intent": "COMPLETE_TASK", |
| "entities": {"task_ref": "it"} |
| } |
|
|
| with patch('core.atom_agent_endpoints.handle_task_intent') as mock_task: |
| mock_task.return_value = { |
| "success": True, |
| "response": {"message": "Task completed"} |
| } |
|
|
| request = ChatRequest( |
| message="Complete it", |
| user_id="test_user", |
| session_id="session_001" |
| ) |
| result = await chat_with_agent(request) |
|
|
| assert result is not None |
|
|
|
|
| |
| |
| |
|
|
| class TestAdditionalHandlerCoverage: |
| """Additional tests to increase coverage of handlers""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_resolve_conflicts(self): |
| """Test conflict resolution handler""" |
| from core.atom_agent_endpoints import handle_resolve_conflicts, ChatRequest |
|
|
| request = ChatRequest(message="Resolve conflicts", user_id="test_user") |
| result = await handle_resolve_conflicts(request, {}) |
|
|
| assert result["success"] is True |
| assert "conflict" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_set_goal(self): |
| """Test goal setting handler""" |
| from core.atom_agent_endpoints import handle_set_goal, ChatRequest |
|
|
| request = ChatRequest(message="Set a goal", user_id="test_user") |
| result = await handle_set_goal(request, {"goal_text": "Complete project"}) |
|
|
| assert result["success"] is True |
| assert "goal" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_goal_status(self): |
| """Test goal status handler""" |
| from core.atom_agent_endpoints import handle_goal_status, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.goal_engine') as mock_goal: |
| request = ChatRequest(message="Goal status", user_id="test_user") |
| result = await handle_goal_status(request, {}) |
|
|
| assert result["success"] is True |
| assert "goal" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_wellness_check(self): |
| """Test wellness check handler""" |
| from core.atom_agent_endpoints import handle_wellness_check, ChatRequest |
|
|
| request = ChatRequest(message="Check wellness", user_id="test_user") |
| result = await handle_wellness_check(request, {}) |
|
|
| assert result["success"] is True |
| assert "wellness" in result["response"]["message"].lower() or "workload" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_finance_intent_check_balance(self): |
| """Test finance handler for balance check""" |
| from core.atom_agent_endpoints import handle_finance_intent, ChatRequest |
|
|
| request = ChatRequest(message="Check balance", user_id="test_user") |
| result = await handle_finance_intent("CHECK_BALANCE", {}, request) |
|
|
| assert result["success"] is True |
| assert "balance" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_finance_intent_transactions(self): |
| """Test finance handler for transactions""" |
| from core.atom_agent_endpoints import handle_finance_intent, ChatRequest |
|
|
| request = ChatRequest(message="Show transactions", user_id="test_user") |
| result = await handle_finance_intent("GET_TRANSACTIONS", {}, request) |
|
|
| assert result["success"] is True |
| assert "transactions" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_get_history_missing_workflow_ref(self): |
| """Test get history without workflow reference""" |
| from core.atom_agent_endpoints import handle_get_history, ChatRequest |
|
|
| request = ChatRequest(message="Get history", user_id="test_user") |
| result = await handle_get_history(request, {}) |
|
|
| assert result["success"] is False |
| assert "specify" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_cancel_schedule_missing_params(self): |
| """Test cancel schedule without parameters""" |
| from core.atom_agent_endpoints import handle_cancel_schedule, ChatRequest |
|
|
| request = ChatRequest(message="Cancel schedule", user_id="test_user") |
| result = await handle_cancel_schedule(request, {}) |
|
|
| assert result["success"] is False |
| assert "specify" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_finance_intent_unknown(self): |
| """Test finance handler with unknown intent""" |
| from core.atom_agent_endpoints import handle_finance_intent, ChatRequest |
|
|
| request = ChatRequest(message="Unknown finance", user_id="test_user") |
| result = await handle_finance_intent("UNKNOWN_FINANCE", {}, request) |
|
|
| assert result["success"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_handle_task_intent_unknown(self): |
| """Test task handler with unknown intent""" |
| from core.atom_agent_endpoints import handle_task_intent, ChatRequest |
|
|
| request = ChatRequest(message="Unknown task", user_id="test_user") |
| result = await handle_task_intent("UNKNOWN_TASK", {}, request) |
|
|
| assert result["success"] is False |
|
|
|
|
| |
| |
| |
|
|
| class TestStreamingEndpointCoverage: |
| """Tests for streaming chat endpoint to increase coverage""" |
|
|
| def test_stream_endpoint_governance_disabled(self, client): |
| """Test streaming endpoint with governance disabled""" |
| import os |
|
|
| |
| old_val = os.environ.get("STREAMING_GOVERNANCE_ENABLED") |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = "false" |
|
|
| try: |
| |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/chat/stream" in r.path] |
| assert len(routes) > 0 |
| finally: |
| |
| if old_val is None: |
| os.environ.pop("STREAMING_GOVERNANCE_ENABLED", None) |
| else: |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = old_val |
|
|
| def test_stream_endpoint_emergency_bypass(self, client): |
| """Test streaming endpoint with emergency bypass""" |
| import os |
|
|
| |
| old_val = os.environ.get("EMERGENCY_GOVERNANCE_BYPASS") |
| os.environ["EMERGENCY_GOVERNANCE_BYPASS"] = "true" |
|
|
| try: |
| |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/chat/stream" in r.path] |
| assert len(routes) > 0 |
| finally: |
| |
| if old_val is None: |
| os.environ.pop("EMERGENCY_GOVERNANCE_BYPASS", None) |
| else: |
| os.environ["EMERGENCY_GOVERNANCE_BYPASS"] = old_val |
|
|
| @pytest.mark.asyncio |
| async def test_stream_endpoint_with_system_intelligence(self, client): |
| """Test streaming endpoint with system intelligence context""" |
| from core.atom_agent_endpoints import chat_stream_agent, ChatRequest |
|
|
| |
| with patch('core.atom_agent_endpoints.AgentContextResolver') as mock_resolver: |
| mock_resolver.return_value.resolve_agent_for_request = AsyncMock(return_value=(None, None)) |
|
|
| with patch('core.atom_agent_endpoints.AgentGovernanceService') as mock_gov: |
| mock_gov.return_value.can_perform_action = Mock(return_value={"allowed": True}) |
|
|
| with patch('core.atom_agent_endpoints.get_db_session') as mock_db: |
| mock_db.return_value.__enter__ = Mock(return_value=Mock()) |
| mock_db.return_value.__exit__ = Mock(return_value=False) |
|
|
| with patch('core.atom_agent_endpoints.BYOKHandler') as mock_byok: |
| mock_handler = Mock() |
| mock_handler.analyze_query_complexity = Mock(return_value="low") |
| mock_handler.get_optimal_provider = Mock(return_value=("openai", "gpt-3.5-turbo")) |
| mock_byok.return_value = mock_handler |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist.return_value.add_message = Mock() |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess.return_value.create_session = Mock(return_value="session_001") |
|
|
| with patch('core.atom_agent_endpoints.SystemIntelligenceService') as mock_intel: |
| mock_intel.return_value.get_aggregated_context = Mock(return_value="System: OK") |
|
|
| with patch('core.atom_agent_endpoints.ws_manager') as mock_ws: |
| mock_ws.broadcast = AsyncMock() |
|
|
| with patch('core.atom_agent_endpoints.AgentExecution') as mock_agent_exec: |
| |
| request = ChatRequest( |
| message="Test stream", |
| user_id="test_user", |
| workspace_id="default" |
| ) |
|
|
| |
| assert request is not None |
|
|
|
|
| |
| |
| |
|
|
| class TestStreamingEndpointComprehensive: |
| """Comprehensive tests for streaming chat endpoint (lines 1639-1918)""" |
|
|
| def test_stream_endpoint_basic_request(self, client): |
| """Test basic streaming request structure""" |
| |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if r.path == "/api/atom-agent/chat/stream"] |
| assert len(routes) == 1 |
| assert routes[0].methods == {"POST"} |
|
|
| @pytest.mark.asyncio |
| async def test_stream_endpoint_request_structure(self): |
| """Test streaming request with all parameters""" |
| from core.atom_agent_endpoints import ChatRequest |
|
|
| request = ChatRequest( |
| message="Test streaming message", |
| user_id="test_user_123", |
| session_id="session_456", |
| current_page="/workflows", |
| context={"key": "value"}, |
| conversation_history=[ |
| {"role": "user", "content": "Previous message"}, |
| {"role": "assistant", "content": "Previous response"} |
| ], |
| agent_id="agent_789", |
| workspace_id="default" |
| ) |
|
|
| assert request.message == "Test streaming message" |
| assert request.user_id == "test_user_123" |
| assert request.session_id == "session_456" |
| assert request.workspace_id == "default" |
|
|
| @pytest.mark.asyncio |
| async def test_stream_endpoint_minimal_request(self): |
| """Test streaming request with minimal parameters""" |
| from core.atom_agent_endpoints import ChatRequest |
|
|
| request = ChatRequest( |
| message="Hello", |
| user_id="user_001" |
| ) |
|
|
| assert request.message == "Hello" |
| assert request.user_id == "user_001" |
| assert request.session_id is None |
| assert request.workspace_id is None |
|
|
| @pytest.mark.asyncio |
| async def test_stream_with_agent_resolution(self): |
| """Test streaming with agent context resolution""" |
| from core.atom_agent_endpoints import chat_stream_agent, ChatRequest |
| from core.models import AgentRegistry |
|
|
| mock_agent = AgentRegistry( |
| id="test_agent_001", |
| name="Test Agent", |
| maturity_level="AUTONOMOUS", |
| confidence_score=0.95, |
| capabilities=["streaming"], |
| workspace_id="default" |
| ) |
|
|
| with patch('core.atom_agent_endpoints.AgentContextResolver') as mock_resolver_class: |
| mock_resolver = Mock() |
| mock_resolver.resolve_agent_for_request = AsyncMock(return_value=(mock_agent, {"context": "test"})) |
| mock_resolver_class.return_value = mock_resolver |
|
|
| with patch('core.atom_agent_endpoints.AgentGovernanceService') as mock_gov_class: |
| mock_gov = Mock() |
| mock_gov.can_perform_action = Mock(return_value={"allowed": True, "reason": ""}) |
| mock_gov_class.return_value = mock_gov |
|
|
| with patch('core.atom_agent_endpoints.get_db_session') as mock_db: |
| mock_db_session = Mock() |
| mock_db.add = Mock() |
| mock_db.commit = Mock() |
| mock_db.refresh = Mock() |
| mock_db.query = Mock(return_value=Mock(first=Mock(return_value=mock_agent))) |
| mock_db.__enter__ = Mock(return_value=mock_db_session) |
| mock_db.__exit__ = Mock(return_value=False) |
| mock_db.return_value = mock_db |
|
|
| with patch('core.atom_agent_endpoints.BYOKHandler') as mock_byok_class: |
| mock_byok = Mock() |
| mock_byok.analyze_query_complexity = Mock(return_value="low") |
| mock_byok.get_optimal_provider = Mock(return_value=("openai", "gpt-3.5-turbo")) |
| mock_byok.get_api_key = Mock(return_value="test_key") |
| mock_byok.stream_completion = AsyncMock(side_effect=self._mock_stream_generator()) |
| mock_byok_class.return_value = mock_byok |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.add_message = Mock() |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.create_session = Mock(return_value="session_001") |
| mock_sess.return_value = mock_sess_mgr |
|
|
| with patch('core.atom_agent_endpoints.SystemIntelligenceService') as mock_intel_class: |
| mock_intel = Mock() |
| mock_intel.get_aggregated_context = Mock(return_value="System OK") |
| mock_intel_class.return_value = mock_intel |
|
|
| with patch('core.atom_agent_endpoints.ws_manager') as mock_ws: |
| mock_ws.broadcast = AsyncMock() |
| mock_ws.STREAMING_UPDATE = "streaming:update" |
| mock_ws.STREAMING_COMPLETE = "streaming:complete" |
| mock_ws.STREAMING_ERROR = "streaming:error" |
|
|
| with patch('core.atom_agent_endpoints.AgentExecution') as mock_agent_exec: |
| mock_agent_exec_instance = Mock() |
| mock_agent_exec_instance.id = "exec_001" |
| mock_agent_exec.return_value = mock_agent_exec_instance |
|
|
| request = ChatRequest( |
| message="Test streaming", |
| user_id="test_user", |
| workspace_id="default", |
| agent_id="test_agent_001" |
| ) |
|
|
| try: |
| result = await chat_stream_agent(request) |
|
|
| |
| assert result is not None |
| assert isinstance(result, dict) |
| except Exception as e: |
| |
| assert "stream" in str(e).lower() or "test" in str(e).lower() |
|
|
| def _mock_stream_generator(self): |
| """Helper to create mock async stream generator""" |
| async def generator(): |
| yield "Hello" |
| yield " World" |
| return generator() |
|
|
| @pytest.mark.asyncio |
| async def test_stream_governance_blocked(self): |
| """Test streaming blocked by governance""" |
| from core.atom_agent_endpoints import chat_stream_agent, ChatRequest |
|
|
| mock_agent = Mock() |
| mock_agent.id = "student_agent" |
| mock_agent.name = "Student Agent" |
|
|
| with patch('core.atom_agent_endpoints.AgentContextResolver') as mock_resolver_class: |
| mock_resolver = Mock() |
| mock_resolver.resolve_agent_for_request = AsyncMock(return_value=(mock_agent, {})) |
| mock_resolver_class.return_value = mock_resolver |
|
|
| with patch('core.atom_agent_endpoints.AgentGovernanceService') as mock_gov_class: |
| mock_gov = Mock() |
| mock_gov.can_perform_action = Mock(return_value={"allowed": False, "reason": "Student agents cannot stream"}) |
| mock_gov_class.return_value = mock_gov |
|
|
| with patch('core.atom_agent_endpoints.get_db_session') as mock_db: |
| mock_db_session = Mock() |
| mock_db.__enter__ = Mock(return_value=mock_db_session) |
| mock_db.__exit__ = Mock(return_value=False) |
| mock_db.return_value = mock_db_session |
|
|
| request = ChatRequest( |
| message="Test", |
| user_id="test_user", |
| workspace_id="default", |
| agent_id="student_agent" |
| ) |
|
|
| |
| import os |
| old_gov = os.environ.get("STREAMING_GOVERNANCE_ENABLED") |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = "true" |
|
|
| try: |
| result = await chat_stream_agent(request) |
|
|
| |
| assert result["success"] is False |
| assert "governance" in str(result).lower() or "permitted" in str(result).lower() |
| finally: |
| if old_gov is None: |
| os.environ.pop("STREAMING_GOVERNANCE_ENABLED", None) |
| else: |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = old_gov |
|
|
| @pytest.mark.asyncio |
| async def test_stream_governance_disabled(self): |
| """Test streaming with governance disabled""" |
| import os |
|
|
| old_gov = os.environ.get("STREAMING_GOVERNANCE_ENABLED") |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = "false" |
|
|
| try: |
| |
| from core.atom_agent_endpoints import ChatRequest |
|
|
| request = ChatRequest( |
| message="Test", |
| user_id="test_user", |
| workspace_id="default" |
| ) |
|
|
| |
| assert request is not None |
| finally: |
| if old_gov is None: |
| os.environ.pop("STREAMING_GOVERNANCE_ENABLED", None) |
| else: |
| os.environ["STREAMING_GOVERNANCE_ENABLED"] = old_gov |
|
|
| @pytest.mark.asyncio |
| async def test_stream_emergency_bypass(self): |
| """Test streaming with emergency bypass enabled""" |
| import os |
|
|
| old_bypass = os.environ.get("EMERGENCY_GOVERNANCE_BYPASS") |
| os.environ["EMERGENCY_GOVERNANCE_BYPASS"] = "true" |
|
|
| try: |
| |
| from core.atom_agent_endpoints import ChatRequest |
|
|
| request = ChatRequest( |
| message="Test emergency", |
| user_id="test_user", |
| workspace_id="default" |
| ) |
|
|
| assert request is not None |
| finally: |
| if old_bypass is None: |
| os.environ.pop("EMERGENCY_GOVERNANCE_BYPASS", None) |
| else: |
| os.environ["EMERGENCY_GOVERNANCE_BYPASS"] = old_bypass |
|
|
|
|
| |
| |
| |
|
|
| class TestWorkflowHandlerComprehensive: |
| """Comprehensive tests for workflow handlers (lines 851-1056)""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_schedule_workflow_missing_params(self): |
| """Test schedule workflow with missing parameters""" |
| from core.atom_agent_endpoints import handle_schedule_workflow, ChatRequest |
|
|
| request = ChatRequest(message="Schedule workflow", user_id="test_user") |
|
|
| |
| result = await handle_schedule_workflow(request, {}) |
|
|
| assert result["success"] is False |
| assert "specify" in result["response"]["message"].lower() or "which" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_schedule_workflow_not_found(self): |
| """Test scheduling non-existent workflow""" |
| from core.atom_agent_endpoints import handle_schedule_workflow, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=[]): |
| request = ChatRequest(message="Schedule nonexistent", user_id="test_user") |
| result = await handle_schedule_workflow(request, { |
| "workflow_ref": "nonexistent", |
| "time_expression": "daily" |
| }) |
|
|
| assert result["success"] is False |
| assert "not found" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_schedule_workflow_parse_failure(self): |
| """Test schedule workflow with invalid time expression""" |
| from core.atom_agent_endpoints import handle_schedule_workflow, ChatRequest |
|
|
| mock_workflows = [ |
| {"name": "Test Workflow", "workflow_id": "wf_001", "id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.parse_time_expression') as mock_parse: |
| mock_parse = AsyncMock() |
| mock_parse.return_value = None |
|
|
| request = ChatRequest(message="Schedule test", user_id="test_user") |
| result = await handle_schedule_workflow(request, { |
| "workflow_ref": "Test Workflow", |
| "time_expression": "invalid time" |
| }) |
|
|
| assert result["success"] is False |
| assert "understand" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_run_workflow_automation_engine_unavailable(self): |
| """Test run workflow when AutomationEngine is not available""" |
| from core.atom_agent_endpoints import handle_run_workflow, ChatRequest |
|
|
| mock_workflows = [ |
| {"name": "Test Workflow", "workflow_id": "wf_001", "id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine', None): |
| request = ChatRequest(message="Run test", user_id="test_user") |
| result = await handle_run_workflow(request, {"workflow_ref": "Test"}) |
|
|
| assert result["success"] is False |
| assert "not available" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_run_workflow_execution_failure(self): |
| """Test run workflow when execution fails""" |
| from core.atom_agent_endpoints import handle_run_workflow, ChatRequest |
|
|
| mock_workflows = [ |
| {"name": "Test Workflow", "workflow_id": "wf_001", "id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine') as mock_engine_class: |
| mock_engine = Mock() |
| mock_engine.execute_workflow_definition = AsyncMock(side_effect=Exception("Execution failed")) |
| mock_engine_class.return_value = mock_engine |
|
|
| request = ChatRequest(message="Run test", user_id="test_user") |
| result = await handle_run_workflow(request, {"workflow_ref": "Test"}) |
|
|
| assert result["success"] is False |
| assert "failed" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_cancel_schedule_by_schedule_id(self): |
| """Test cancel schedule with schedule ID""" |
| from core.atom_agent_endpoints import handle_cancel_schedule, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.workflow_scheduler') as mock_scheduler: |
| mock_scheduler.remove_job = Mock(return_value=True) |
|
|
| request = ChatRequest(message="Cancel schedule", user_id="test_user") |
| result = await handle_cancel_schedule(request, {"schedule_id": "job_001"}) |
|
|
| assert result["success"] is True |
| assert "cancelled" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_cancel_schedule_not_found(self): |
| """Test cancel non-existent schedule""" |
| from core.atom_agent_endpoints import handle_cancel_schedule, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.workflow_scheduler') as mock_scheduler: |
| mock_scheduler.remove_job = Mock(return_value=False) |
|
|
| request = ChatRequest(message="Cancel schedule", user_id="test_user") |
| result = await handle_cancel_schedule(request, {"schedule_id": "nonexistent_job"}) |
|
|
| assert result["success"] is False |
| assert "not found" in result["response"]["message"].lower() or "could not find" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_cancel_schedule_by_workflow_ref(self): |
| """Test cancel schedule with workflow reference (returns message to check UI)""" |
| from core.atom_agent_endpoints import handle_cancel_schedule, ChatRequest |
|
|
| request = ChatRequest(message="Cancel schedule", user_id="test_user") |
| result = await handle_cancel_schedule(request, {"workflow_ref": "Daily Report"}) |
|
|
| assert result["success"] is True |
| assert "schedule tab" in result["response"]["message"].lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestSystemSearchHandlerComprehensive: |
| """Comprehensive tests for system and search handlers (lines 1537-1634)""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_system_status_unhealthy(self): |
| """Test system status when services are unhealthy""" |
| from core.atom_agent_endpoints import handle_system_status, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.SystemStatus') as mock_status: |
| mock_status.get_overall_status = Mock(return_value="degraded") |
| mock_status.get_system_info = Mock(return_value={"platform": {"system": "Linux"}}) |
| mock_status.get_resource_usage = Mock(return_value={ |
| "cpu": {"percent": 95.2}, |
| "memory": {"percent": 92.8} |
| }) |
| mock_status.get_service_status = Mock(return_value={ |
| "database": {"status": "healthy"}, |
| "redis": {"status": "unhealthy"} |
| }) |
|
|
| request = ChatRequest(message="System status", user_id="test_user") |
| result = await handle_system_status(request) |
|
|
| assert result["success"] is True |
| assert "degraded" in result["response"]["message"].lower() or "system status" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_platform_search_no_results(self): |
| """Test platform search with no results""" |
| from core.atom_agent_endpoints import handle_platform_search, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.unified_hybrid_search') as mock_search: |
| mock_response = Mock() |
| mock_response.success = True |
| mock_response.results = [] |
| mock_response.total_count = 0 |
| mock_search = AsyncMock(return_value=mock_response) |
|
|
| with patch('core.atom_agent_endpoints.unified_hybrid_search', return_value=mock_response): |
| request = ChatRequest(message="Search nothing", user_id="test_user") |
| result = await handle_platform_search(request, {"query": "xyzabc"}) |
|
|
| assert result["success"] is True |
| assert "no results" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_platform_search_error(self): |
| """Test platform search with error""" |
| from core.atom_agent_endpoints import handle_platform_search, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.unified_hybrid_search') as mock_search: |
| mock_search = AsyncMock(side_effect=Exception("Search failed")) |
|
|
| request = ChatRequest(message="Search error", user_id="test_user") |
| result = await handle_platform_search(request, {"query": "test"}) |
|
|
| assert result["success"] is False |
| assert "failed" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_knowledge_query_error(self): |
| """Test knowledge query with error""" |
| from core.atom_agent_endpoints import handle_knowledge_query, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_knowledge_query_manager') as mock_kq: |
| mock_mgr = Mock() |
| mock_mgr.answer_query = AsyncMock(side_effect=Exception("Knowledge query failed")) |
| mock_kq.return_value = mock_mgr |
|
|
| request = ChatRequest(message="Query error", user_id="test_user") |
| result = await handle_knowledge_query(request, {"query": "test"}) |
|
|
| assert result["success"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_handle_crm_query_success(self): |
| """Test CRM query handler""" |
| from core.atom_agent_endpoints import handle_crm_intent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.SalesAssistant') as mock_sales_class: |
| mock_sales = Mock() |
| mock_sales.answer_sales_query = AsyncMock(return_value="Found 5 leads") |
| mock_sales_class.return_value = mock_sales |
|
|
| with patch('core.atom_agent_endpoints.get_db_session') as mock_db: |
| mock_db_session = Mock() |
| mock_db.__enter__ = Mock(return_value=mock_db_session) |
| mock_db.__exit__ = Mock(return_value=False) |
| mock_db.return_value = mock_db |
|
|
| request = ChatRequest(message="Show me leads", user_id="test_user") |
| result = await handle_crm_intent(request, {}) |
|
|
| assert result["success"] is True |
| assert "leads" in result["response"]["message"].lower() or "sales" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_crm_query_error(self): |
| """Test CRM query with error""" |
| from core.atom_agent_endpoints import handle_crm_intent, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.SalesAssistant') as mock_sales_class: |
| mock_sales = Mock() |
| mock_sales.answer_sales_query = AsyncMock(side_effect=Exception("CRM failed")) |
| mock_sales_class.return_value = mock_sales |
|
|
| with patch('core.atom_agent_endpoints.get_db_session') as mock_db: |
| mock_db_session = Mock() |
| mock_db.__enter__ = Mock(return_value=mock_db_session) |
| mock_db.__exit__ = Mock(return_value=Exception("DB error")) |
| mock_db.return_value = mock_db |
|
|
| request = ChatRequest(message="Show me leads", user_id="test_user") |
| result = await handle_crm_intent(request, {}) |
|
|
| assert result["success"] is False |
| assert "failed" in result.get("error", "").lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestAdditionalHandlersForCoverage: |
| """Additional handler tests to increase coverage""" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_silent_stakeholders_success(self): |
| """Test silent stakeholders handler with results""" |
| from core.atom_agent_endpoints import handle_silent_stakeholders, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_stakeholder_engine') as mock_stake_class: |
| mock_engine = Mock() |
| mock_engine.identify_silent_stakeholders = AsyncMock(return_value=[ |
| {"name": "John Doe", "email": "john@example.com", "days_since": 10} |
| ]) |
| mock_stake_class.return_value = mock_engine |
|
|
| request = ChatRequest(message="Who should I follow up with", user_id="test_user") |
| result = await handle_silent_stakeholders(request) |
|
|
| assert result["success"] is True |
| assert "stakeholder" in result["response"]["message"].lower() or "engaged" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_silent_stakeholders_none_found(self): |
| """Test silent stakeholders handler with no results""" |
| from core.atom_agent_endpoints import handle_silent_stakeholders, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.get_stakeholder_engine') as mock_stake_class: |
| mock_engine = Mock() |
| mock_engine.identify_silent_stakeholders = AsyncMock(return_value=[]) |
| mock_stake_class.return_value = mock_engine |
|
|
| request = ChatRequest(message="Who should I follow up with", user_id="test_user") |
| result = await handle_silent_stakeholders(request) |
|
|
| assert result["success"] is True |
| assert "actively engaged" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_follow_up_emails_success(self): |
| """Test follow up emails handler""" |
| from core.atom_agent_endpoints import handle_follow_up_emails, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.template_manager') as mock_tm: |
| mock_tm.get_template = Mock(return_value=Mock(id="email_followup")) |
|
|
| request = ChatRequest(message="Follow up with leads", user_id="test_user") |
| result = await handle_follow_up_emails(request, {}) |
|
|
| assert result["success"] is True |
| assert "follow-up" in result["response"]["message"].lower() or "follow up" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_follow_up_emails_template_missing(self): |
| """Test follow up emails when template is missing""" |
| from core.atom_agent_endpoints import handle_follow_up_emails, ChatRequest |
|
|
| with patch('core.atom_agent_endpoints.template_manager') as mock_tm: |
| mock_tm.get_template = Mock(return_value=None) |
|
|
| request = ChatRequest(message="Follow up with leads", user_id="test_user") |
| result = await handle_follow_up_emails(request, {}) |
|
|
| assert result["success"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_handle_resolve_conflicts_success(self): |
| """Test conflict resolution handler""" |
| from core.atom_agent_endpoints import handle_resolve_conflicts, ChatRequest |
|
|
| request = ChatRequest(message="Resolve my calendar conflicts", user_id="test_user") |
| result = await handle_resolve_conflicts(request, {}) |
|
|
| assert result["success"] is True |
| assert "conflict" in result["response"]["message"].lower() |
|
|
| @pytest.mark.asyncio |
| async def test_handle_help_request_structure(self): |
| """Test help request response structure""" |
| from core.atom_agent_endpoints import handle_help_request |
|
|
| result = handle_help_request() |
|
|
| assert result["success"] is True |
| assert "message" in result["response"] |
| assert "actions" in result["response"] |
| |
| assert "workflow" in result["response"]["message"].lower() or "help" in result["response"]["message"].lower() |
|
|
|
|
| |
| |
| |
|
|
| class TestHybridRetrievalEndpointsComprehensive: |
| """Comprehensive tests for hybrid retrieval endpoints (lines 1929-2043)""" |
|
|
| def test_retrieve_hybrid_endpoint_signature(self): |
| """Test hybrid retrieval endpoint exists and has correct signature""" |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/retrieve-hybrid" in r.path] |
| assert len(routes) == 1 |
|
|
| route = routes[0] |
| assert "POST" in route.methods |
| |
| assert "/agents/{agent_id}/retrieve-hybrid" in route.path |
|
|
| def test_retrieve_baseline_endpoint_signature(self): |
| """Test baseline retrieval endpoint exists and has correct signature""" |
| from fastapi.routing import APIRoute |
|
|
| routes = [r for r in router.routes if "/retrieve-baseline" in r.path] |
| assert len(routes) == 1 |
|
|
| route = routes[0] |
| assert "POST" in route.methods |
| assert "/agents/{agent_id}/retrieve-baseline" in route.path |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_hybrid_service_error(self, client): |
| """Test hybrid retrieval when service raises error""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_hybrid = AsyncMock(side_effect=Exception("Retrieval failed")) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-hybrid", json={ |
| "query": "test query", |
| "coarse_top_k": 100, |
| "rerank_top_k": 50, |
| "use_reranking": True |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "error" in data |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_baseline_service_error(self, client): |
| """Test baseline retrieval when service raises error""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_baseline = AsyncMock(side_effect=Exception("Retrieval failed")) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-baseline", json={ |
| "query": "test query", |
| "top_k": 50 |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "error" in data |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_hybrid_with_custom_params(self, client): |
| """Test hybrid retrieval with custom parameters""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_hybrid = AsyncMock(return_value=[ |
| ("ep_001", 0.95, "rerank"), |
| ("ep_002", 0.87, "rerank"), |
| ("ep_003", 0.82, "coarse") |
| ]) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-hybrid", json={ |
| "query": "custom query", |
| "coarse_top_k": 200, |
| "rerank_top_k": 100, |
| "use_reranking": True |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert data["coarse_top_k"] == 200 |
| assert data["rerank_top_k"] == 100 |
| assert len(data["results"]) == 3 |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_hybrid_without_reranking(self, client): |
| """Test hybrid retrieval with reranking disabled""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_hybrid = AsyncMock(return_value=[ |
| ("ep_001", 0.85, "coarse") |
| ]) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-hybrid", json={ |
| "query": "test query", |
| "coarse_top_k": 100, |
| "rerank_top_k": 50, |
| "use_reranking": False |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert data["use_reranking"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_retrieve_baseline_with_custom_top_k(self, client): |
| """Test baseline retrieval with custom top_k""" |
| with patch('core.atom_agent_endpoints.HybridRetrievalService') as mock_service_class: |
| mock_service = Mock() |
| mock_service.retrieve_semantic_baseline = AsyncMock(return_value=[ |
| ("ep_001", 0.90), |
| ("ep_002", 0.85), |
| ("ep_003", 0.80) |
| ]) |
| mock_service_class.return_value = mock_service |
|
|
| response = client.post("/api/atom-agent/agents/agent_001/retrieve-baseline", json={ |
| "query": "test query", |
| "top_k": 100 |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is True |
| assert data["top_k"] == 100 |
| assert len(data["results"]) == 3 |
|
|
|
|
| |
| |
| |
|
|
| class TestExecuteGeneratedWorkflowComprehensive: |
| """Comprehensive tests for execute-generated workflow endpoint""" |
|
|
| def test_execute_generated_workflow_execution_failure(self, client): |
| """Test execute workflow when execution fails""" |
| mock_workflows = [ |
| {"id": "wf_001", "name": "Test Workflow", "workflow_id": "wf_001"} |
| ] |
|
|
| with patch('core.atom_agent_endpoints.load_workflows', return_value=mock_workflows): |
| with patch('core.atom_agent_endpoints.AutomationEngine') as mock_engine_class: |
| mock_engine = Mock() |
| mock_engine.execute_workflow_definition = AsyncMock(side_effect=Exception("Execution error")) |
| mock_engine_class.return_value = mock_engine |
|
|
| response = client.post("/api/atom-agent/execute-generated", json={ |
| "workflow_id": "wf_001", |
| "input_data": {} |
| }) |
|
|
| assert response.status_code == 200 |
| data = response.json() |
| assert data["success"] is False |
| assert "error" in data |
|
|
| def test_execute_generated_invalid_json_payload(self, client): |
| """Test execute workflow with invalid payload""" |
| response = client.post("/api/atom-agent/execute-generated", json={ |
| "workflow_id": "wf_001" |
| |
| }) |
|
|
| |
| assert response.status_code == 422 |
|
|
|
|
| |
| |
| |
|
|
| class TestChatHistoryPersistenceComprehensive: |
| """Comprehensive tests for chat history persistence""" |
|
|
| @pytest.mark.asyncio |
| async def test_save_chat_interaction_saves_both_messages(self): |
| """Test that save_chat_interaction saves both user and assistant messages""" |
| from core.atom_agent_endpoints import save_chat_interaction |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.save_message = Mock() |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.update_session_activity = Mock() |
| mock_sess.return_value = mock_sess_mgr |
|
|
| save_chat_interaction( |
| session_id="session_001", |
| user_id="user_001", |
| user_message="Hello", |
| assistant_message="Hi there!", |
| intent="GREETING", |
| entities={}, |
| result_data=None, |
| chat_history_mgr=mock_hist_mgr, |
| session_mgr=mock_sess_mgr |
| ) |
|
|
| |
| assert mock_hist_mgr.save_message.call_count == 2 |
|
|
| @pytest.mark.asyncio |
| async def test_save_chat_interaction_with_workflow_id(self): |
| """Test saving chat interaction with workflow ID in result""" |
| from core.atom_agent_endpoints import save_chat_interaction |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.save_message = Mock() |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.update_session_activity = Mock() |
| mock_sess.return_value = mock_sess_mgr |
|
|
| result_data = { |
| "response": { |
| "workflow_id": "wf_001", |
| "workflow_name": "Test Workflow" |
| } |
| } |
|
|
| save_chat_interaction( |
| session_id="session_001", |
| user_id="user_001", |
| user_message="Create workflow", |
| assistant_message="Workflow created", |
| intent="CREATE_WORKFLOW", |
| entities={}, |
| result_data=result_data, |
| chat_history_mgr=mock_hist_mgr, |
| session_mgr=mock_sess_mgr |
| ) |
|
|
| |
| assistant_call = mock_hist_mgr.save_message.call_args_list[1] |
| metadata = assistant_call[1]["metadata"] |
| assert "workflow_id" in metadata |
| assert metadata["workflow_id"] == "wf_001" |
|
|
| @pytest.mark.asyncio |
| async def test_save_chat_interaction_with_task_id(self): |
| """Test saving chat interaction with task ID in result""" |
| from core.atom_agent_endpoints import save_chat_interaction |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.save_message = Mock() |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.update_session_activity = Mock() |
| mock_sess.return_value = mock_sess_mgr |
|
|
| result_data = { |
| "response": { |
| "task_id": "task_001" |
| } |
| } |
|
|
| save_chat_interaction( |
| session_id="session_001", |
| user_id="user_001", |
| user_message="Create task", |
| assistant_message="Task created", |
| intent="CREATE_TASK", |
| entities={}, |
| result_data=result_data, |
| chat_history_mgr=mock_hist_mgr, |
| session_mgr=mock_sess_mgr |
| ) |
|
|
| |
| assistant_call = mock_hist_mgr.save_message.call_args_list[1] |
| metadata = assistant_call[1]["metadata"] |
| assert "task_id" in metadata |
| assert metadata["task_id"] == "task_001" |
|
|
| @pytest.mark.asyncio |
| async def test_save_chat_interaction_updates_session_activity(self): |
| """Test that save_chat_interaction updates session activity""" |
| from core.atom_agent_endpoints import save_chat_interaction |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.save_message = Mock() |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.update_session_activity = Mock() |
| mock_sess.return_value = mock_sess_mgr |
|
|
| save_chat_interaction( |
| session_id="session_001", |
| user_id="user_001", |
| user_message="Test", |
| assistant_message="Response", |
| intent=None, |
| entities=None, |
| result_data=None, |
| chat_history_mgr=mock_hist_mgr, |
| session_mgr=mock_sess_mgr |
| ) |
|
|
| |
| mock_sess_mgr.update_session_activity.assert_called_once_with("session_001") |
|
|
| @pytest.mark.asyncio |
| async def test_save_chat_interaction_handles_errors_gracefully(self): |
| """Test that save_chat_interaction handles errors gracefully""" |
| from core.atom_agent_endpoints import save_chat_interaction |
|
|
| with patch('core.atom_agent_endpoints.get_chat_history_manager') as mock_hist: |
| mock_hist_mgr = Mock() |
| mock_hist_mgr.save_message = Mock(side_effect=Exception("Save failed")) |
| mock_hist.return_value = mock_hist_mgr |
|
|
| with patch('core.atom_agent_endpoints.get_chat_session_manager') as mock_sess: |
| mock_sess_mgr = Mock() |
| mock_sess_mgr.update_session_activity = Mock() |
| mock_sess.return_value = mock_sess_mgr |
|
|
| |
| save_chat_interaction( |
| session_id="session_001", |
| user_id="user_001", |
| user_message="Test", |
| assistant_message="Response", |
| intent=None, |
| entities=None, |
| result_data=None, |
| chat_history_mgr=mock_hist_mgr, |
| session_mgr=mock_sess_mgr |
| ) |
|
|
| |
| assert True |
|
|