Download tests/integration/services/test_websocket_coverage.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/integration/services/test_websocket_coverage.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/integration/services/test_websocket_coverage.py
-
curl -L -o test_websocket_coverage.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/integration/services/test_websocket_coverage.py
14.3 kB
| """ | |
| Coverage expansion tests for WebSocket broadcast integration with canvas. | |
| Target: Verify WebSocket broadcast functionality for canvas operations: | |
| - WebSocket broadcast called for all canvas presentations | |
| - WebSocket error handling | |
| - Session-based routing | |
| - User channel routing | |
| Tests use mocked WebSocket manager to focus on broadcast logic. | |
| """ | |
| import pytest | |
| from unittest.mock import AsyncMock, Mock, patch | |
| # Add parent directory to path for imports | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent)) | |
| # ============================================================================ | |
| # Fixtures | |
| # ============================================================================ | |
| def mock_db_context(): | |
| """Mock database context manager.""" | |
| with patch('core.database.get_db_session') as mock_ctx: | |
| db = Mock() | |
| db.add = Mock() | |
| db.commit = Mock() | |
| db.refresh = Mock() | |
| db.rollback = Mock() | |
| # Track different types of objects | |
| mock_agent = None | |
| mock_agent_execution = None | |
| mock_canvas_audit = None | |
| def capture_add(obj): | |
| """Capture added objects.""" | |
| nonlocal mock_agent, mock_agent_execution, mock_canvas_audit | |
| if hasattr(obj, 'module_path'): # AgentRegistry | |
| mock_agent = obj | |
| elif hasattr(obj, 'triggered_by'): # AgentExecution | |
| mock_agent_execution = obj | |
| elif hasattr(obj, 'canvas_id'): # CanvasAudit | |
| mock_canvas_audit = obj | |
| db.add = Mock(side_effect=capture_add) | |
| def mock_query(model): | |
| """Mock query that returns appropriate object.""" | |
| query_obj = Mock() | |
| def mock_filter(*args, **kwargs): | |
| result = Mock() | |
| if hasattr(model, '__tablename__'): | |
| if model.__tablename__ == 'agent_registry': | |
| result.first = Mock(return_value=mock_agent) | |
| elif model.__tablename__ == 'agent_execution': | |
| result.first = Mock(return_value=mock_agent_execution) | |
| elif model.__tablename__ == 'canvas_audit': | |
| result.first = Mock(return_value=mock_canvas_audit) | |
| else: | |
| result.first = Mock(return_value=None) | |
| else: | |
| result.first = Mock(return_value=None) | |
| return result | |
| query_obj.filter = mock_filter | |
| return query_obj | |
| db.query = Mock(side_effect=mock_query) | |
| mock_ctx.return_value.__enter__ = Mock(return_value=db) | |
| mock_ctx.return_value.__exit__ = Mock(return_value=False) | |
| yield mock_ctx | |
| def mock_ws_manager(): | |
| """Mock WebSocket manager with AsyncMock for async broadcast methods.""" | |
| # Create mock manager with AsyncMock for async methods | |
| mock_mgr = Mock() | |
| mock_mgr.broadcast = AsyncMock() | |
| # Patch ws_manager in canvas_tool module | |
| with patch('tools.canvas_tool.ws_manager', mock_mgr): | |
| yield mock_mgr | |
| # ============================================================================ | |
| # Test WebSocket Broadcast | |
| # ============================================================================ | |
| class TestWebSocketBroadcast: | |
| """Test WebSocket broadcast for canvas operations.""" | |
| async def test_chart_broadcast_called(self, mock_db_context, mock_ws_manager): | |
| """Test that chart presentation triggers WebSocket broadcast.""" | |
| from tools.canvas_tool import present_chart | |
| result = await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}], | |
| title="Test Chart" | |
| ) | |
| assert result["success"] is True | |
| assert mock_ws_manager.broadcast.called | |
| # Verify broadcast structure | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert "user:test_user" in call_args[0][0] | |
| assert call_args[0][1]["type"] == "canvas:update" | |
| assert call_args[0][1]["data"]["action"] == "present" | |
| async def test_form_broadcast_called(self, mock_db_context, mock_ws_manager): | |
| """Test that form presentation triggers WebSocket broadcast.""" | |
| from tools.canvas_tool import present_form | |
| result = await present_form( | |
| user_id="test_user", | |
| form_schema={"fields": [{"name": "field1", "type": "text"}]}, | |
| title="Test Form" | |
| ) | |
| assert result["success"] is True | |
| assert mock_ws_manager.broadcast.called | |
| # Verify broadcast includes form data | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert call_args[0][1]["data"]["component"] == "form" | |
| async def test_markdown_broadcast_called(self, mock_db_context, mock_ws_manager): | |
| """Test that markdown presentation triggers WebSocket broadcast.""" | |
| from tools.canvas_tool import present_markdown | |
| result = await present_markdown( | |
| user_id="test_user", | |
| content="# Test Markdown\n\nThis is a test.", | |
| title="Test MD" | |
| ) | |
| assert result["success"] is True | |
| assert mock_ws_manager.broadcast.called | |
| # Verify broadcast includes markdown content | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert call_args[0][1]["data"]["component"] == "markdown" | |
| async def test_update_broadcast_called(self, mock_db_context, mock_ws_manager): | |
| """Test that canvas update triggers WebSocket broadcast.""" | |
| from tools.canvas_tool import update_canvas | |
| result = await update_canvas( | |
| user_id="test_user", | |
| canvas_id="canvas-123", | |
| updates={"title": "Updated Title"} | |
| ) | |
| assert result["success"] is True | |
| assert mock_ws_manager.broadcast.called | |
| # Verify broadcast includes update action | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert call_args[0][1]["data"]["action"] == "update" | |
| async def test_close_broadcast_called(self, mock_ws_manager): | |
| """Test that canvas close triggers WebSocket broadcast.""" | |
| from tools.canvas_tool import close_canvas | |
| result = await close_canvas(user_id="test_user") | |
| assert result["success"] is True | |
| assert mock_ws_manager.broadcast.called | |
| # Verify broadcast includes close action | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert call_args[0][1]["data"]["action"] == "close" | |
| # ============================================================================ | |
| # Test WebSocket Routing | |
| # ============================================================================ | |
| class TestWebSocketRouting: | |
| """Test WebSocket channel routing for canvas operations.""" | |
| async def test_user_channel_routing(self, mock_db_context, mock_ws_manager): | |
| """Test that canvas broadcasts to correct user channel.""" | |
| from tools.canvas_tool import present_chart | |
| await present_chart( | |
| user_id="user-12345", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}] | |
| ) | |
| call_args = mock_ws_manager.broadcast.call_args | |
| assert "user:user-12345" in call_args[0][0] | |
| async def test_session_channel_routing(self, mock_db_context, mock_ws_manager): | |
| """Test that canvas broadcasts to session-specific channel.""" | |
| from tools.canvas_tool import present_chart | |
| await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}], | |
| session_id="session-abc" | |
| ) | |
| call_args = mock_ws_manager.broadcast.call_args | |
| # Should include session ID in channel | |
| assert "session:session-abc" in call_args[0][0] | |
| async def test_multiple_sessions_isolated(self, mock_db_context, mock_ws_manager): | |
| """Test that different sessions are isolated.""" | |
| from tools.canvas_tool import present_chart | |
| # Present to session 1 | |
| await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}], | |
| session_id="session-1" | |
| ) | |
| # Present to session 2 | |
| await present_chart( | |
| user_id="test_user", | |
| chart_type="bar_chart", | |
| data=[{"category": "A", "value": 100}], | |
| session_id="session-2" | |
| ) | |
| # Verify two separate calls | |
| assert mock_ws_manager.broadcast.call_count == 2 | |
| # Check first call (session-1) | |
| first_call = mock_ws_manager.broadcast.call_args_list[0] | |
| assert "session:session-1" in first_call[0][0] | |
| # Check second call (session-2) | |
| second_call = mock_ws_manager.broadcast.call_args_list[1] | |
| assert "session:session-2" in second_call[0][0] | |
| # ============================================================================ | |
| # Test WebSocket Error Handling | |
| # ============================================================================ | |
| class TestWebSocketErrorHandling: | |
| """Test WebSocket error handling.""" | |
| async def test_broadcast_failure_graceful_degradation(self, mock_db_context): | |
| """Test that broadcast failures are handled gracefully.""" | |
| from tools.canvas_tool import present_chart | |
| # Mock broadcast to raise exception | |
| with patch('tools.canvas_tool.ws_manager') as mock_mgr: | |
| mock_mgr.broadcast = AsyncMock(side_effect=Exception("WebSocket connection failed")) | |
| result = await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}] | |
| ) | |
| # Should handle error and return failure result | |
| assert isinstance(result, dict) | |
| assert "success" in result | |
| async def test_broadcast_timeout_handling(self, mock_db_context): | |
| """Test handling of broadcast timeouts.""" | |
| from tools.canvas_tool import present_chart | |
| # Mock broadcast to timeout | |
| with patch('tools.canvas_tool.ws_manager') as mock_mgr: | |
| mock_mgr.broadcast = AsyncMock(side_effect=TimeoutError("Broadcast timeout")) | |
| result = await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}] | |
| ) | |
| # Should handle timeout gracefully | |
| assert isinstance(result, dict) | |
| async def test_multiple_broadcast_retries(self, mock_db_context, mock_ws_manager): | |
| """Test that broadcast attempts are made (if retry logic exists).""" | |
| from tools.canvas_tool import present_chart | |
| # Mock broadcast to fail once then succeed | |
| call_count = [0] | |
| async def failing_then_succeeding(*args, **kwargs): | |
| call_count[0] += 1 | |
| if call_count[0] == 1: | |
| raise Exception("Temporary failure") | |
| return None | |
| mock_ws_manager.broadcast.side_effect = failing_then_succeeding | |
| result = await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}] | |
| ) | |
| # Function should complete (may or may not have retry logic) | |
| assert isinstance(result, dict) | |
| # ============================================================================ | |
| # Test WebSocket Data Integrity | |
| # ============================================================================ | |
| class TestWebSocketDataIntegrity: | |
| """Test WebSocket broadcast data integrity.""" | |
| async def test_chart_data_integrity(self, mock_db_context, mock_ws_manager): | |
| """Test that chart data is correctly passed through WebSocket.""" | |
| from tools.canvas_tool import present_chart | |
| chart_data = [ | |
| {"x": "Jan", "y": 100}, | |
| {"x": "Feb", "y": 200}, | |
| {"x": "Mar", "y": 150} | |
| ] | |
| await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=chart_data, | |
| title="Monthly Sales" | |
| ) | |
| call_args = mock_ws_manager.broadcast.call_args | |
| broadcast_data = call_args[0][1]["data"]["data"] | |
| # Verify data integrity | |
| assert broadcast_data["data"] == chart_data | |
| assert broadcast_data["title"] == "Monthly Sales" | |
| async def test_form_schema_integrity(self, mock_db_context, mock_ws_manager): | |
| """Test that form schema is correctly passed through WebSocket.""" | |
| from tools.canvas_tool import present_form | |
| form_schema = { | |
| "fields": [ | |
| {"name": "email", "type": "email", "required": True}, | |
| {"name": "age", "type": "number", "min": 18} | |
| ] | |
| } | |
| await present_form( | |
| user_id="test_user", | |
| form_schema=form_schema, | |
| title="User Form" | |
| ) | |
| call_args = mock_ws_manager.broadcast.call_args | |
| broadcast_schema = call_args[0][1]["data"]["data"]["schema"] | |
| # Verify schema integrity | |
| assert broadcast_schema == form_schema | |
| async def test_canvas_id_consistency(self, mock_db_context, mock_ws_manager): | |
| """Test that canvas_id is consistent across presentation and audit.""" | |
| from tools.canvas_tool import present_chart | |
| result = await present_chart( | |
| user_id="test_user", | |
| chart_type="line_chart", | |
| data=[{"x": 1, "y": 2}] | |
| ) | |
| canvas_id = result["canvas_id"] | |
| # Verify canvas_id in broadcast (it's at data["canvas_id"], not data["data"]["canvas_id"]) | |
| call_args = mock_ws_manager.broadcast.call_args | |
| broadcast_canvas_id = call_args[0][1]["data"]["canvas_id"] | |
| assert canvas_id == broadcast_canvas_id | |