chore: apply pending code updates

This commit is contained in:
2026-03-17 11:39:27 +01:00
parent e5fae0a0a2
commit 92bd55ada1
45 changed files with 2236 additions and 2130 deletions

View File

@@ -4,12 +4,15 @@ This test verifies that the /api/anime/add endpoint can handle
multiple concurrent requests without blocking.
"""
import asyncio
import logging
import time
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from httpx import ASGITransport, AsyncClient
logger = logging.getLogger(__name__)
from src.server.fastapi_app import app
from src.server.services.auth_service import auth_service
from src.server.services.background_loader_service import get_background_loader_service
@@ -103,7 +106,7 @@ async def test_concurrent_anime_add_requests(authenticated_client):
f"indicating possible blocking issues"
)
print(f"3 concurrent anime add requests completed in {total_time:.2f}s")
logger.info("3 concurrent anime add requests completed in %.2fs", total_time)
@pytest.mark.asyncio
@@ -130,4 +133,4 @@ async def test_same_anime_concurrent_add(authenticated_client):
keys = [r.json().get("key") for r in responses]
assert keys[0] == keys[1], "Both responses should have the same key"
print(f"Concurrent same-anime requests handled correctly: {statuses}")
logger.info("Concurrent same-anime requests handled correctly: %s", statuses)

View File

@@ -4,6 +4,7 @@ This module tests the performance characteristics of batch NFO creation
including concurrent operations, TMDB API request optimization, and memory usage.
"""
import asyncio
import logging
import time
from pathlib import Path
from typing import List
@@ -15,6 +16,8 @@ from src.core.services.nfo_service import NFOService
from src.server.api.nfo import batch_create_nfo
from src.server.models.nfo import NFOBatchCreateRequest
logger = logging.getLogger(__name__)
class TestConcurrentNFOCreation:
"""Test performance of concurrent NFO creation operations."""
@@ -83,8 +86,11 @@ class TestConcurrentNFOCreation:
# Concurrent should take roughly (num_series / 5) * 0.1 = 0.2s
assert elapsed_time < 1.0, "Concurrency not providing speedup"
print(f"\nPerformance: {num_series} series in {elapsed_time:.2f}s")
print(f"Rate: {num_series / elapsed_time:.2f} series/second")
logger.info("Batch NFO creation completed", extra={"num_series": num_series, "elapsed_s": elapsed_time})
logger.debug(
"Batch NFO creation rate",
extra={"series_per_second": num_series / elapsed_time},
)
@pytest.mark.asyncio
async def test_concurrent_nfo_creation_50_series(self):

View File

@@ -4,6 +4,7 @@ This module tests the performance characteristics of WebSocket connections
including concurrent clients, message throughput, and progress update throttling.
"""
import asyncio
import logging
import time
from typing import List
from unittest.mock import AsyncMock, Mock
@@ -12,6 +13,8 @@ import pytest
from src.server.services.websocket_service import WebSocketService
logger = logging.getLogger(__name__)
class MockWebSocket:
"""Mock WebSocket client for testing."""
@@ -82,8 +85,14 @@ class TestWebSocketConcurrentClients:
for i in range(num_clients):
await websocket_service.disconnect(f"client_{i:03d}")
print(f"\n100 clients: Broadcast in {elapsed_time:.2f}s")
print(f"Average per client: {elapsed_time / num_clients * 1000:.2f}ms")
logger.info("Broadcast completed for %d clients", num_clients, extra={"elapsed_s": elapsed_time})
logger.debug(
"Broadcast performance per client",
extra={
"num_clients": num_clients,
"avg_ms_per_client": elapsed_time / num_clients * 1000,
},
)
@pytest.mark.asyncio
async def test_200_concurrent_clients_scalability(self):
@@ -114,7 +123,7 @@ class TestWebSocketConcurrentClients:
for i in range(num_clients):
await websocket_service.disconnect(f"client_{i:03d}")
print(f"\n200 clients: Broadcast in {elapsed_time:.2f}s")
logger.info("Broadcast completed for %d clients", num_clients, extra={"elapsed_s": elapsed_time})
@pytest.mark.asyncio
async def test_connection_pool_efficiency(self):
@@ -144,8 +153,8 @@ class TestWebSocketConcurrentClients:
for i in range(num_clients):
await websocket_service.disconnect(f"client_{i:02d}")
print(f"\nConnected {num_clients} clients in {connection_time:.3f}s")
print(f"Average: {connection_time / num_clients * 1000:.2f}ms per connection")
logger.info("Connected %d clients in %.3fs", num_clients, connection_time)
logger.info("Average: %.2fms per connection", connection_time / num_clients * 1000)
class TestMessageThroughput:
@@ -192,8 +201,13 @@ class TestMessageThroughput:
for i in range(num_clients):
await websocket_service.disconnect(f"client_{i}")
print(f"\nThroughput: {messages_per_second:.2f} messages/second")
print(f"Total: {num_messages} messages to {num_clients} clients in {elapsed_time:.2f}s")
logger.info("Throughput: %.2f messages/second", messages_per_second)
logger.info(
"Total: %d messages to %d clients in %.2fs",
num_messages,
num_clients,
elapsed_time,
)
@pytest.mark.asyncio
async def test_high_frequency_updates(self):
@@ -234,7 +248,7 @@ class TestMessageThroughput:
for i in range(5):
await websocket_service.disconnect(f"client_{i}")
print(f"\nHigh-frequency: {updates_per_second:.2f} updates/second")
logger.info("High-frequency: %.2f updates/second", updates_per_second)
@pytest.mark.asyncio
async def test_burst_message_handling(self):
@@ -275,7 +289,7 @@ class TestMessageThroughput:
for i in range(num_clients):
await websocket_service.disconnect(f"client_{i:02d}")
print(f"\nBurst: {num_messages} messages in {elapsed_time:.2f}s")
logger.info("Burst: %d messages in %.2fs", num_messages, elapsed_time)
class TestProgressUpdateThrottling:
@@ -313,7 +327,10 @@ class TestProgressUpdateThrottling:
await websocket_service.disconnect("test_client")
print(f"\nThrottling: {len(client.received_messages)} updates sent (100 possible)")
logger.info(
"Throttling: %d updates sent (100 possible)",
len(client.received_messages),
)
@pytest.mark.asyncio
async def test_throttling_reduces_network_load(self):
@@ -356,7 +373,11 @@ class TestProgressUpdateThrottling:
for i in range(10):
await websocket_service.disconnect(f"client_{i}")
print(f"\nThrottling: {throttled_updates}/1000 updates sent ({reduction_percent:.1f}% reduction)")
logger.info(
"Throttling: %d/1000 updates sent (%.1f%% reduction)",
throttled_updates,
reduction_percent,
)
class TestRoomIsolation:
@@ -402,7 +423,7 @@ class TestRoomIsolation:
for i in range(clients_per_room):
await websocket_service.disconnect(f"{room}_client_{i:02d}")
print(f"\nRoom isolation: 3 rooms × 30 clients in {elapsed_time:.2f}s")
logger.info("Room isolation: 3 rooms × 30 clients in %.2fs", elapsed_time)
@pytest.mark.asyncio
async def test_selective_room_broadcast_performance(self):
@@ -435,7 +456,7 @@ class TestRoomIsolation:
for i in range(clients_per_room):
await websocket_service.disconnect(f"{room}_{i:02d}")
print(f"\nSelective broadcast: 25/100 clients in {elapsed_time:.3f}s")
logger.info("Selective broadcast: 25/100 clients in %.3fs", elapsed_time)
class TestConnectionStability:
@@ -472,7 +493,7 @@ class TestConnectionStability:
# All connections should be cleaned up
assert len(websocket_service.manager._active_connections) == 0
print(f"\nRapid cycles: {cycles_per_second:.2f} cycles/second")
logger.info("Rapid cycles: %.2f cycles/second", cycles_per_second)
@pytest.mark.asyncio
async def test_concurrent_connect_disconnect(self):
@@ -497,7 +518,7 @@ class TestConnectionStability:
# All should be cleaned up
assert len(websocket_service.manager._active_connections) == 0
print(f"\nConcurrent ops: 30 clients in {elapsed_time:.2f}s")
logger.info("Concurrent ops: 30 clients in %.2fs", elapsed_time)
class TestMemoryEfficiency:
@@ -533,8 +554,8 @@ class TestMemoryEfficiency:
for i in range(100):
await websocket_service.disconnect(f"mem_client_{i:03d}")
print(f"\nMemory: {memory_increase_mb:.2f}MB for 100 connections")
print(f"Per connection: {per_connection_kb:.2f}KB")
logger.info("Memory: %.2fMB for 100 connections", memory_increase_mb)
logger.info("Per connection: %.2fKB", per_connection_kb)
@pytest.mark.asyncio
async def test_message_queue_memory_efficiency(self):
@@ -567,5 +588,5 @@ class TestMemoryEfficiency:
await websocket_service.disconnect("queue_test")
print(f"\nMessage queue: {total_size} bytes for 100 messages")
print(f"Average: {total_size / 100:.2f} bytes/message")
logger.info("Message queue: %d bytes for 100 messages", total_size)
logger.info("Average: %.2f bytes/message", total_size / 100)

View File

@@ -1,6 +1,7 @@
"""Unit test for NFOService.update_tvshow_nfo() - tests XML parsing logic."""
import asyncio
import logging
import shutil
import tempfile
from pathlib import Path
@@ -8,6 +9,8 @@ from pathlib import Path
import pytest
from lxml import etree
logger = logging.getLogger(__name__)
from src.core.services.nfo_service import NFOService
from src.core.services.tmdb_client import TMDBAPIError
@@ -51,7 +54,7 @@ def test_parse_nfo_with_uniqueid():
break
assert tmdb_id == 1429, f"Expected TMDB ID 1429, got {tmdb_id}"
print(f"Successfully parsed TMDB ID from uniqueid: {tmdb_id}")
logger.info("Successfully parsed TMDB ID from uniqueid: %s", tmdb_id)
finally:
shutil.rmtree(temp_dir)
@@ -92,7 +95,7 @@ def test_parse_nfo_with_tmdbid_element():
tmdb_id = int(tmdbid_elem.text)
assert tmdb_id == 12345, f"Expected TMDB ID 12345, got {tmdb_id}"
print(f"Successfully parsed TMDB ID from tmdbid element: {tmdb_id}")
logger.info("Successfully parsed TMDB ID from tmdbid element: %s", tmdb_id)
finally:
shutil.rmtree(temp_dir)
@@ -131,7 +134,7 @@ def test_parse_nfo_without_tmdb_id():
tmdb_id = int(tmdbid_elem.text)
assert tmdb_id is None, "Should not have found TMDB ID"
print("Correctly identified NFO without TMDB ID")
logger.info("Correctly identified NFO without TMDB ID")
finally:
shutil.rmtree(temp_dir)
@@ -157,22 +160,23 @@ def test_parse_invalid_xml():
tree = etree.parse(str(nfo_path))
assert False, "Should have raised XMLSyntaxError"
except etree.XMLSyntaxError:
print("Correctly raised XMLSyntaxError for invalid XML")
logger.info("Correctly raised XMLSyntaxError for invalid XML")
finally:
shutil.rmtree(temp_dir)
if __name__ == "__main__":
print("Testing NFO XML parsing logic...")
print()
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger.info("Testing NFO XML parsing logic...")
logger.info("")
test_parse_nfo_with_uniqueid()
test_parse_nfo_with_tmdbid_element()
test_parse_nfo_without_tmdb_id()
test_parse_invalid_xml()
print()
print("=" * 60)
print("ALL TESTS PASSED")
print("=" * 60)
logger.info("")
logger.info("%s", "=" * 60)
logger.info("ALL TESTS PASSED")
logger.info("%s", "=" * 60)

View File

@@ -5,11 +5,14 @@ each other. The background loader should process multiple series simultaneously
rather than sequentially.
"""
import asyncio
import logging
from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
logger = logging.getLogger(__name__)
from src.server.services.background_loader_service import (
BackgroundLoaderService,
LoadingStatus,
@@ -162,9 +165,9 @@ async def test_parallel_anime_additions(
f"(indicating sequential processing)"
)
print(f"Parallel execution verified:")
print(f" - Start time difference: {start_diff:.3f}s")
print(f" - Total duration: {total_duration:.3f}s")
logger.info("Parallel execution verified")
logger.info("Start time difference: %.3fs", start_diff)
logger.info("Total duration: %.3fs", total_duration)
@pytest.mark.asyncio