fixed tests
This commit is contained in:
parent
36e09b72ed
commit
4db53c93df
@ -51,59 +51,6 @@ This document lists all failed tests identified during the test run on October 1
|
|||||||
|
|
||||||
## 🔴 Critical Issues to Address First
|
## 🔴 Critical Issues to Address First
|
||||||
|
|
||||||
### 3. Authentication/Authorization Failures
|
|
||||||
|
|
||||||
**Priority:** HIGH
|
|
||||||
|
|
||||||
**Files Affected:**
|
|
||||||
|
|
||||||
- `tests/api/test_config_endpoints.py`
|
|
||||||
- `tests/api/test_download_endpoints.py`
|
|
||||||
- `tests/integration/test_auth_flow.py`
|
|
||||||
|
|
||||||
**Tests Failing:**
|
|
||||||
|
|
||||||
#### Config Endpoints (7 failures)
|
|
||||||
|
|
||||||
1. `test_validate_invalid_config`
|
|
||||||
2. `test_update_config_unauthorized`
|
|
||||||
3. `test_list_backups` (403 instead of 200)
|
|
||||||
4. `test_create_backup` (403 instead of expected)
|
|
||||||
5. `test_restore_backup` (403 instead of expected)
|
|
||||||
6. `test_delete_backup` (403 instead of expected)
|
|
||||||
7. `test_config_persistence` (403 instead of expected)
|
|
||||||
|
|
||||||
#### Download Endpoints (2 failures)
|
|
||||||
|
|
||||||
8. `test_get_queue_status_unauthorized`
|
|
||||||
9. `test_queue_endpoints_require_auth`
|
|
||||||
|
|
||||||
#### Auth Flow Integration (43 failures)
|
|
||||||
|
|
||||||
Multiple test classes affected - all authentication flow tests
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
|
|
||||||
- 403 Forbidden responses when 200 expected
|
|
||||||
- Authentication/authorization not working as expected
|
|
||||||
- Tokens not being validated correctly
|
|
||||||
|
|
||||||
**Root Cause Options:**
|
|
||||||
|
|
||||||
1. Middleware not properly checking authentication
|
|
||||||
2. Test fixtures not setting up auth correctly
|
|
||||||
3. Token generation/validation broken
|
|
||||||
4. Session management issues
|
|
||||||
|
|
||||||
**Investigation Required:**
|
|
||||||
|
|
||||||
- Review `src/server/middleware/auth.py`
|
|
||||||
- Check `src/server/services/auth_service.py`
|
|
||||||
- Verify test fixtures for authentication setup
|
|
||||||
- Check if routes are properly protected
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. Frontend Integration Test Errors
|
### 4. Frontend Integration Test Errors
|
||||||
|
|
||||||
**Priority:** HIGH
|
**Priority:** HIGH
|
||||||
|
|||||||
@ -10,18 +10,14 @@ This module tests the complete WebSocket integration including:
|
|||||||
- Concurrent client management
|
- Concurrent client management
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import uuid
|
||||||
from typing import Any, Dict, List
|
from unittest.mock import AsyncMock
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import ASGITransport, AsyncClient
|
from httpx import ASGITransport, AsyncClient
|
||||||
from starlette.websockets import WebSocketDisconnect
|
|
||||||
|
|
||||||
from src.server.fastapi_app import app
|
from src.server.fastapi_app import app
|
||||||
from src.server.models.download import DownloadPriority, DownloadStatus
|
|
||||||
from src.server.services.auth_service import auth_service
|
from src.server.services.auth_service import auth_service
|
||||||
from src.server.services.progress_service import ProgressType
|
|
||||||
from src.server.services.websocket_service import (
|
from src.server.services.websocket_service import (
|
||||||
ConnectionManager,
|
ConnectionManager,
|
||||||
get_websocket_service,
|
get_websocket_service,
|
||||||
@ -85,10 +81,6 @@ class TestWebSocketConnection:
|
|||||||
|
|
||||||
async def test_websocket_endpoint_exists(self, client, auth_token):
|
async def test_websocket_endpoint_exists(self, client, auth_token):
|
||||||
"""Test that WebSocket endpoint is available."""
|
"""Test that WebSocket endpoint is available."""
|
||||||
# This test verifies the endpoint exists
|
|
||||||
# Full WebSocket testing requires WebSocket client
|
|
||||||
|
|
||||||
# Verify the WebSocket route is registered
|
|
||||||
routes = [route.path for route in app.routes]
|
routes = [route.path for route in app.routes]
|
||||||
websocket_routes = [
|
websocket_routes = [
|
||||||
path for path in routes if "ws" in path or "websocket" in path
|
path for path in routes if "ws" in path or "websocket" in path
|
||||||
@ -100,42 +92,44 @@ class TestWebSocketConnection:
|
|||||||
):
|
):
|
||||||
"""Test that connection manager tracks active connections."""
|
"""Test that connection manager tracks active connections."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
connection_id = "test-conn-1"
|
||||||
|
|
||||||
# Initially no connections
|
initial_count = await manager.get_connection_count()
|
||||||
initial_count = len(manager.active_connections)
|
|
||||||
|
|
||||||
# Add a connection
|
mock_websocket.accept = AsyncMock()
|
||||||
await manager.connect(mock_websocket, room="test-room")
|
await manager.connect(mock_websocket, connection_id)
|
||||||
|
|
||||||
assert len(manager.active_connections) == initial_count + 1
|
assert await manager.get_connection_count() == initial_count + 1
|
||||||
assert mock_websocket in manager.active_connections
|
assert connection_id in manager._active_connections
|
||||||
|
|
||||||
async def test_disconnect_removes_connection(
|
async def test_disconnect_removes_connection(
|
||||||
self, websocket_service, mock_websocket
|
self, websocket_service, mock_websocket
|
||||||
):
|
):
|
||||||
"""Test that disconnecting removes connection from manager."""
|
"""Test that disconnecting removes connection from manager."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
connection_id = "test-conn-2"
|
||||||
|
|
||||||
# Connect
|
mock_websocket.accept = AsyncMock()
|
||||||
await manager.connect(mock_websocket, room="test-room")
|
await manager.connect(mock_websocket, connection_id)
|
||||||
assert mock_websocket in manager.active_connections
|
assert connection_id in manager._active_connections
|
||||||
|
|
||||||
# Disconnect
|
await manager.disconnect(connection_id)
|
||||||
manager.disconnect(mock_websocket)
|
assert connection_id not in manager._active_connections
|
||||||
assert mock_websocket not in manager.active_connections
|
|
||||||
|
|
||||||
async def test_room_assignment_on_connection(
|
async def test_room_assignment_on_connection(
|
||||||
self, websocket_service, mock_websocket
|
self, websocket_service, mock_websocket
|
||||||
):
|
):
|
||||||
"""Test that connections are assigned to rooms."""
|
"""Test that connections can join rooms."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
connection_id = "test-conn-3"
|
||||||
room = "test-room-1"
|
room = "test-room-1"
|
||||||
|
|
||||||
await manager.connect(mock_websocket, room=room)
|
mock_websocket.accept = AsyncMock()
|
||||||
|
await manager.connect(mock_websocket, connection_id)
|
||||||
|
await manager.join_room(connection_id, room)
|
||||||
|
|
||||||
# Verify connection is in the room
|
|
||||||
assert room in manager._rooms
|
assert room in manager._rooms
|
||||||
assert mock_websocket in manager._rooms[room]
|
assert connection_id in manager._rooms[room]
|
||||||
|
|
||||||
async def test_multiple_rooms_support(
|
async def test_multiple_rooms_support(
|
||||||
self, websocket_service
|
self, websocket_service
|
||||||
@ -144,15 +138,20 @@ class TestWebSocketConnection:
|
|||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
ws1 = AsyncMock()
|
ws1 = AsyncMock()
|
||||||
|
ws1.accept = AsyncMock()
|
||||||
ws2 = AsyncMock()
|
ws2 = AsyncMock()
|
||||||
|
ws2.accept = AsyncMock()
|
||||||
ws3 = AsyncMock()
|
ws3 = AsyncMock()
|
||||||
|
ws3.accept = AsyncMock()
|
||||||
|
|
||||||
# Connect to different rooms
|
await manager.connect(ws1, "conn-1")
|
||||||
await manager.connect(ws1, room="room-1")
|
await manager.connect(ws2, "conn-2")
|
||||||
await manager.connect(ws2, room="room-2")
|
await manager.connect(ws3, "conn-3")
|
||||||
await manager.connect(ws3, room="room-1")
|
|
||||||
|
await manager.join_room("conn-1", "room-1")
|
||||||
|
await manager.join_room("conn-2", "room-2")
|
||||||
|
await manager.join_room("conn-3", "room-1")
|
||||||
|
|
||||||
# Verify room structure
|
|
||||||
assert "room-1" in manager._rooms
|
assert "room-1" in manager._rooms
|
||||||
assert "room-2" in manager._rooms
|
assert "room-2" in manager._rooms
|
||||||
assert len(manager._rooms["room-1"]) == 2
|
assert len(manager._rooms["room-1"]) == 2
|
||||||
@ -168,20 +167,20 @@ class TestMessageBroadcasting:
|
|||||||
"""Test broadcasting message to all connected clients."""
|
"""Test broadcasting message to all connected clients."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Create mock connections
|
|
||||||
ws1 = AsyncMock()
|
ws1 = AsyncMock()
|
||||||
|
ws1.accept = AsyncMock()
|
||||||
ws2 = AsyncMock()
|
ws2 = AsyncMock()
|
||||||
|
ws2.accept = AsyncMock()
|
||||||
ws3 = AsyncMock()
|
ws3 = AsyncMock()
|
||||||
|
ws3.accept = AsyncMock()
|
||||||
|
|
||||||
await manager.connect(ws1, room="room-1")
|
await manager.connect(ws1, "conn-1")
|
||||||
await manager.connect(ws2, room="room-1")
|
await manager.connect(ws2, "conn-2")
|
||||||
await manager.connect(ws3, room="room-2")
|
await manager.connect(ws3, "conn-3")
|
||||||
|
|
||||||
# Broadcast to all
|
|
||||||
message = {"type": "test", "data": "broadcast to all"}
|
message = {"type": "test", "data": "broadcast to all"}
|
||||||
await manager.broadcast(message)
|
await manager.broadcast(message)
|
||||||
|
|
||||||
# All connections should receive message
|
|
||||||
ws1.send_json.assert_called_once()
|
ws1.send_json.assert_called_once()
|
||||||
ws2.send_json.assert_called_once()
|
ws2.send_json.assert_called_once()
|
||||||
ws3.send_json.assert_called_once()
|
ws3.send_json.assert_called_once()
|
||||||
@ -193,18 +192,23 @@ class TestMessageBroadcasting:
|
|||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
ws1 = AsyncMock()
|
ws1 = AsyncMock()
|
||||||
|
ws1.accept = AsyncMock()
|
||||||
ws2 = AsyncMock()
|
ws2 = AsyncMock()
|
||||||
|
ws2.accept = AsyncMock()
|
||||||
ws3 = AsyncMock()
|
ws3 = AsyncMock()
|
||||||
|
ws3.accept = AsyncMock()
|
||||||
|
|
||||||
await manager.connect(ws1, room="downloads")
|
await manager.connect(ws1, "conn-1")
|
||||||
await manager.connect(ws2, room="downloads")
|
await manager.connect(ws2, "conn-2")
|
||||||
await manager.connect(ws3, room="system")
|
await manager.connect(ws3, "conn-3")
|
||||||
|
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
await manager.join_room("conn-2", "downloads")
|
||||||
|
await manager.join_room("conn-3", "system")
|
||||||
|
|
||||||
# Broadcast to specific room
|
|
||||||
message = {"type": "download_progress", "data": {}}
|
message = {"type": "download_progress", "data": {}}
|
||||||
await manager.broadcast_to_room(message, room="downloads")
|
await manager.broadcast_to_room(message, room="downloads")
|
||||||
|
|
||||||
# Only room members should receive
|
|
||||||
assert ws1.send_json.call_count == 1
|
assert ws1.send_json.call_count == 1
|
||||||
assert ws2.send_json.call_count == 1
|
assert ws2.send_json.call_count == 1
|
||||||
assert ws3.send_json.call_count == 0
|
assert ws3.send_json.call_count == 0
|
||||||
@ -215,7 +219,8 @@ class TestMessageBroadcasting:
|
|||||||
"""Test broadcasting JSON-formatted messages."""
|
"""Test broadcasting JSON-formatted messages."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="test")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "queue_update",
|
"type": "queue_update",
|
||||||
@ -237,20 +242,19 @@ class TestMessageBroadcasting:
|
|||||||
"""Test that broadcasting handles disconnected clients gracefully."""
|
"""Test that broadcasting handles disconnected clients gracefully."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Mock connection that will fail
|
|
||||||
failing_ws = AsyncMock()
|
failing_ws = AsyncMock()
|
||||||
|
failing_ws.accept = AsyncMock()
|
||||||
failing_ws.send_json.side_effect = RuntimeError("Connection closed")
|
failing_ws.send_json.side_effect = RuntimeError("Connection closed")
|
||||||
|
|
||||||
working_ws = AsyncMock()
|
working_ws = AsyncMock()
|
||||||
|
working_ws.accept = AsyncMock()
|
||||||
|
|
||||||
await manager.connect(failing_ws, room="test")
|
await manager.connect(failing_ws, "conn-1")
|
||||||
await manager.connect(working_ws, room="test")
|
await manager.connect(working_ws, "conn-2")
|
||||||
|
|
||||||
# Broadcast should handle failure
|
|
||||||
message = {"type": "test", "data": "test"}
|
message = {"type": "test", "data": "test"}
|
||||||
await manager.broadcast(message)
|
await manager.broadcast(message)
|
||||||
|
|
||||||
# Working connection should still receive
|
|
||||||
working_ws.send_json.assert_called_once()
|
working_ws.send_json.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
@ -263,9 +267,10 @@ class TestProgressIntegration:
|
|||||||
"""Test that download progress updates broadcast via WebSocket."""
|
"""Test that download progress updates broadcast via WebSocket."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="downloads")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
|
||||||
# Simulate progress update broadcast
|
|
||||||
message = {
|
message = {
|
||||||
"type": "download_progress",
|
"type": "download_progress",
|
||||||
"data": {
|
"data": {
|
||||||
@ -287,7 +292,9 @@ class TestProgressIntegration:
|
|||||||
"""Test download completion notification via WebSocket."""
|
"""Test download completion notification via WebSocket."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="downloads")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "download_complete",
|
"type": "download_complete",
|
||||||
@ -308,7 +315,9 @@ class TestProgressIntegration:
|
|||||||
"""Test download failure notification via WebSocket."""
|
"""Test download failure notification via WebSocket."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="downloads")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "download_failed",
|
"type": "download_failed",
|
||||||
@ -333,7 +342,9 @@ class TestQueueStatusBroadcasting:
|
|||||||
"""Test broadcasting queue status updates."""
|
"""Test broadcasting queue status updates."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="queue")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "queue")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "queue_status",
|
"type": "queue_status",
|
||||||
@ -356,7 +367,9 @@ class TestQueueStatusBroadcasting:
|
|||||||
"""Test notification when item is added to queue."""
|
"""Test notification when item is added to queue."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="queue")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "queue")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "queue_item_added",
|
"type": "queue_item_added",
|
||||||
@ -378,7 +391,9 @@ class TestQueueStatusBroadcasting:
|
|||||||
"""Test notification when item is removed from queue."""
|
"""Test notification when item is removed from queue."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="queue")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "queue")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "queue_item_removed",
|
"type": "queue_item_removed",
|
||||||
@ -403,9 +418,14 @@ class TestSystemMessaging:
|
|||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
ws1 = AsyncMock()
|
ws1 = AsyncMock()
|
||||||
|
ws1.accept = AsyncMock()
|
||||||
ws2 = AsyncMock()
|
ws2 = AsyncMock()
|
||||||
await manager.connect(ws1, room="system")
|
ws2.accept = AsyncMock()
|
||||||
await manager.connect(ws2, room="system")
|
await manager.connect(ws1, "conn-1")
|
||||||
|
await manager.connect(ws2, "conn-2")
|
||||||
|
|
||||||
|
await manager.join_room("conn-1", "system")
|
||||||
|
await manager.join_room("conn-2", "system")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "system_notification",
|
"type": "system_notification",
|
||||||
@ -427,7 +447,9 @@ class TestSystemMessaging:
|
|||||||
"""Test broadcasting error messages."""
|
"""Test broadcasting error messages."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="errors")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "errors")
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"type": "error",
|
"type": "error",
|
||||||
@ -452,16 +474,17 @@ class TestConcurrentConnections:
|
|||||||
"""Test multiple clients receiving broadcasts in same room."""
|
"""Test multiple clients receiving broadcasts in same room."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Create multiple connections
|
clients = []
|
||||||
clients = [AsyncMock() for _ in range(5)]
|
for i in range(5):
|
||||||
for client in clients:
|
ws = AsyncMock()
|
||||||
await manager.connect(client, room="shared-room")
|
ws.accept = AsyncMock()
|
||||||
|
clients.append(ws)
|
||||||
|
await manager.connect(ws, f"conn-{i}")
|
||||||
|
await manager.join_room(f"conn-{i}", "shared-room")
|
||||||
|
|
||||||
# Broadcast to all
|
|
||||||
message = {"type": "test", "data": "multi-client test"}
|
message = {"type": "test", "data": "multi-client test"}
|
||||||
await manager.broadcast_to_room(message, room="shared-room")
|
await manager.broadcast_to_room(message, room="shared-room")
|
||||||
|
|
||||||
# All clients should receive
|
|
||||||
for client in clients:
|
for client in clients:
|
||||||
client.send_json.assert_called_once_with(message)
|
client.send_json.assert_called_once_with(message)
|
||||||
|
|
||||||
@ -471,16 +494,21 @@ class TestConcurrentConnections:
|
|||||||
"""Test concurrent broadcasts to different rooms."""
|
"""Test concurrent broadcasts to different rooms."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Setup rooms with clients
|
|
||||||
downloads_ws = AsyncMock()
|
downloads_ws = AsyncMock()
|
||||||
|
downloads_ws.accept = AsyncMock()
|
||||||
queue_ws = AsyncMock()
|
queue_ws = AsyncMock()
|
||||||
|
queue_ws.accept = AsyncMock()
|
||||||
system_ws = AsyncMock()
|
system_ws = AsyncMock()
|
||||||
|
system_ws.accept = AsyncMock()
|
||||||
|
|
||||||
await manager.connect(downloads_ws, room="downloads")
|
await manager.connect(downloads_ws, "conn-1")
|
||||||
await manager.connect(queue_ws, room="queue")
|
await manager.connect(queue_ws, "conn-2")
|
||||||
await manager.connect(system_ws, room="system")
|
await manager.connect(system_ws, "conn-3")
|
||||||
|
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
await manager.join_room("conn-2", "queue")
|
||||||
|
await manager.join_room("conn-3", "system")
|
||||||
|
|
||||||
# Concurrent broadcasts
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
manager.broadcast_to_room(
|
manager.broadcast_to_room(
|
||||||
{"type": "download_progress"}, "downloads"
|
{"type": "download_progress"}, "downloads"
|
||||||
@ -493,7 +521,6 @@ class TestConcurrentConnections:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Each client should receive only their room's message
|
|
||||||
downloads_ws.send_json.assert_called_once()
|
downloads_ws.send_json.assert_called_once()
|
||||||
queue_ws.send_json.assert_called_once()
|
queue_ws.send_json.assert_called_once()
|
||||||
system_ws.send_json.assert_called_once()
|
system_ws.send_json.assert_called_once()
|
||||||
@ -508,13 +535,12 @@ class TestConnectionErrorHandling:
|
|||||||
"""Test handling of message send failures."""
|
"""Test handling of message send failures."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Connection that will fail on send
|
|
||||||
failing_ws = AsyncMock()
|
failing_ws = AsyncMock()
|
||||||
|
failing_ws.accept = AsyncMock()
|
||||||
failing_ws.send_json.side_effect = RuntimeError("Send failed")
|
failing_ws.send_json.side_effect = RuntimeError("Send failed")
|
||||||
|
|
||||||
await manager.connect(failing_ws, room="test")
|
await manager.connect(failing_ws, "conn-1")
|
||||||
|
|
||||||
# Should handle error gracefully
|
|
||||||
message = {"type": "test", "data": "test"}
|
message = {"type": "test", "data": "test"}
|
||||||
try:
|
try:
|
||||||
await manager.broadcast_to_room(message, room="test")
|
await manager.broadcast_to_room(message, room="test")
|
||||||
@ -527,23 +553,23 @@ class TestConnectionErrorHandling:
|
|||||||
"""Test handling multiple concurrent send failures."""
|
"""Test handling multiple concurrent send failures."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Multiple failing connections
|
|
||||||
failing_clients = []
|
failing_clients = []
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
|
ws.accept = AsyncMock()
|
||||||
ws.send_json.side_effect = RuntimeError(f"Failed {i}")
|
ws.send_json.side_effect = RuntimeError(f"Failed {i}")
|
||||||
failing_clients.append(ws)
|
failing_clients.append(ws)
|
||||||
await manager.connect(ws, room="test")
|
await manager.connect(ws, f"conn-{i}")
|
||||||
|
await manager.join_room(f"conn-{i}", "test")
|
||||||
|
|
||||||
# Add one working connection
|
|
||||||
working_ws = AsyncMock()
|
working_ws = AsyncMock()
|
||||||
await manager.connect(working_ws, room="test")
|
working_ws.accept = AsyncMock()
|
||||||
|
await manager.connect(working_ws, "conn-working")
|
||||||
|
await manager.join_room("conn-working", "test")
|
||||||
|
|
||||||
# Broadcast should continue despite failures
|
|
||||||
message = {"type": "test", "data": "test"}
|
message = {"type": "test", "data": "test"}
|
||||||
await manager.broadcast_to_room(message, room="test")
|
await manager.broadcast_to_room(message, room="test")
|
||||||
|
|
||||||
# Working connection should still receive
|
|
||||||
working_ws.send_json.assert_called_once()
|
working_ws.send_json.assert_called_once()
|
||||||
|
|
||||||
async def test_cleanup_after_disconnect(
|
async def test_cleanup_after_disconnect(
|
||||||
@ -552,16 +578,18 @@ class TestConnectionErrorHandling:
|
|||||||
"""Test proper cleanup after client disconnect."""
|
"""Test proper cleanup after client disconnect."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
|
ws.accept = AsyncMock()
|
||||||
room = "test-room"
|
room = "test-room"
|
||||||
|
connection_id = "test-conn"
|
||||||
|
|
||||||
# Connect and then disconnect
|
await manager.connect(ws, connection_id)
|
||||||
await manager.connect(ws, room=room)
|
await manager.join_room(connection_id, room)
|
||||||
manager.disconnect(ws)
|
|
||||||
|
|
||||||
# Verify cleanup
|
await manager.disconnect(connection_id)
|
||||||
assert ws not in manager.active_connections
|
|
||||||
|
assert connection_id not in manager._active_connections
|
||||||
if room in manager._rooms:
|
if room in manager._rooms:
|
||||||
assert ws not in manager._rooms[room]
|
assert connection_id not in manager._rooms[room]
|
||||||
|
|
||||||
|
|
||||||
class TestMessageFormatting:
|
class TestMessageFormatting:
|
||||||
@ -573,9 +601,9 @@ class TestMessageFormatting:
|
|||||||
"""Test that messages have required structure."""
|
"""Test that messages have required structure."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="test")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
|
||||||
# Valid message structure
|
|
||||||
valid_message = {
|
valid_message = {
|
||||||
"type": "test_message",
|
"type": "test_message",
|
||||||
"data": {"key": "value"},
|
"data": {"key": "value"},
|
||||||
@ -590,7 +618,8 @@ class TestMessageFormatting:
|
|||||||
"""Test broadcasting different message types."""
|
"""Test broadcasting different message types."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="test")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
|
||||||
message_types = [
|
message_types = [
|
||||||
"download_progress",
|
"download_progress",
|
||||||
@ -605,7 +634,6 @@ class TestMessageFormatting:
|
|||||||
message = {"type": msg_type, "data": {}}
|
message = {"type": msg_type, "data": {}}
|
||||||
await manager.broadcast(message)
|
await manager.broadcast(message)
|
||||||
|
|
||||||
# Should have received all message types
|
|
||||||
assert ws.send_json.call_count == len(message_types)
|
assert ws.send_json.call_count == len(message_types)
|
||||||
|
|
||||||
|
|
||||||
@ -652,15 +680,14 @@ class TestRoomManagement:
|
|||||||
"""Test that room is created when first client connects."""
|
"""Test that room is created when first client connects."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
|
ws.accept = AsyncMock()
|
||||||
room = "new-room"
|
room = "new-room"
|
||||||
|
|
||||||
# Room should not exist initially
|
|
||||||
assert room not in manager._rooms
|
assert room not in manager._rooms
|
||||||
|
|
||||||
# Connect to room
|
await manager.connect(ws, "conn-1")
|
||||||
await manager.connect(ws, room=room)
|
await manager.join_room("conn-1", room)
|
||||||
|
|
||||||
# Room should now exist
|
|
||||||
assert room in manager._rooms
|
assert room in manager._rooms
|
||||||
|
|
||||||
async def test_room_cleanup_when_empty(
|
async def test_room_cleanup_when_empty(
|
||||||
@ -669,14 +696,14 @@ class TestRoomManagement:
|
|||||||
"""Test that empty rooms are cleaned up."""
|
"""Test that empty rooms are cleaned up."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
|
ws.accept = AsyncMock()
|
||||||
room = "temp-room"
|
room = "temp-room"
|
||||||
|
connection_id = "conn-1"
|
||||||
|
|
||||||
# Connect and disconnect
|
await manager.connect(ws, connection_id)
|
||||||
await manager.connect(ws, room=room)
|
await manager.join_room(connection_id, room)
|
||||||
manager.disconnect(ws)
|
await manager.disconnect(connection_id)
|
||||||
|
|
||||||
# Room should be cleaned up if empty
|
|
||||||
# (Implementation may vary)
|
|
||||||
if room in manager._rooms:
|
if room in manager._rooms:
|
||||||
assert len(manager._rooms[room]) == 0
|
assert len(manager._rooms[room]) == 0
|
||||||
|
|
||||||
@ -686,13 +713,13 @@ class TestRoomManagement:
|
|||||||
"""Test client room membership."""
|
"""Test client room membership."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
|
ws.accept = AsyncMock()
|
||||||
|
|
||||||
# Connect to room
|
await manager.connect(ws, "conn-1")
|
||||||
await manager.connect(ws, room="room-1")
|
await manager.join_room("conn-1", "room-1")
|
||||||
|
|
||||||
# Verify in room
|
|
||||||
assert "room-1" in manager._rooms
|
assert "room-1" in manager._rooms
|
||||||
assert ws in manager._rooms["room-1"]
|
assert "conn-1" in manager._rooms["room-1"]
|
||||||
|
|
||||||
|
|
||||||
class TestCompleteWebSocketWorkflow:
|
class TestCompleteWebSocketWorkflow:
|
||||||
@ -704,17 +731,15 @@ class TestCompleteWebSocketWorkflow:
|
|||||||
"""Test complete workflow of download notifications."""
|
"""Test complete workflow of download notifications."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
ws = AsyncMock()
|
ws = AsyncMock()
|
||||||
await manager.connect(ws, room="downloads")
|
ws.accept = AsyncMock()
|
||||||
|
await manager.connect(ws, "conn-1")
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
|
||||||
# Simulate download lifecycle
|
|
||||||
|
|
||||||
# 1. Download started
|
|
||||||
await manager.broadcast_to_room(
|
await manager.broadcast_to_room(
|
||||||
{"type": "download_started", "data": {"item_id": "dl-1"}},
|
{"type": "download_started", "data": {"item_id": "dl-1"}},
|
||||||
"downloads"
|
"downloads"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Progress updates
|
|
||||||
for progress in [25, 50, 75]:
|
for progress in [25, 50, 75]:
|
||||||
await manager.broadcast_to_room(
|
await manager.broadcast_to_room(
|
||||||
{
|
{
|
||||||
@ -724,13 +749,11 @@ class TestCompleteWebSocketWorkflow:
|
|||||||
"downloads"
|
"downloads"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Download complete
|
|
||||||
await manager.broadcast_to_room(
|
await manager.broadcast_to_room(
|
||||||
{"type": "download_complete", "data": {"item_id": "dl-1"}},
|
{"type": "download_complete", "data": {"item_id": "dl-1"}},
|
||||||
"downloads"
|
"downloads"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Client should have received all notifications
|
|
||||||
assert ws.send_json.call_count == 5
|
assert ws.send_json.call_count == 5
|
||||||
|
|
||||||
async def test_multi_room_workflow(
|
async def test_multi_room_workflow(
|
||||||
@ -739,16 +762,21 @@ class TestCompleteWebSocketWorkflow:
|
|||||||
"""Test workflow involving multiple rooms."""
|
"""Test workflow involving multiple rooms."""
|
||||||
manager = websocket_service.manager
|
manager = websocket_service.manager
|
||||||
|
|
||||||
# Setup clients in different rooms
|
|
||||||
download_ws = AsyncMock()
|
download_ws = AsyncMock()
|
||||||
|
download_ws.accept = AsyncMock()
|
||||||
queue_ws = AsyncMock()
|
queue_ws = AsyncMock()
|
||||||
|
queue_ws.accept = AsyncMock()
|
||||||
system_ws = AsyncMock()
|
system_ws = AsyncMock()
|
||||||
|
system_ws.accept = AsyncMock()
|
||||||
|
|
||||||
await manager.connect(download_ws, room="downloads")
|
await manager.connect(download_ws, "conn-1")
|
||||||
await manager.connect(queue_ws, room="queue")
|
await manager.connect(queue_ws, "conn-2")
|
||||||
await manager.connect(system_ws, room="system")
|
await manager.connect(system_ws, "conn-3")
|
||||||
|
|
||||||
|
await manager.join_room("conn-1", "downloads")
|
||||||
|
await manager.join_room("conn-2", "queue")
|
||||||
|
await manager.join_room("conn-3", "system")
|
||||||
|
|
||||||
# Broadcast to each room
|
|
||||||
await manager.broadcast_to_room(
|
await manager.broadcast_to_room(
|
||||||
{"type": "download_update"}, "downloads"
|
{"type": "download_update"}, "downloads"
|
||||||
)
|
)
|
||||||
@ -759,7 +787,6 @@ class TestCompleteWebSocketWorkflow:
|
|||||||
{"type": "system_update"}, "system"
|
{"type": "system_update"}, "system"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Each client should only receive their room's messages
|
|
||||||
download_ws.send_json.assert_called_once()
|
download_ws.send_json.assert_called_once()
|
||||||
queue_ws.send_json.assert_called_once()
|
queue_ws.send_json.assert_called_once()
|
||||||
system_ws.send_json.assert_called_once()
|
system_ws.send_json.assert_called_once()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user