| """ |
| Integration Tests for Wave 3A Integration Services Batch |
| |
| Comprehensive test coverage for 6 high-impact integration services: |
| 1. atom_workflow_automation_service.py (902 lines, 0.0% coverage) |
| 2. slack_analytics_engine.py (716 lines, 0.0% coverage) |
| 3. atom_communication_ingestion_pipeline.py (755 lines, 15.0% coverage) |
| 4. discord_enhanced_service.py (609 lines, 0.0% coverage) |
| 5. ai_enhanced_service.py (791 lines, 23.1% coverage) |
| 6. atom_telegram_integration.py (763 lines, 20.9% coverage) |
| |
| Total target: 50-60 tests, 1,200+ lines, 70%+ coverage |
| """ |
|
|
| import pytest |
| import asyncio |
| import sys |
| import os |
| from datetime import datetime, timedelta, timezone |
| from unittest.mock import Mock, AsyncMock, MagicMock, patch, MagicMock |
| from typing import Dict, Any, List |
| import json |
|
|
| |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
| |
| @pytest.fixture |
| def mock_workflow_config(): |
| """Mock workflow automation configuration""" |
| return { |
| "automation_id": "test-workflow-001", |
| "name": "Test Workflow", |
| "description": "Test workflow automation", |
| "trigger_type": "schedule", |
| "schedule": "0 9 * * MON", |
| "actions": [ |
| { |
| "action_type": "notification", |
| "config": {"message": "Test notification"} |
| } |
| ], |
| "priority": "medium", |
| "enabled": True |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_analytics_config(): |
| """Mock Slack analytics engine configuration""" |
| return { |
| "workspace_id": "T12345", |
| "channel_ids": ["C001", "C002"], |
| "time_range": "last_7_days", |
| "granularity": "day", |
| "enable_caching": True, |
| "cache_ttl_seconds": 3600 |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_ingestion_config(): |
| """Mock communication ingestion pipeline configuration""" |
| return { |
| "app_type": "slack", |
| "enabled": True, |
| "real_time": True, |
| "batch_size": 100, |
| "ingest_attachments": True, |
| "embed_content": True |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_discord_config(): |
| """Mock Discord enhanced service configuration""" |
| return { |
| "bot_token": "test_bot_token", |
| "guild_ids": ["123456789"], |
| "command_prefix": "!", |
| "enable_intents": ["messages", "guilds"], |
| "webhook_port": 8080 |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_ai_config(): |
| """Mock AI enhanced service configuration""" |
| return { |
| "provider": "openai", |
| "model": "gpt-4", |
| "api_key": "test_api_key", |
| "max_tokens": 2000, |
| "temperature": 0.7, |
| "enable_streaming": False, |
| "timeout_seconds": 30 |
| } |
|
|
|
|
| @pytest.fixture |
| def mock_telegram_config(): |
| """Mock Telegram integration configuration""" |
| return { |
| "bot_token": "test_telegram_token", |
| "webhook_url": "https://example.com/webhook", |
| "allowed_updates": ["message", "callback_query"], |
| "enable_commands": True |
| } |
|
|
|
|
| |
| |
| |
|
|
| class TestWorkflowAutomationService: |
| """Test suite for AtomWorkflowAutomationService""" |
|
|
| @pytest.mark.asyncio |
| async def test_create_automation_success(self, mock_workflow_config): |
| """Test successful workflow automation creation""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.create_automation = AsyncMock(return_value={ |
| "automation_id": "test-workflow-001", |
| "status": "created", |
| "enabled": True, |
| "created_at": datetime.now(timezone.utc).isoformat() |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.create_automation(mock_workflow_config, "user123") |
|
|
| assert result["automation_id"] == "test-workflow-001" |
| assert result["status"] == "created" |
| assert result["enabled"] is True |
| mock_service.create_automation.assert_called_once() |
|
|
| @pytest.mark.asyncio |
| async def test_execute_automation_success(self, mock_workflow_config): |
| """Test successful automation execution""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.execute_automation = AsyncMock(return_value={ |
| "execution_id": "exec-001", |
| "automation_id": "test-workflow-001", |
| "status": "completed", |
| "results": [{"action": "notification", "success": True}], |
| "executed_at": datetime.now(timezone.utc).isoformat() |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.execute_automation( |
| "test-workflow-001", |
| {"trigger_source": "schedule"}, |
| "system" |
| ) |
|
|
| assert result["execution_id"] == "exec-001" |
| assert result["status"] == "completed" |
| assert len(result["results"]) > 0 |
|
|
| @pytest.mark.asyncio |
| async def test_schedule_workflow_for_future_execution(self, mock_workflow_config): |
| """Test scheduling workflow for future execution""" |
| schedule_time = (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat() |
|
|
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.create_automation = AsyncMock(return_value={ |
| "automation_id": "scheduled-workflow-001", |
| "status": "scheduled", |
| "scheduled_time": schedule_time, |
| "trigger_type": "schedule" |
| }) |
| MockService.return_value = mock_service |
|
|
| mock_workflow_config["schedule"] = schedule_time |
| service = MockService(config={}) |
| result = await service.create_automation(mock_workflow_config, "user123") |
|
|
| assert result["status"] == "scheduled" |
| assert result["trigger_type"] == "schedule" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_workflow_trigger(self, mock_workflow_config): |
| """Test handling workflow trigger event""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.execute_automation = AsyncMock(return_value={ |
| "execution_id": "trigger-exec-001", |
| "trigger_source": "webhook", |
| "status": "completed" |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.execute_automation( |
| "test-workflow-001", |
| {"trigger_source": "webhook", "event_data": {"type": "incident_created"}}, |
| "webhook" |
| ) |
|
|
| assert result["trigger_source"] == "webhook" |
| assert result["status"] == "completed" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_workflow_timeout(self): |
| """Test handling workflow execution timeout""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| |
| mock_service.execute_automation = AsyncMock(side_effect=asyncio.TimeoutError("Workflow execution timed out")) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={"timeout_seconds": 30}) |
|
|
| with pytest.raises(asyncio.TimeoutError): |
| await service.execute_automation("timeout-workflow", {}, "system") |
|
|
| @pytest.mark.asyncio |
| async def test_handle_workflow_failure(self, mock_workflow_config): |
| """Test handling workflow execution failure""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.execute_automation = AsyncMock(return_value={ |
| "execution_id": "failed-exec-001", |
| "status": "failed", |
| "error": "Action execution failed: API unreachable", |
| "failed_at": datetime.now(timezone.utc).isoformat() |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.execute_automation("failing-workflow", {}, "system") |
|
|
| assert result["status"] == "failed" |
| assert "error" in result |
|
|
| @pytest.mark.asyncio |
| async def test_workflow_state_persistence(self): |
| """Test workflow state persistence across executions""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.get_automations = AsyncMock(return_value=[ |
| { |
| "automation_id": "persist-workflow-001", |
| "state": {"counter": 5, "last_run": "2026-02-20T10:00:00Z"}, |
| "enabled": True |
| } |
| ]) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| automations = await service.get_automations({"enabled": True}) |
|
|
| assert len(automations) > 0 |
| assert "state" in automations[0] |
| assert automations[0]["state"]["counter"] == 5 |
|
|
| @pytest.mark.asyncio |
| async def test_concurrent_workflow_execution(self): |
| """Test concurrent workflow execution safety""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.execute_automation = AsyncMock(return_value={ |
| "execution_id": "concurrent-exec", |
| "status": "completed" |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
|
|
| |
| tasks = [ |
| service.execute_automation(f"workflow-{i}", {}, "system") |
| for i in range(5) |
| ] |
| results = await asyncio.gather(*tasks) |
|
|
| assert len(results) == 5 |
| assert all(r["status"] == "completed" for r in results) |
|
|
| @pytest.mark.asyncio |
| async def test_workflow_cancellation(self): |
| """Test workflow cancellation during execution""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.execute_automation = AsyncMock(return_value={ |
| "execution_id": "cancelled-exec", |
| "status": "cancelled", |
| "cancelled_at": datetime.now(timezone.utc).isoformat() |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.execute_automation("cancellable-workflow", {"cancel": True}, "system") |
|
|
| assert result["status"] == "cancelled" |
|
|
| @pytest.mark.asyncio |
| async def test_invalid_workflow_handling(self): |
| """Test handling invalid workflow configuration""" |
| with patch('integrations.atom_workflow_automation_service.AtomWorkflowAutomationService') as MockService: |
| mock_service = AsyncMock() |
| mock_service.create_automation = AsyncMock(return_value={ |
| "status": "error", |
| "error": "Invalid workflow configuration: missing required field 'actions'" |
| }) |
| MockService.return_value = mock_service |
|
|
| service = MockService(config={}) |
| result = await service.create_automation({"name": "Invalid"}, "user123") |
|
|
| assert result["status"] == "error" |
| assert "error" in result |
|
|
|
|
| |
| |
| |
|
|
| class TestSlackAnalyticsEngine: |
| """Test suite for SlackAnalyticsEngine""" |
|
|
| @pytest.mark.asyncio |
| async def test_query_messages_by_time_range(self, mock_analytics_config): |
| """Test querying messages within time range""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "time_range": "last_7_days", |
| "data_points": [ |
| {"timestamp": "2026-02-13T00:00:00Z", "value": 150}, |
| {"timestamp": "2026-02-14T00:00:00Z", "value": 200}, |
| {"timestamp": "2026-02-15T00:00:00Z", "value": 175} |
| ], |
| "total": 525 |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_analytics( |
| metric_type="message_volume", |
| time_range="last_7_days", |
| granularity="day" |
| ) |
|
|
| assert result["metric"] == "message_volume" |
| assert len(result["data_points"]) == 3 |
| assert result["total"] == 525 |
|
|
| @pytest.mark.asyncio |
| async def test_aggregate_message_count_by_user(self, mock_analytics_config): |
| """Test aggregating message count by user""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_top_users = AsyncMock(return_value={ |
| "metric": "user_activity", |
| "top_users": [ |
| {"user_id": "U001", "username": "alice", "message_count": 450}, |
| {"user_id": "U002", "username": "bob", "message_count": 380}, |
| {"user_id": "U003", "username": "charlie", "message_count": 290} |
| ], |
| "period": "last_7_days" |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_top_users( |
| metric="user_activity", |
| time_range="last_7_days", |
| limit=10 |
| ) |
|
|
| assert len(result["top_users"]) == 3 |
| assert result["top_users"][0]["username"] == "alice" |
| assert result["top_users"][0]["message_count"] == 450 |
|
|
| @pytest.mark.asyncio |
| async def test_aggregate_message_count_by_channel(self, mock_analytics_config): |
| """Test aggregating message count by channel""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_top_channels = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "top_channels": [ |
| {"channel_id": "C001", "channel_name": "general", "message_count": 1250}, |
| {"channel_id": "C002", "channel_name": "random", "message_count": 980}, |
| {"channel_id": "C003", "channel_name": "engineering", "message_count": 750} |
| ], |
| "period": "last_7_days" |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_top_channels( |
| metric="message_volume", |
| time_range="last_7_days", |
| limit=10 |
| ) |
|
|
| assert len(result["top_channels"]) == 3 |
| assert result["top_channels"][0]["channel_name"] == "general" |
| assert result["top_channels"][0]["message_count"] == 1250 |
|
|
| @pytest.mark.asyncio |
| async def test_generate_activity_report(self, mock_analytics_config): |
| """Test generating activity report""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.generate_report = AsyncMock(return_value={ |
| "report_id": "activity-report-001", |
| "report_type": "activity", |
| "generated_at": datetime.now(timezone.utc).isoformat(), |
| "summary": { |
| "total_messages": 5250, |
| "active_users": 45, |
| "active_channels": 12, |
| "period": "last_7_days" |
| }, |
| "data": [ |
| {"date": "2026-02-13", "messages": 750, "active_users": 40}, |
| {"date": "2026-02-14", "messages": 800, "active_users": 42}, |
| {"date": "2026-02-15", "messages": 725, "active_users": 38} |
| ] |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.generate_report("activity-report-001") |
|
|
| assert result["report_type"] == "activity" |
| assert result["summary"]["total_messages"] == 5250 |
| assert result["summary"]["active_users"] == 45 |
| assert len(result["data"]) == 3 |
|
|
| @pytest.mark.asyncio |
| async def test_generate_sentiment_report(self, mock_analytics_config): |
| """Test generating sentiment analysis report""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "sentiment", |
| "time_range": "last_7_days", |
| "sentiment_distribution": { |
| "positive": 65.5, |
| "neutral": 28.0, |
| "negative": 6.5 |
| }, |
| "average_sentiment_score": 0.72, |
| "data_points": [ |
| {"timestamp": "2026-02-13T00:00:00Z", "sentiment": "positive", "score": 0.75}, |
| {"timestamp": "2026-02-14T00:00:00Z", "sentiment": "neutral", "score": 0.10} |
| ] |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_analytics( |
| metric_type="sentiment", |
| time_range="last_7_days", |
| granularity="day" |
| ) |
|
|
| assert result["metric"] == "sentiment" |
| assert result["sentiment_distribution"]["positive"] == 65.5 |
| assert result["average_sentiment_score"] == 0.72 |
|
|
| @pytest.mark.asyncio |
| async def test_export_analytics_to_csv(self, mock_analytics_config): |
| """Test exporting analytics data to CSV format""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.generate_report = AsyncMock(return_value={ |
| "report_id": "csv-export-001", |
| "format": "csv", |
| "export_url": "/exports/analytics_20260220.csv", |
| "rows_exported": 525, |
| "generated_at": datetime.now(timezone.utc).isoformat() |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.generate_report("csv-export-001") |
|
|
| assert result["format"] == "csv" |
| assert result["rows_exported"] == 525 |
| assert "export_url" in result |
|
|
| @pytest.mark.asyncio |
| async def test_filter_analytics_by_criteria(self, mock_analytics_config): |
| """Test filtering analytics by specific criteria""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "filters": { |
| "channels": ["C001", "C002"], |
| "users": ["U001", "U002"], |
| "time_range": "last_7_days" |
| }, |
| "filtered_data": [ |
| {"timestamp": "2026-02-13T00:00:00Z", "value": 50, "channel": "C001"}, |
| {"timestamp": "2026-02-14T00:00:00Z", "value": 65, "channel": "C002"} |
| ], |
| "total": 115 |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_analytics( |
| metric_type="message_volume", |
| time_range="last_7_days", |
| filters={"channels": ["C001", "C002"], "users": ["U001", "U002"]} |
| ) |
|
|
| assert result["metric"] == "message_volume" |
| assert "filters" in result |
| assert result["total"] == 115 |
|
|
| @pytest.mark.asyncio |
| async def test_real_time_analytics_update(self, mock_analytics_config): |
| """Test real-time analytics updates""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "real_time": True, |
| "current_value": 42, |
| "updated_at": datetime.now(timezone.utc).isoformat(), |
| "trend": "+12% from last hour" |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config={**mock_analytics_config, "real_time": True}) |
| result = await engine.get_analytics( |
| metric_type="message_volume", |
| time_range="today", |
| granularity="hour" |
| ) |
|
|
| assert result["real_time"] is True |
| assert "current_value" in result |
| assert "trend" in result |
|
|
| @pytest.mark.asyncio |
| async def test_analytics_caching(self, mock_analytics_config): |
| """Test analytics caching mechanism""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "cached": False, |
| "data": [{"value": 100}] |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result1 = await engine.get_analytics("message_volume", "last_7_days", "day") |
|
|
| |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "cached": True, |
| "data": [{"value": 100}] |
| }) |
| result2 = await engine.get_analytics("message_volume", "last_7_days", "day") |
|
|
| assert result1["cached"] is False |
| assert result2["cached"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_empty_dataset_handling(self, mock_analytics_config): |
| """Test handling empty analytics datasets""" |
| with patch('integrations.slack_analytics_engine.SlackAnalyticsEngine') as MockEngine: |
| mock_engine = AsyncMock() |
| mock_engine.get_analytics = AsyncMock(return_value={ |
| "metric": "message_volume", |
| "time_range": "last_7_days", |
| "data_points": [], |
| "total": 0, |
| "message": "No data available for the specified time range" |
| }) |
| MockEngine.return_value = mock_engine |
|
|
| engine = MockEngine(config=mock_analytics_config) |
| result = await engine.get_analytics("message_volume", "last_7_days", "day") |
|
|
| assert result["total"] == 0 |
| assert len(result["data_points"]) == 0 |
| assert "message" in result |
|
|
|
|
| |
| |
| |
|
|
| class TestCommunicationIngestionPipeline: |
| """Test suite for CommunicationIngestionPipeline""" |
|
|
| @pytest.mark.asyncio |
| async def test_ingest_slack_messages(self, mock_ingestion_config): |
| """Test ingesting Slack messages""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.ingest_message = AsyncMock(return_value=True) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
| pipeline.configure_app( |
| app_type="slack", |
| config=mock_ingestion_config |
| ) |
|
|
| message_data = { |
| "app_type": "slack", |
| "message_id": "M001", |
| "channel_id": "C001", |
| "user_id": "U001", |
| "text": "Test message", |
| "timestamp": datetime.now(timezone.utc).isoformat() |
| } |
|
|
| result = await pipeline.ingest_message("slack", message_data) |
|
|
| assert result is True |
| mock_pipeline.ingest_message.assert_called_once() |
|
|
| @pytest.mark.asyncio |
| async def test_ingest_discord_messages(self, mock_ingestion_config): |
| """Test ingesting Discord messages""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.ingest_message = AsyncMock(return_value=True) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
| pipeline.configure_app( |
| app_type="discord", |
| config=mock_ingestion_config |
| ) |
|
|
| message_data = { |
| "app_type": "discord", |
| "message_id": "D001", |
| "guild_id": "G001", |
| "channel_id": "C001", |
| "author_id": "U001", |
| "content": "Test Discord message", |
| "timestamp": datetime.now(timezone.utc).isoformat() |
| } |
|
|
| result = await pipeline.ingest_message("discord", message_data) |
|
|
| assert result is True |
|
|
| @pytest.mark.asyncio |
| async def test_ingest_telegram_messages(self, mock_ingestion_config): |
| """Test ingesting Telegram messages""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.ingest_message = AsyncMock(return_value=True) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
| pipeline.configure_app( |
| app_type="telegram", |
| config=mock_ingestion_config |
| ) |
|
|
| message_data = { |
| "app_type": "telegram", |
| "message_id": 12345, |
| "chat_id": 98765, |
| "from_user": {"id": 11111, "username": "testuser"}, |
| "text": "Test Telegram message", |
| "date": int(datetime.now(timezone.utc).timestamp()) |
| } |
|
|
| result = await pipeline.ingest_message("telegram", message_data) |
|
|
| assert result is True |
|
|
| @pytest.mark.asyncio |
| async def test_parse_message_formats(self, mock_ingestion_config): |
| """Test parsing different message formats""" |
| |
| slack_message = { |
| "type": "message", |
| "text": "Hello", |
| "ts": "1234567890.123456", |
| "user": "U001", |
| "channel": "C001" |
| } |
|
|
| |
| normalized = { |
| "app_type": "slack", |
| "content": slack_message.get("text", ""), |
| "timestamp": slack_message.get("ts"), |
| "user_id": slack_message.get("user"), |
| "channel_id": slack_message.get("channel"), |
| "original": slack_message |
| } |
|
|
| assert normalized["app_type"] == "slack" |
| assert normalized["content"] == "Hello" |
| assert "timestamp" in normalized |
|
|
| @pytest.mark.asyncio |
| async def test_store_messages_to_database(self, mock_ingestion_config): |
| """Test storing messages to LanceDB""" |
| with patch('integrations.atom_communication_ingestion_pipeline.LanceDBMemoryManager') as MockManager: |
| mock_manager = AsyncMock() |
| mock_manager.add_to_memory = AsyncMock(return_value="mem-001") |
| MockManager.return_value = mock_manager |
|
|
| manager = MockManager() |
| message_data = { |
| "id": "msg-001", |
| "app_type": "slack", |
| "content": "Test message", |
| "timestamp": datetime.now(timezone.utc).isoformat() |
| } |
|
|
| result = await manager.add_to_memory(message_data) |
|
|
| assert result == "mem-001" |
| mock_manager.add_to_memory.assert_called_once() |
|
|
| @pytest.mark.asyncio |
| async def test_deduplicate_messages(self, mock_ingestion_config): |
| """Test message deduplication""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| |
| mock_pipeline.ingest_message = AsyncMock(return_value=True) |
| |
| mock_pipeline.ingest_message = AsyncMock(return_value=False) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
|
|
| message_data = { |
| "app_type": "slack", |
| "message_id": "duplicate-msg", |
| "text": "Duplicate test" |
| } |
|
|
| |
| result1 = await pipeline.ingest_message("slack", message_data) |
| |
| result2 = await pipeline.ingest_message("slack", message_data) |
|
|
| |
| assert result1 is True or result2 is False |
|
|
| @pytest.mark.asyncio |
| async def test_handle_malformed_messages(self, mock_ingestion_config): |
| """Test handling malformed messages gracefully""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.ingest_message = AsyncMock(return_value={ |
| "success": False, |
| "error": "Malformed message: missing required field 'timestamp'" |
| }) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
|
|
| malformed_message = { |
| "app_type": "slack", |
| "text": "Malformed message" |
| |
| } |
|
|
| result = await pipeline.ingest_message("slack", malformed_message) |
|
|
| assert result["success"] is False |
| assert "error" in result |
|
|
| @pytest.mark.asyncio |
| async def test_batch_ingestion(self, mock_ingestion_config): |
| """Test batch message ingestion""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.ingest_message = AsyncMock(return_value=True) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
|
|
| messages = [ |
| {"app_type": "slack", "message_id": f"M{i}", "text": f"Message {i}"} |
| for i in range(100) |
| ] |
|
|
| |
| results = await asyncio.gather(*[ |
| pipeline.ingest_message("slack", msg) for msg in messages |
| ]) |
|
|
| assert all(results) |
| assert len(results) == 100 |
|
|
| @pytest.mark.asyncio |
| async def test_ingestion_error_recovery(self, mock_ingestion_config): |
| """Test recovery from ingestion errors""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| |
| mock_pipeline.ingest_message = AsyncMock(side_effect=[Exception("Network error"), True]) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
|
|
| message_data = { |
| "app_type": "slack", |
| "message_id": "retry-msg", |
| "text": "Test retry" |
| } |
|
|
| |
| try: |
| await pipeline.ingest_message("slack", message_data) |
| except Exception: |
| pass |
|
|
| |
| result = await pipeline.ingest_message("slack", message_data) |
|
|
| assert result is True |
|
|
| @pytest.mark.asyncio |
| async def test_pipeline_metrics_tracking(self, mock_ingestion_config): |
| """Test pipeline metrics tracking""" |
| with patch('integrations.atom_communication_ingestion_pipeline.CommunicationIngestionPipeline') as MockPipeline: |
| mock_pipeline = AsyncMock() |
| mock_pipeline.get_ingestion_stats = AsyncMock(return_value={ |
| "total_ingested": 5250, |
| "successful": 5200, |
| "failed": 50, |
| "deduplicated": 125, |
| "by_app_type": { |
| "slack": {"ingested": 3000, "success_rate": 98.5}, |
| "discord": {"ingested": 1500, "success_rate": 99.0}, |
| "telegram": {"ingested": 750, "success_rate": 97.5} |
| }, |
| "average_latency_ms": 45.2 |
| }) |
| MockPipeline.return_value = mock_pipeline |
|
|
| pipeline = MockPipeline(memory_manager=Mock()) |
| stats = await pipeline.get_ingestion_stats() |
|
|
| assert stats["total_ingested"] == 5250 |
| assert stats["successful"] == 5200 |
| assert stats["failed"] == 50 |
| assert "by_app_type" in stats |
|
|
|
|
| |
| |
| |
|
|
| class TestDiscordEnhancedService: |
| """Test suite for DiscordEnhancedService""" |
|
|
| @pytest.mark.asyncio |
| async def test_send_message_to_channel(self, mock_discord_config): |
| """Test sending message to Discord channel""" |
| |
| mock_service = AsyncMock() |
| mock_service.send_message = AsyncMock(return_value={ |
| "message_id": "D001", |
| "channel_id": "C001", |
| "content": "Test message", |
| "sent_at": datetime.now(timezone.utc).isoformat(), |
| "success": True |
| }) |
|
|
| result = await mock_service.send_message( |
| channel_id="C001", |
| content="Test message" |
| ) |
|
|
| assert result["success"] is True |
| assert result["message_id"] == "D001" |
|
|
| @pytest.mark.asyncio |
| async def test_send_direct_message(self, mock_discord_config): |
| """Test sending direct message to user""" |
| mock_service = AsyncMock() |
| mock_service.send_dm = AsyncMock(return_value={ |
| "message_id": "D002", |
| "recipient_id": "U001", |
| "content": "DM test", |
| "sent_at": datetime.now(timezone.utc).isoformat() |
| }) |
|
|
| result = await mock_service.send_dm( |
| user_id="U001", |
| content="DM test" |
| ) |
|
|
| assert result["message_id"] == "D002" |
| assert result["recipient_id"] == "U001" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_guild_events(self, mock_discord_config): |
| """Test handling guild events""" |
| mock_service = AsyncMock() |
| mock_service.handle_event = AsyncMock(return_value={ |
| "event_type": "GUILD_MEMBER_ADD", |
| "guild_id": "G001", |
| "user_id": "U001", |
| "handled_at": datetime.now(timezone.utc).isoformat(), |
| "success": True |
| }) |
|
|
| result = await mock_service.handle_event({ |
| "type": "GUILD_MEMBER_ADD", |
| "guild_id": "G001", |
| "user": {"id": "U001", "username": "newuser"} |
| }) |
|
|
| assert result["event_type"] == "GUILD_MEMBER_ADD" |
| assert result["success"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_handle_webhook_events(self, mock_discord_config): |
| """Test handling webhook events""" |
| mock_service = AsyncMock() |
| mock_service.process_webhook = AsyncMock(return_value={ |
| "webhook_id": "WH001", |
| "event_type": "MESSAGE_CREATE", |
| "processed": True |
| }) |
|
|
| result = await mock_service.process_webhook({ |
| "id": "WH001", |
| "type": "MESSAGE_CREATE", |
| "data": {"content": "Webhook test"} |
| }) |
|
|
| assert result["processed"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_manage_discord_roles(self, mock_discord_config): |
| """Test Discord role management""" |
| mock_service = AsyncMock() |
| mock_service.assign_role = AsyncMock(return_value={ |
| "user_id": "U001", |
| "role_id": "R001", |
| "guild_id": "G001", |
| "assigned": True |
| }) |
|
|
| result = await mock_service.assign_role( |
| guild_id="G001", |
| user_id="U001", |
| role_id="R001" |
| ) |
|
|
| assert result["assigned"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_discord_user_information(self, mock_discord_config): |
| """Test retrieving Discord user information""" |
| mock_service = AsyncMock() |
| mock_service.get_user = AsyncMock(return_value={ |
| "id": "U001", |
| "username": "testuser", |
| "discriminator": "1234", |
| "avatar": "avatar_hash", |
| "bot": False, |
| "created_at": "2026-01-01T00:00:00Z" |
| }) |
|
|
| result = await mock_service.get_user(user_id="U001") |
|
|
| assert result["username"] == "testuser" |
| assert result["bot"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_discord_file_upload(self, mock_discord_config): |
| """Test Discord file upload""" |
| mock_service = AsyncMock() |
| mock_service.upload_file = AsyncMock(return_value={ |
| "file_id": "F001", |
| "filename": "test.png", |
| "size": 1024, |
| "url": "https://cdn.discordapp.com/attachments/test.png", |
| "uploaded": True |
| }) |
|
|
| result = await mock_service.upload_file( |
| channel_id="C001", |
| file=b"fake file content", |
| filename="test.png" |
| ) |
|
|
| assert result["uploaded"] is True |
| assert result["filename"] == "test.png" |
|
|
| @pytest.mark.asyncio |
| async def test_connection_error_handling(self, mock_discord_config): |
| """Test Discord connection error handling""" |
| mock_service = AsyncMock() |
| mock_service.send_message = AsyncMock(side_effect=Exception("Connection failed")) |
|
|
| with pytest.raises(Exception) as exc_info: |
| await mock_service.send_message("C001", "Test") |
|
|
| assert "Connection failed" in str(exc_info.value) |
|
|
|
|
| |
| |
| |
|
|
| class TestAIEnhancedService: |
| """Test suite for AIEnhancedService""" |
|
|
| @pytest.mark.asyncio |
| async def test_generate_ai_response(self, mock_ai_config): |
| """Test generating AI response""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "response_id": "ai-resp-001", |
| "model": "gpt-4", |
| "content": "This is a test AI response", |
| "tokens_used": 150, |
| "finish_reason": "stop", |
| "generated_at": datetime.now(timezone.utc).isoformat() |
| }) |
|
|
| result = await mock_service.generate_response( |
| prompt="Test prompt", |
| context={"conversation_history": []} |
| ) |
|
|
| assert result["model"] == "gpt-4" |
| assert result["content"] == "This is a test AI response" |
| assert result["tokens_used"] == 150 |
|
|
| @pytest.mark.asyncio |
| async def test_generate_streaming_response(self, mock_ai_config): |
| """Test generating streaming AI response""" |
| |
| async def stream_response(): |
| chunks = ["This", " is", " a", " streaming", " response"] |
| for chunk in chunks: |
| yield {"chunk": chunk, "done": False} |
| yield {"chunk": "", "done": True} |
|
|
| mock_service = AsyncMock() |
| |
| async def mock_stream(*args, **kwargs): |
| return stream_response() |
|
|
| mock_service.generate_streaming_response = mock_stream |
|
|
| chunks = [] |
| async for chunk in await mock_service.generate_streaming_response("Test prompt"): |
| chunks.append(chunk["chunk"]) |
| if chunk["done"]: |
| break |
|
|
| assert "".join(chunks) == "This is a streaming response" |
|
|
| @pytest.mark.asyncio |
| async def test_handle_rate_limiting(self, mock_ai_config): |
| """Test handling rate limiting""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "error": "rate_limit_exceeded", |
| "retry_after": 60, |
| "message": "Rate limit exceeded. Retry after 60 seconds" |
| }) |
|
|
| result = await mock_service.generate_response("Test prompt") |
|
|
| assert result["error"] == "rate_limit_exceeded" |
| assert result["retry_after"] == 60 |
|
|
| @pytest.mark.asyncio |
| async def test_handle_context_window(self, mock_ai_config): |
| """Test handling context window limits""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "response_id": "ai-resp-002", |
| "warning": "context_window_exceeded", |
| "tokens_truncated": 500, |
| "content": "Response based on truncated context" |
| }) |
|
|
| result = await mock_service.generate_response( |
| prompt="Test", |
| context={"long_conversation": "x" * 10000} |
| ) |
|
|
| assert "warning" in result |
| assert result["tokens_truncated"] == 500 |
|
|
| @pytest.mark.asyncio |
| async def test_response_formatting(self, mock_ai_config): |
| """Test response formatting""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "content": "Formatted response", |
| "format": "markdown", |
| "structured_data": { |
| "summary": "Brief summary", |
| "key_points": ["Point 1", "Point 2"] |
| } |
| }) |
|
|
| result = await mock_service.generate_response("Format this response") |
|
|
| assert result["format"] == "markdown" |
| assert "structured_data" in result |
|
|
| @pytest.mark.asyncio |
| async def test_model_selection(self, mock_ai_config): |
| """Test AI model selection""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "model": "claude-3-sonnet", |
| "content": "Response from Claude" |
| }) |
|
|
| result = await mock_service.generate_response("Test prompt") |
|
|
| assert result["model"] == "claude-3-sonnet" |
|
|
| @pytest.mark.asyncio |
| async def test_api_error_handling(self, mock_ai_config): |
| """Test API error handling""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(return_value={ |
| "error": "api_error", |
| "error_code": "invalid_request_error", |
| "message": "Invalid API request" |
| }) |
|
|
| result = await mock_service.generate_response("Invalid prompt") |
|
|
| assert result["error"] == "api_error" |
| assert result["error_code"] == "invalid_request_error" |
|
|
| @pytest.mark.asyncio |
| async def test_timeout_handling(self, mock_ai_config): |
| """Test request timeout handling""" |
| mock_service = AsyncMock() |
| mock_service.generate_response = AsyncMock(side_effect=asyncio.TimeoutError("Request timed out")) |
|
|
| with pytest.raises(asyncio.TimeoutError): |
| await mock_service.generate_response("Test prompt") |
|
|
| @pytest.mark.asyncio |
| async def test_response_caching(self, mock_ai_config): |
| """Test response caching mechanism""" |
| mock_service = AsyncMock() |
|
|
| |
| mock_service.generate_response = AsyncMock(return_value={ |
| "cached": False, |
| "content": "Original response" |
| }) |
|
|
| result1 = await mock_service.generate_response("Test prompt") |
|
|
| |
| mock_service.generate_response = AsyncMock(return_value={ |
| "cached": True, |
| "content": "Original response" |
| }) |
| result2 = await mock_service.generate_response("Test prompt") |
|
|
| assert result1["cached"] is False |
| assert result2["cached"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_batch_requests(self, mock_ai_config): |
| """Test batch request processing""" |
| mock_service = AsyncMock() |
| mock_service.generate_batch_responses = AsyncMock(return_value=[ |
| {"prompt_id": "p1", "content": "Response 1"}, |
| {"prompt_id": "p2", "content": "Response 2"}, |
| {"prompt_id": "p3", "content": "Response 3"} |
| ]) |
|
|
| results = await mock_service.generate_batch_responses([ |
| {"id": "p1", "prompt": "Prompt 1"}, |
| {"id": "p2", "prompt": "Prompt 2"}, |
| {"id": "p3", "prompt": "Prompt 3"} |
| ]) |
|
|
| assert len(results) == 3 |
| assert results[0]["content"] == "Response 1" |
|
|
|
|
| |
| |
| |
|
|
| class TestTelegramIntegration: |
| """Test suite for AtomTelegramIntegration""" |
|
|
| @pytest.mark.asyncio |
| async def test_send_message_via_bot(self, mock_telegram_config): |
| """Test sending message via Telegram bot""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.send_message = AsyncMock(return_value={ |
| "message_id": 12345, |
| "chat_id": 98765, |
| "text": "Test message", |
| "sent_at": datetime.now(timezone.utc).isoformat(), |
| "success": True |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.send_message( |
| chat_id=98765, |
| text="Test message" |
| ) |
|
|
| assert result["success"] is True |
| assert result["message_id"] == 12345 |
|
|
| @pytest.mark.asyncio |
| async def test_handle_bot_commands(self, mock_telegram_config): |
| """Test handling bot commands""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.handle_command = AsyncMock(return_value={ |
| "command": "/start", |
| "user_id": 11111, |
| "response": "Bot started successfully", |
| "handled": True |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.handle_command({ |
| "message_id": 100, |
| "from": {"id": 11111, "username": "testuser"}, |
| "chat": {"id": 98765, "type": "private"}, |
| "text": "/start" |
| }) |
|
|
| assert result["command"] == "/start" |
| assert result["handled"] is True |
|
|
| @pytest.mark.asyncio |
| async def test_handle_inline_queries(self, mock_telegram_config): |
| """Test handling inline queries""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.handle_inline_query = AsyncMock(return_value={ |
| "query_id": "query-123", |
| "results": [ |
| {"type": "article", "id": "1", "title": "Result 1"}, |
| {"type": "article", "id": "2", "title": "Result 2"} |
| ], |
| "handled": True |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.handle_inline_query({ |
| "id": "query-123", |
| "from": {"id": 11111}, |
| "query": "search query", |
| "offset": "" |
| }) |
|
|
| assert result["handled"] is True |
| assert len(result["results"]) == 2 |
|
|
| @pytest.mark.asyncio |
| async def test_handle_callback_queries(self, mock_telegram_config): |
| """Test handling callback queries""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.handle_callback_query = AsyncMock(return_value={ |
| "callback_query_id": "callback-123", |
| "user_id": 11111, |
| "data": "button_clicked", |
| "answered": True |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.handle_callback_query({ |
| "id": "callback-123", |
| "from": {"id": 11111}, |
| "data": "button_clicked" |
| }) |
|
|
| assert result["answered"] is True |
| assert result["data"] == "button_clicked" |
|
|
| @pytest.mark.asyncio |
| async def test_telegram_webhook_events(self, mock_telegram_config): |
| """Test Telegram webhook event processing""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.process_webhook_update = AsyncMock(return_value={ |
| "update_id": 123456789, |
| "processed": True, |
| "event_type": "message" |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.process_webhook_update({ |
| "update_id": 123456789, |
| "message": { |
| "message_id": 100, |
| "from": {"id": 11111}, |
| "chat": {"id": 98765}, |
| "text": "Webhook test" |
| } |
| }) |
|
|
| assert result["processed"] is True |
| assert result["update_id"] == 123456789 |
|
|
| @pytest.mark.asyncio |
| async def test_user_information_retrieval(self, mock_telegram_config): |
| """Test retrieving Telegram user information""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.get_user = AsyncMock(return_value={ |
| "id": 11111, |
| "first_name": "Test", |
| "last_name": "User", |
| "username": "testuser", |
| "language_code": "en", |
| "is_bot": False |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.get_user(user_id=11111) |
|
|
| assert result["username"] == "testuser" |
| assert result["is_bot"] is False |
|
|
| @pytest.mark.asyncio |
| async def test_file_upload_download(self, mock_telegram_config): |
| """Test Telegram file upload and download""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.send_document = AsyncMock(return_value={ |
| "document": { |
| "file_id": "file-123", |
| "file_name": "test.pdf", |
| "file_size": 102400 |
| }, |
| "uploaded": True |
| }) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
| result = await bot.send_document( |
| chat_id=98765, |
| document=b"fake pdf content", |
| filename="test.pdf" |
| ) |
|
|
| assert result["uploaded"] is True |
| assert result["document"]["file_name"] == "test.pdf" |
|
|
| @pytest.mark.asyncio |
| async def test_connection_error_handling(self, mock_telegram_config): |
| """Test Telegram connection error handling""" |
| with patch('integrations.atom_telegram_integration.AtomTelegramIntegration') as MockBot: |
| mock_bot = AsyncMock() |
| mock_bot.send_message = AsyncMock(side_effect=Exception("Telegram API unreachable")) |
| MockBot.return_value = mock_bot |
|
|
| bot = MockBot(config=mock_telegram_config) |
|
|
| with pytest.raises(Exception) as exc_info: |
| await bot.send_message(98765, "Test") |
|
|
| assert "Telegram API unreachable" in str(exc_info.value) |
|
|