fix: Lint fixes of last tests

This commit is contained in:
JSC
2025-08-01 09:30:15 +02:00
parent dc29915fbc
commit fceff92ca1
20 changed files with 326 additions and 313 deletions

View File

@@ -1,4 +1,5 @@
"""Tests for authentication service."""
# ruff: noqa: S106, SLF001, PLC0415
import pytest
import pytest_asyncio
@@ -10,6 +11,11 @@ from app.models.user import User
from app.schemas.auth import UserLoginRequest, UserRegisterRequest
from app.services.auth import AuthService
# Constants
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_404_NOT_FOUND = 404
class TestAuthService:
"""Test authentication service operations."""
@@ -62,7 +68,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.register(request)
assert exc_info.value.status_code == 400
assert exc_info.value.status_code == HTTP_400_BAD_REQUEST
assert "Email address is already registered" in exc_info.value.detail
@pytest.mark.asyncio
@@ -100,7 +106,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.login(request)
assert exc_info.value.status_code == 401
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Invalid email or password" in exc_info.value.detail
@pytest.mark.asyncio
@@ -115,7 +121,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.login(request)
assert exc_info.value.status_code == 401
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Invalid email or password" in exc_info.value.detail
@pytest.mark.asyncio
@@ -138,7 +144,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.login(request)
assert exc_info.value.status_code == 401
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Account is deactivated" in exc_info.value.detail
@pytest.mark.asyncio
@@ -161,7 +167,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.login(request)
assert exc_info.value.status_code == 401
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Invalid email or password" in exc_info.value.detail
@pytest.mark.asyncio
@@ -184,7 +190,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.get_current_user(99999)
assert exc_info.value.status_code == 404
assert exc_info.value.status_code == HTTP_404_NOT_FOUND
assert "User not found" in exc_info.value.detail
@pytest.mark.asyncio
@@ -205,7 +211,7 @@ class TestAuthService:
with pytest.raises(HTTPException) as exc_info:
await auth_service.get_current_user(user_id)
assert exc_info.value.status_code == 401
assert exc_info.value.status_code == HTTP_401_UNAUTHORIZED
assert "Account is deactivated" in exc_info.value.detail
@pytest.mark.asyncio

View File

@@ -1,4 +1,5 @@
"""Tests for credit service."""
# ruff: noqa: ANN001, ANN201, PLR2004, E501
import json
from unittest.mock import AsyncMock, patch
@@ -39,7 +40,7 @@ class TestCreditService:
)
@pytest.mark.asyncio
async def test_check_credits_sufficient(self, credit_service, sample_user):
async def test_check_credits_sufficient(self, credit_service, sample_user) -> None:
"""Test checking credits when user has sufficient credits."""
mock_session = credit_service.db_session_factory()
@@ -55,7 +56,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_check_credits_insufficient(self, credit_service):
async def test_check_credits_insufficient(self, credit_service) -> None:
"""Test checking credits when user has insufficient credits."""
mock_session = credit_service.db_session_factory()
poor_user = User(
@@ -78,7 +79,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_check_credits_user_not_found(self, credit_service):
async def test_check_credits_user_not_found(self, credit_service) -> None:
"""Test checking credits when user is not found."""
mock_session = credit_service.db_session_factory()
@@ -93,7 +94,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user):
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user) -> None:
"""Test successful credit validation and reservation."""
mock_session = credit_service.db_session_factory()
@@ -112,7 +113,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_validate_and_reserve_credits_insufficient(self, credit_service):
async def test_validate_and_reserve_credits_insufficient(self, credit_service) -> None:
"""Test credit validation with insufficient credits."""
mock_session = credit_service.db_session_factory()
poor_user = User(
@@ -139,7 +140,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_validate_and_reserve_credits_user_not_found(self, credit_service):
async def test_validate_and_reserve_credits_user_not_found(self, credit_service) -> None:
"""Test credit validation when user is not found."""
mock_session = credit_service.db_session_factory()
@@ -156,7 +157,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_deduct_credits_success(self, credit_service, sample_user):
async def test_deduct_credits_success(self, credit_service, sample_user) -> None:
"""Test successful credit deduction."""
mock_session = credit_service.db_session_factory()
@@ -167,7 +168,7 @@ class TestCreditService:
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.deduct_credits(
await credit_service.deduct_credits(
1, CreditActionType.VLC_PLAY_SOUND, success=True, metadata={"test": "data"},
)
@@ -202,7 +203,7 @@ class TestCreditService:
assert json.loads(added_transaction.metadata_json) == {"test": "data"}
@pytest.mark.asyncio
async def test_deduct_credits_failed_action_requires_success(self, credit_service, sample_user):
async def test_deduct_credits_failed_action_requires_success(self, credit_service, sample_user) -> None:
"""Test credit deduction when action failed but requires success."""
mock_session = credit_service.db_session_factory()
@@ -213,7 +214,7 @@ class TestCreditService:
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.deduct_credits(
await credit_service.deduct_credits(
1, CreditActionType.VLC_PLAY_SOUND, success=False, # Action failed
)
@@ -235,7 +236,7 @@ class TestCreditService:
assert added_transaction.success is False
@pytest.mark.asyncio
async def test_deduct_credits_insufficient(self, credit_service):
async def test_deduct_credits_insufficient(self, credit_service) -> None:
"""Test credit deduction with insufficient credits."""
mock_session = credit_service.db_session_factory()
poor_user = User(
@@ -266,7 +267,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_add_credits(self, credit_service, sample_user):
async def test_add_credits(self, credit_service, sample_user) -> None:
"""Test adding credits to user account."""
mock_session = credit_service.db_session_factory()
@@ -277,7 +278,7 @@ class TestCreditService:
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.add_credits(
await credit_service.add_credits(
1, 5, "Bonus credits", {"reason": "signup"},
)
@@ -308,7 +309,7 @@ class TestCreditService:
assert added_transaction.description == "Bonus credits"
@pytest.mark.asyncio
async def test_add_credits_invalid_amount(self, credit_service):
async def test_add_credits_invalid_amount(self, credit_service) -> None:
"""Test adding invalid amount of credits."""
with pytest.raises(ValueError, match="Amount must be positive"):
await credit_service.add_credits(1, 0, "Invalid")
@@ -317,7 +318,7 @@ class TestCreditService:
await credit_service.add_credits(1, -5, "Invalid")
@pytest.mark.asyncio
async def test_get_user_balance(self, credit_service, sample_user):
async def test_get_user_balance(self, credit_service, sample_user) -> None:
"""Test getting user credit balance."""
mock_session = credit_service.db_session_factory()
@@ -332,7 +333,7 @@ class TestCreditService:
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_get_user_balance_user_not_found(self, credit_service):
async def test_get_user_balance_user_not_found(self, credit_service) -> None:
"""Test getting balance for non-existent user."""
mock_session = credit_service.db_session_factory()
@@ -350,7 +351,7 @@ class TestCreditService:
class TestInsufficientCreditsError:
"""Test InsufficientCreditsError exception."""
def test_insufficient_credits_error_creation(self):
def test_insufficient_credits_error_creation(self) -> None:
"""Test creating InsufficientCreditsError."""
error = InsufficientCreditsError(5, 2)
assert error.required == 5

View File

@@ -1,4 +1,5 @@
"""Tests for extraction service."""
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001, E501
import tempfile
from pathlib import Path
@@ -26,13 +27,13 @@ class TestExtractionService:
with patch("app.services.extraction.Path.mkdir"):
return ExtractionService(mock_session)
def test_init(self, extraction_service):
def test_init(self, extraction_service) -> None:
"""Test service initialization."""
assert extraction_service.session is not None
assert extraction_service.extraction_repo is not None
assert extraction_service.sound_repo is not None
def test_sanitize_filename(self, extraction_service):
def test_sanitize_filename(self, extraction_service) -> None:
"""Test filename sanitization."""
test_cases = [
("Hello World", "Hello World"),
@@ -54,7 +55,7 @@ class TestExtractionService:
@pytest.mark.asyncio
async def test_detect_service_info_youtube(
self, mock_ydl_class, extraction_service,
):
) -> None:
"""Test service detection for YouTube."""
mock_ydl = Mock()
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
@@ -79,7 +80,7 @@ class TestExtractionService:
@pytest.mark.asyncio
async def test_detect_service_info_failure(
self, mock_ydl_class, extraction_service,
):
) -> None:
"""Test service detection failure."""
mock_ydl = Mock()
mock_ydl_class.return_value.__enter__.return_value = mock_ydl
@@ -90,7 +91,7 @@ class TestExtractionService:
assert result is None
@pytest.mark.asyncio
async def test_create_extraction_success(self, extraction_service):
async def test_create_extraction_success(self, extraction_service) -> None:
"""Test successful extraction creation."""
url = "https://www.youtube.com/watch?v=test123"
user_id = 1
@@ -118,7 +119,7 @@ class TestExtractionService:
assert result["status"] == "pending"
@pytest.mark.asyncio
async def test_create_extraction_basic(self, extraction_service):
async def test_create_extraction_basic(self, extraction_service) -> None:
"""Test basic extraction creation without validation."""
url = "https://www.youtube.com/watch?v=test123"
user_id = 1
@@ -144,7 +145,7 @@ class TestExtractionService:
assert result["status"] == "pending"
@pytest.mark.asyncio
async def test_create_extraction_any_url(self, extraction_service):
async def test_create_extraction_any_url(self, extraction_service) -> None:
"""Test extraction creation accepts any URL."""
url = "https://invalid.url"
user_id = 1
@@ -170,7 +171,7 @@ class TestExtractionService:
assert result["status"] == "pending"
@pytest.mark.asyncio
async def test_process_extraction_with_service_detection(self, extraction_service):
async def test_process_extraction_with_service_detection(self, extraction_service) -> None:
"""Test extraction processing with service detection."""
extraction_id = 1
@@ -211,8 +212,8 @@ class TestExtractionService:
patch.object(
extraction_service, "_create_sound_record",
) as mock_create_sound,
patch.object(extraction_service, "_normalize_sound") as mock_normalize,
patch.object(extraction_service, "_add_to_main_playlist") as mock_playlist,
patch.object(extraction_service, "_normalize_sound"),
patch.object(extraction_service, "_add_to_main_playlist"),
):
mock_sound = Sound(id=42, type="EXT", name="Test", filename="test.mp3")
mock_extract.return_value = (Path("/fake/audio.mp3"), None)
@@ -234,7 +235,7 @@ class TestExtractionService:
assert result["service_id"] == "test123"
assert result["title"] == "Test Video"
def test_ensure_unique_filename(self, extraction_service):
def test_ensure_unique_filename(self, extraction_service) -> None:
"""Test unique filename generation."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
@@ -255,7 +256,7 @@ class TestExtractionService:
assert result == expected_2
@pytest.mark.asyncio
async def test_create_sound_record(self, extraction_service):
async def test_create_sound_record(self, extraction_service) -> None:
"""Test sound record creation."""
# Create temporary audio file
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f:
@@ -317,7 +318,7 @@ class TestExtractionService:
audio_path.unlink()
@pytest.mark.asyncio
async def test_normalize_sound_success(self, extraction_service):
async def test_normalize_sound_success(self, extraction_service) -> None:
"""Test sound normalization."""
sound = Sound(
id=1,
@@ -349,7 +350,7 @@ class TestExtractionService:
mock_normalizer.normalize_sound.assert_called_once_with(sound)
@pytest.mark.asyncio
async def test_normalize_sound_failure(self, extraction_service):
async def test_normalize_sound_failure(self, extraction_service) -> None:
"""Test sound normalization failure."""
sound = Sound(
id=1,
@@ -381,7 +382,7 @@ class TestExtractionService:
mock_normalizer.normalize_sound.assert_called_once_with(sound)
@pytest.mark.asyncio
async def test_get_extraction_by_id(self, extraction_service):
async def test_get_extraction_by_id(self, extraction_service) -> None:
"""Test getting extraction by ID."""
extraction = Extraction(
id=1,
@@ -409,7 +410,7 @@ class TestExtractionService:
assert result["sound_id"] == 42
@pytest.mark.asyncio
async def test_get_extraction_by_id_not_found(self, extraction_service):
async def test_get_extraction_by_id_not_found(self, extraction_service) -> None:
"""Test getting extraction by ID when not found."""
extraction_service.extraction_repo.get_by_id = AsyncMock(return_value=None)
@@ -418,7 +419,7 @@ class TestExtractionService:
assert result is None
@pytest.mark.asyncio
async def test_get_user_extractions(self, extraction_service):
async def test_get_user_extractions(self, extraction_service) -> None:
"""Test getting user extractions."""
extractions = [
Extraction(
@@ -455,7 +456,7 @@ class TestExtractionService:
assert result[1]["title"] == "Test Video 2"
@pytest.mark.asyncio
async def test_get_pending_extractions(self, extraction_service):
async def test_get_pending_extractions(self, extraction_service) -> None:
"""Test getting pending extractions."""
pending_extractions = [
Extraction(

View File

@@ -1,4 +1,5 @@
"""Tests for extraction background processor."""
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001
from unittest.mock import AsyncMock, Mock, patch
@@ -16,7 +17,7 @@ class TestExtractionProcessor:
# Use a custom processor instance to avoid affecting the global one
return ExtractionProcessor()
def test_init(self, processor):
def test_init(self, processor) -> None:
"""Test processor initialization."""
assert processor.max_concurrent > 0
assert len(processor.running_extractions) == 0
@@ -25,12 +26,12 @@ class TestExtractionProcessor:
assert processor.processor_task is None
@pytest.mark.asyncio
async def test_start_and_stop(self, processor):
async def test_start_and_stop(self, processor) -> None:
"""Test starting and stopping the processor."""
# Mock the _process_queue method to avoid actual processing
with patch.object(
processor, "_process_queue", new_callable=AsyncMock,
) as mock_process:
):
# Start the processor
await processor.start()
assert processor.processor_task is not None
@@ -41,7 +42,7 @@ class TestExtractionProcessor:
assert processor.processor_task.done()
@pytest.mark.asyncio
async def test_start_already_running(self, processor):
async def test_start_already_running(self, processor) -> None:
"""Test starting processor when already running."""
with patch.object(processor, "_process_queue", new_callable=AsyncMock):
# Start first time
@@ -56,7 +57,7 @@ class TestExtractionProcessor:
await processor.stop()
@pytest.mark.asyncio
async def test_queue_extraction(self, processor):
async def test_queue_extraction(self, processor) -> None:
"""Test queuing an extraction."""
extraction_id = 123
@@ -66,7 +67,7 @@ class TestExtractionProcessor:
assert extraction_id not in processor.running_extractions
@pytest.mark.asyncio
async def test_queue_extraction_already_running(self, processor):
async def test_queue_extraction_already_running(self, processor) -> None:
"""Test queuing an extraction that's already running."""
extraction_id = 123
processor.running_extractions.add(extraction_id)
@@ -75,7 +76,7 @@ class TestExtractionProcessor:
# Should still be in running extractions
assert extraction_id in processor.running_extractions
def test_get_status(self, processor):
def test_get_status(self, processor) -> None:
"""Test getting processor status."""
status = processor.get_status()
@@ -89,7 +90,7 @@ class TestExtractionProcessor:
assert status["currently_processing"] == 0
assert status["available_slots"] == processor.max_concurrent
def test_get_status_with_running_extractions(self, processor):
def test_get_status_with_running_extractions(self, processor) -> None:
"""Test getting processor status with running extractions."""
processor.running_extractions.add(123)
processor.running_extractions.add(456)
@@ -101,7 +102,7 @@ class TestExtractionProcessor:
assert 123 in status["processing_ids"]
assert 456 in status["processing_ids"]
def test_on_extraction_completed(self, processor):
def test_on_extraction_completed(self, processor) -> None:
"""Test extraction completion callback."""
extraction_id = 123
processor.running_extractions.add(extraction_id)
@@ -115,7 +116,7 @@ class TestExtractionProcessor:
# Should be removed from running extractions
assert extraction_id not in processor.running_extractions
def test_on_extraction_completed_with_exception(self, processor):
def test_on_extraction_completed_with_exception(self, processor) -> None:
"""Test extraction completion callback with exception."""
extraction_id = 123
processor.running_extractions.add(extraction_id)
@@ -130,7 +131,7 @@ class TestExtractionProcessor:
assert extraction_id not in processor.running_extractions
@pytest.mark.asyncio
async def test_process_single_extraction_success(self, processor):
async def test_process_single_extraction_success(self, processor) -> None:
"""Test processing a single extraction successfully."""
extraction_id = 123
@@ -157,7 +158,7 @@ class TestExtractionProcessor:
mock_service.process_extraction.assert_called_once_with(extraction_id)
@pytest.mark.asyncio
async def test_process_single_extraction_failure(self, processor):
async def test_process_single_extraction_failure(self, processor) -> None:
"""Test processing a single extraction with failure."""
extraction_id = 123
@@ -183,7 +184,7 @@ class TestExtractionProcessor:
mock_service.process_extraction.assert_called_once_with(extraction_id)
@pytest.mark.asyncio
async def test_process_pending_extractions_no_slots(self, processor):
async def test_process_pending_extractions_no_slots(self, processor) -> None:
"""Test processing when no slots are available."""
# Fill all slots
for i in range(processor.max_concurrent):
@@ -213,7 +214,7 @@ class TestExtractionProcessor:
assert 100 not in processor.running_extractions
@pytest.mark.asyncio
async def test_process_pending_extractions_with_slots(self, processor):
async def test_process_pending_extractions_with_slots(self, processor) -> None:
"""Test processing when slots are available."""
# Mock extraction service
mock_service = Mock()
@@ -230,7 +231,7 @@ class TestExtractionProcessor:
) as mock_session_class,
patch.object(
processor, "_process_single_extraction", new_callable=AsyncMock,
) as mock_process,
),
patch(
"app.services.extraction_processor.ExtractionService",
return_value=mock_service,
@@ -254,7 +255,7 @@ class TestExtractionProcessor:
assert mock_create_task.call_count == 2
@pytest.mark.asyncio
async def test_process_pending_extractions_respect_limit(self, processor):
async def test_process_pending_extractions_respect_limit(self, processor) -> None:
"""Test that processing respects concurrency limit."""
# Set max concurrent to 1 for this test
processor.max_concurrent = 1
@@ -275,7 +276,7 @@ class TestExtractionProcessor:
) as mock_session_class,
patch.object(
processor, "_process_single_extraction", new_callable=AsyncMock,
) as mock_process,
),
patch(
"app.services.extraction_processor.ExtractionService",
return_value=mock_service,

View File

@@ -1,4 +1,5 @@
"""Tests for OAuth service."""
# ruff: noqa: ANN001, PLR2004, ANN401, PT011, ANN202, ANN003, ARG001
from typing import Any
from unittest.mock import AsyncMock, Mock, patch

View File

@@ -1,4 +1,5 @@
"""Tests for player service."""
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, SIM117, ARG005
import asyncio
import threading
@@ -24,7 +25,7 @@ from app.services.player import (
class TestPlayerState:
"""Test player state data structure."""
def test_init_creates_default_state(self):
def test_init_creates_default_state(self) -> None:
"""Test that player state initializes with default values."""
state = PlayerState()
@@ -42,7 +43,7 @@ class TestPlayerState:
assert state.playlist_duration == 0
assert state.playlist_sounds == []
def test_to_dict_serializes_correctly(self):
def test_to_dict_serializes_correctly(self) -> None:
"""Test that player state serializes to dict correctly."""
state = PlayerState()
state.status = PlayerStatus.PLAYING
@@ -70,7 +71,7 @@ class TestPlayerState:
assert result["playlist"]["length"] == 5
assert result["playlist"]["duration"] == 150000
def test_serialize_sound_with_sound_object(self):
def test_serialize_sound_with_sound_object(self) -> None:
"""Test serializing a sound object."""
state = PlayerState()
sound = Sound(
@@ -95,7 +96,7 @@ class TestPlayerState:
assert result["thumbnail"] == "test.jpg"
assert result["play_count"] == 5
def test_serialize_sound_with_none(self):
def test_serialize_sound_with_none(self) -> None:
"""Test serializing None sound."""
state = PlayerState()
result = state._serialize_sound(None)
@@ -133,10 +134,9 @@ class TestPlayerService:
@pytest.fixture
def player_service(self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager):
"""Create a player service instance for testing."""
service = PlayerService(mock_db_session_factory)
return service
return PlayerService(mock_db_session_factory)
def test_init_creates_player_service(self, mock_db_session_factory, mock_vlc_instance):
def test_init_creates_player_service(self, mock_db_session_factory, mock_vlc_instance) -> None:
"""Test that player service initializes correctly."""
with patch("app.services.player.socket_manager"):
service = PlayerService(mock_db_session_factory)
@@ -153,7 +153,7 @@ class TestPlayerService:
assert service._loop is None
@pytest.mark.asyncio
async def test_start_initializes_service(self, player_service, mock_vlc_instance):
async def test_start_initializes_service(self, player_service, mock_vlc_instance) -> None:
"""Test that start method initializes the service."""
with patch.object(player_service, "reload_playlist", new_callable=AsyncMock):
await player_service.start()
@@ -165,7 +165,7 @@ class TestPlayerService:
player_service._player.audio_set_volume.assert_called_once_with(50)
@pytest.mark.asyncio
async def test_stop_cleans_up_service(self, player_service):
async def test_stop_cleans_up_service(self, player_service) -> None:
"""Test that stop method cleans up the service."""
# Setup initial state
player_service._is_running = True
@@ -180,7 +180,7 @@ class TestPlayerService:
player_service._player.release.assert_called_once()
@pytest.mark.asyncio
async def test_play_new_track(self, player_service, mock_vlc_instance):
async def test_play_new_track(self, player_service, mock_vlc_instance) -> None:
"""Test playing a new track."""
# Setup test sound
sound = Sound(
@@ -212,7 +212,7 @@ class TestPlayerService:
assert 1 in player_service._play_time_tracking
@pytest.mark.asyncio
async def test_play_resume_from_pause(self, player_service):
async def test_play_resume_from_pause(self, player_service) -> None:
"""Test resuming playback from pause."""
# Setup paused state
sound = Sound(id=1, name="Test Song", filename="test.mp3", duration=30000)
@@ -230,7 +230,7 @@ class TestPlayerService:
player_service._player.play.assert_called_once()
@pytest.mark.asyncio
async def test_play_invalid_index(self, player_service):
async def test_play_invalid_index(self, player_service) -> None:
"""Test playing with invalid index raises ValueError."""
player_service.state.playlist_sounds = []
@@ -238,7 +238,7 @@ class TestPlayerService:
await player_service.play(0)
@pytest.mark.asyncio
async def test_pause_when_playing(self, player_service):
async def test_pause_when_playing(self, player_service) -> None:
"""Test pausing when currently playing."""
player_service.state.status = PlayerStatus.PLAYING
@@ -249,7 +249,7 @@ class TestPlayerService:
player_service._player.pause.assert_called_once()
@pytest.mark.asyncio
async def test_pause_when_not_playing(self, player_service):
async def test_pause_when_not_playing(self, player_service) -> None:
"""Test pausing when not playing does nothing."""
player_service.state.status = PlayerStatus.STOPPED
@@ -260,7 +260,7 @@ class TestPlayerService:
mock_broadcast.assert_not_called()
@pytest.mark.asyncio
async def test_stop_playback(self, player_service):
async def test_stop_playback(self, player_service) -> None:
"""Test stopping playback."""
player_service.state.status = PlayerStatus.PLAYING
player_service.state.current_sound_position = 5000
@@ -274,7 +274,7 @@ class TestPlayerService:
player_service._player.stop.assert_called_once()
@pytest.mark.asyncio
async def test_next_track(self, player_service):
async def test_next_track(self, player_service) -> None:
"""Test skipping to next track."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
@@ -286,7 +286,7 @@ class TestPlayerService:
mock_play.assert_called_once_with(1)
@pytest.mark.asyncio
async def test_previous_track(self, player_service):
async def test_previous_track(self, player_service) -> None:
"""Test going to previous track."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3")
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3")
@@ -298,7 +298,7 @@ class TestPlayerService:
mock_play.assert_called_once_with(0)
@pytest.mark.asyncio
async def test_seek_position(self, player_service):
async def test_seek_position(self, player_service) -> None:
"""Test seeking to specific position."""
player_service.state.status = PlayerStatus.PLAYING
player_service.state.current_sound_duration = 30000
@@ -311,7 +311,7 @@ class TestPlayerService:
assert player_service.state.current_sound_position == 15000
@pytest.mark.asyncio
async def test_seek_when_stopped(self, player_service):
async def test_seek_when_stopped(self, player_service) -> None:
"""Test seeking when stopped does nothing."""
player_service.state.status = PlayerStatus.STOPPED
@@ -322,7 +322,7 @@ class TestPlayerService:
mock_broadcast.assert_not_called()
@pytest.mark.asyncio
async def test_set_volume(self, player_service):
async def test_set_volume(self, player_service) -> None:
"""Test setting volume."""
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
await player_service.set_volume(75)
@@ -331,7 +331,7 @@ class TestPlayerService:
player_service._player.audio_set_volume.assert_called_once_with(75)
@pytest.mark.asyncio
async def test_set_volume_clamping(self, player_service):
async def test_set_volume_clamping(self, player_service) -> None:
"""Test volume clamping to valid range."""
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
# Test upper bound
@@ -343,7 +343,7 @@ class TestPlayerService:
assert player_service.state.volume == 0
@pytest.mark.asyncio
async def test_set_mode(self, player_service):
async def test_set_mode(self, player_service) -> None:
"""Test setting playback mode."""
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
await player_service.set_mode(PlayerMode.LOOP)
@@ -351,7 +351,7 @@ class TestPlayerService:
assert player_service.state.mode == PlayerMode.LOOP
@pytest.mark.asyncio
async def test_reload_playlist(self, player_service):
async def test_reload_playlist(self, player_service) -> None:
"""Test reloading playlist from database."""
mock_session = AsyncMock()
player_service.db_session_factory = lambda: mock_session
@@ -383,7 +383,7 @@ class TestPlayerService:
assert player_service.state.playlist_duration == 75000
@pytest.mark.asyncio
async def test_handle_playlist_id_changed(self, player_service):
async def test_handle_playlist_id_changed(self, player_service) -> None:
"""Test handling when playlist ID changes."""
# Setup initial state
player_service.state.status = PlayerStatus.PLAYING
@@ -405,7 +405,7 @@ class TestPlayerService:
assert player_service.state.current_sound_id == 1
@pytest.mark.asyncio
async def test_handle_playlist_id_changed_empty_playlist(self, player_service):
async def test_handle_playlist_id_changed_empty_playlist(self, player_service) -> None:
"""Test handling playlist ID change with empty playlist."""
player_service.state.status = PlayerStatus.PLAYING
@@ -418,7 +418,7 @@ class TestPlayerService:
assert player_service.state.current_sound_id is None
@pytest.mark.asyncio
async def test_handle_same_playlist_track_exists_same_index(self, player_service):
async def test_handle_same_playlist_track_exists_same_index(self, player_service) -> None:
"""Test handling same playlist when track exists at same index."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
@@ -431,7 +431,7 @@ class TestPlayerService:
assert player_service.state.current_sound == sound1
@pytest.mark.asyncio
async def test_handle_same_playlist_track_exists_different_index(self, player_service):
async def test_handle_same_playlist_track_exists_different_index(self, player_service) -> None:
"""Test handling same playlist when track exists at different index."""
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
sound2 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
@@ -444,7 +444,7 @@ class TestPlayerService:
assert player_service.state.current_sound == sound2
@pytest.mark.asyncio
async def test_handle_same_playlist_track_not_found(self, player_service):
async def test_handle_same_playlist_track_not_found(self, player_service) -> None:
"""Test handling same playlist when current track no longer exists."""
player_service.state.status = PlayerStatus.PLAYING
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
@@ -456,7 +456,7 @@ class TestPlayerService:
mock_removed.assert_called_once_with(1, sounds)
@pytest.mark.asyncio
async def test_handle_track_removed_with_sounds(self, player_service):
async def test_handle_track_removed_with_sounds(self, player_service) -> None:
"""Test handling when current track is removed with sounds available."""
player_service.state.status = PlayerStatus.PLAYING
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
@@ -471,7 +471,7 @@ class TestPlayerService:
assert player_service.state.current_sound_id == 2
@pytest.mark.asyncio
async def test_handle_track_removed_empty_playlist(self, player_service):
async def test_handle_track_removed_empty_playlist(self, player_service) -> None:
"""Test handling when current track is removed with empty playlist."""
player_service.state.status = PlayerStatus.PLAYING
@@ -483,7 +483,7 @@ class TestPlayerService:
assert player_service.state.current_sound is None
assert player_service.state.current_sound_id is None
def test_update_playlist_state(self, player_service):
def test_update_playlist_state(self, player_service) -> None:
"""Test updating playlist state information."""
mock_playlist = Mock()
mock_playlist.id = 5
@@ -501,7 +501,7 @@ class TestPlayerService:
assert player_service.state.playlist_length == 2
assert player_service.state.playlist_duration == 75000
def test_find_sound_index_found(self, player_service):
def test_find_sound_index_found(self, player_service) -> None:
"""Test finding sound index when sound exists."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
@@ -510,7 +510,7 @@ class TestPlayerService:
index = player_service._find_sound_index(2, sounds)
assert index == 1
def test_find_sound_index_not_found(self, player_service):
def test_find_sound_index_not_found(self, player_service) -> None:
"""Test finding sound index when sound doesn't exist."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
sounds = [sound1]
@@ -518,7 +518,7 @@ class TestPlayerService:
index = player_service._find_sound_index(999, sounds)
assert index is None
def test_set_first_track_as_current(self, player_service):
def test_set_first_track_as_current(self, player_service) -> None:
"""Test setting first track as current."""
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
@@ -530,7 +530,7 @@ class TestPlayerService:
assert player_service.state.current_sound == sound1
assert player_service.state.current_sound_id == 1
def test_clear_current_track(self, player_service):
def test_clear_current_track(self, player_service) -> None:
"""Test clearing current track state."""
# Set some initial state
player_service.state.current_sound_index = 2
@@ -544,7 +544,7 @@ class TestPlayerService:
assert player_service.state.current_sound_id is None
@pytest.mark.asyncio
async def test_reload_playlist_different_id_scenario(self, player_service):
async def test_reload_playlist_different_id_scenario(self, player_service) -> None:
"""Test complete reload scenario when playlist ID changes."""
# Setup current state
player_service.state.playlist_id = 1
@@ -580,7 +580,7 @@ class TestPlayerService:
assert player_service.state.current_sound_id == 1
@pytest.mark.asyncio
async def test_reload_playlist_same_id_track_moved(self, player_service):
async def test_reload_playlist_same_id_track_moved(self, player_service) -> None:
"""Test reload when playlist ID same but track moved to different index."""
# Setup current state
player_service.state.playlist_id = 1
@@ -616,7 +616,7 @@ class TestPlayerService:
assert player_service.state.current_sound == sound1
def test_get_next_index_continuous_mode(self, player_service):
def test_get_next_index_continuous_mode(self, player_service) -> None:
"""Test getting next index in continuous mode."""
player_service.state.mode = PlayerMode.CONTINUOUS
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -628,7 +628,7 @@ class TestPlayerService:
# Test end of playlist
assert player_service._get_next_index(2) is None
def test_get_next_index_loop_mode(self, player_service):
def test_get_next_index_loop_mode(self, player_service) -> None:
"""Test getting next index in loop mode."""
player_service.state.mode = PlayerMode.LOOP
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -640,7 +640,7 @@ class TestPlayerService:
# Test wrapping to beginning
assert player_service._get_next_index(2) == 0
def test_get_next_index_loop_one_mode(self, player_service):
def test_get_next_index_loop_one_mode(self, player_service) -> None:
"""Test getting next index in loop one mode."""
player_service.state.mode = PlayerMode.LOOP_ONE
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -650,7 +650,7 @@ class TestPlayerService:
assert player_service._get_next_index(1) == 1
assert player_service._get_next_index(2) == 2
def test_get_next_index_single_mode(self, player_service):
def test_get_next_index_single_mode(self, player_service) -> None:
"""Test getting next index in single mode."""
player_service.state.mode = PlayerMode.SINGLE
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -660,7 +660,7 @@ class TestPlayerService:
assert player_service._get_next_index(1) is None
assert player_service._get_next_index(2) is None
def test_get_next_index_random_mode(self, player_service):
def test_get_next_index_random_mode(self, player_service) -> None:
"""Test getting next index in random mode."""
player_service.state.mode = PlayerMode.RANDOM
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -674,7 +674,7 @@ class TestPlayerService:
# Should exclude current index
mock_choice.assert_called_once_with([1, 2])
def test_get_previous_index(self, player_service):
def test_get_previous_index(self, player_service) -> None:
"""Test getting previous index."""
player_service.state.playlist_sounds = [Mock(), Mock(), Mock()]
@@ -690,7 +690,7 @@ class TestPlayerService:
player_service.state.mode = PlayerMode.LOOP
assert player_service._get_previous_index(0) == 2
def test_update_play_time(self, player_service):
def test_update_play_time(self, player_service) -> None:
"""Test updating play time tracking."""
# Setup state
player_service.state.status = PlayerStatus.PLAYING
@@ -716,7 +716,7 @@ class TestPlayerService:
assert tracking["last_update"] == current_time
@pytest.mark.asyncio
async def test_record_play_count(self, player_service):
async def test_record_play_count(self, player_service) -> None:
"""Test recording play count for a sound."""
mock_session = AsyncMock()
player_service.db_session_factory = lambda: mock_session
@@ -742,7 +742,7 @@ class TestPlayerService:
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()
def test_get_state(self, player_service):
def test_get_state(self, player_service) -> None:
"""Test getting current player state."""
result = player_service.get_state()
assert isinstance(result, dict)
@@ -750,7 +750,7 @@ class TestPlayerService:
assert "mode" in result
assert "volume" in result
def test_uses_shared_sound_path_utility(self, player_service):
def test_uses_shared_sound_path_utility(self, player_service) -> None:
"""Test that player service uses the shared sound path utility."""
sound = Sound(
id=1,
@@ -767,7 +767,7 @@ class TestPlayerService:
mock_path.return_value = mock_file_path
# This should fail because file doesn't exist
result = asyncio.run(player_service.play(0))
asyncio.run(player_service.play(0))
# Verify the utility was called
mock_path.assert_called_once_with(sound)
@@ -776,7 +776,7 @@ class TestPlayerServiceGlobalFunctions:
"""Test global player service functions."""
@pytest.mark.asyncio
async def test_initialize_player_service(self):
async def test_initialize_player_service(self) -> None:
"""Test initializing global player service."""
mock_factory = Mock()
@@ -790,7 +790,7 @@ class TestPlayerServiceGlobalFunctions:
mock_service.start.assert_called_once()
@pytest.mark.asyncio
async def test_shutdown_player_service(self):
async def test_shutdown_player_service(self) -> None:
"""Test shutting down global player service."""
# Mock global player service exists
with patch("app.services.player.player_service") as mock_global:
@@ -802,7 +802,7 @@ class TestPlayerServiceGlobalFunctions:
await shutdown_player_service()
mock_service.stop.assert_called_once()
def test_get_player_service_success(self):
def test_get_player_service_success(self) -> None:
"""Test getting player service when initialized."""
mock_service = Mock()
@@ -810,7 +810,7 @@ class TestPlayerServiceGlobalFunctions:
result = get_player_service()
assert result is mock_service
def test_get_player_service_not_initialized(self):
def test_get_player_service_not_initialized(self) -> None:
"""Test getting player service when not initialized."""
with patch("app.services.player.player_service", None):
with pytest.raises(RuntimeError, match="Player service not initialized"):

View File

@@ -1,4 +1,5 @@
"""Tests for playlist service."""
# ruff: noqa: ANN001, ARG002, PLR2004, E501, PLC0415
from collections.abc import AsyncGenerator

View File

@@ -1,4 +1,5 @@
"""Tests for socket service."""
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, ANN202
from unittest.mock import AsyncMock, patch
@@ -22,7 +23,7 @@ class TestSocketManager:
socket_manager.sio = AsyncMock(spec=socketio.AsyncServer)
return socket_manager.sio
def test_init_creates_socket_server(self):
def test_init_creates_socket_server(self) -> None:
"""Test that socket manager initializes with proper configuration."""
manager = SocketManager()
@@ -33,7 +34,7 @@ class TestSocketManager:
assert len(manager.socket_users) == 0
@pytest.mark.asyncio
async def test_send_to_user_success(self, socket_manager, mock_sio):
async def test_send_to_user_success(self, socket_manager, mock_sio) -> None:
"""Test sending message to connected user."""
user_id = "123"
room_id = "user_123"
@@ -48,7 +49,7 @@ class TestSocketManager:
mock_sio.emit.assert_called_once_with(event, data, room=room_id)
@pytest.mark.asyncio
async def test_send_to_user_not_connected(self, socket_manager, mock_sio):
async def test_send_to_user_not_connected(self, socket_manager, mock_sio) -> None:
"""Test sending message to user who is not connected."""
user_id = "999"
event = "test_event"
@@ -60,7 +61,7 @@ class TestSocketManager:
mock_sio.emit.assert_not_called()
@pytest.mark.asyncio
async def test_broadcast_to_all(self, socket_manager, mock_sio):
async def test_broadcast_to_all(self, socket_manager, mock_sio) -> None:
"""Test broadcasting message to all users."""
event = "broadcast_event"
data = {"message": "announcement"}
@@ -69,7 +70,7 @@ class TestSocketManager:
mock_sio.emit.assert_called_once_with(event, data)
def test_get_connected_users(self, socket_manager):
def test_get_connected_users(self, socket_manager) -> None:
"""Test getting list of connected users."""
# Add some users
socket_manager.user_rooms["1"] = "user_1"
@@ -83,7 +84,7 @@ class TestSocketManager:
assert "2" in connected_users
assert "3" in connected_users
def test_get_room_info(self, socket_manager):
def test_get_room_info(self, socket_manager) -> None:
"""Test getting room information."""
# Add some users
socket_manager.user_rooms["1"] = "user_1"
@@ -99,7 +100,7 @@ class TestSocketManager:
@patch("app.services.socket.JWTUtils.decode_access_token")
async def test_connect_handler_success(
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
) -> None:
"""Test successful connection with valid token."""
# Setup mocks
mock_extract_token.return_value = "valid_token"
@@ -110,7 +111,6 @@ class TestSocketManager:
# Access the connect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func
@@ -134,7 +134,7 @@ class TestSocketManager:
@patch("app.services.socket.extract_access_token_from_cookies")
async def test_connect_handler_no_token(
self, mock_extract_token, socket_manager, mock_sio,
):
) -> None:
"""Test connection with no access token."""
# Setup mocks
mock_extract_token.return_value = None
@@ -144,7 +144,6 @@ class TestSocketManager:
# Access the connect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func
@@ -168,7 +167,7 @@ class TestSocketManager:
@patch("app.services.socket.JWTUtils.decode_access_token")
async def test_connect_handler_invalid_token(
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
) -> None:
"""Test connection with invalid token."""
# Setup mocks
mock_extract_token.return_value = "invalid_token"
@@ -179,7 +178,6 @@ class TestSocketManager:
# Access the connect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func
@@ -203,7 +201,7 @@ class TestSocketManager:
@patch("app.services.socket.JWTUtils.decode_access_token")
async def test_connect_handler_missing_user_id(
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
) -> None:
"""Test connection with token missing user ID."""
# Setup mocks
mock_extract_token.return_value = "token_without_user_id"
@@ -214,7 +212,6 @@ class TestSocketManager:
# Access the connect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func
@@ -234,7 +231,7 @@ class TestSocketManager:
assert len(socket_manager.user_rooms) == 0
@pytest.mark.asyncio
async def test_disconnect_handler(self, socket_manager, mock_sio):
async def test_disconnect_handler(self, socket_manager, mock_sio) -> None:
"""Test disconnect handler."""
# Setup initial state
socket_manager.socket_users["test_sid"] = "123"
@@ -242,7 +239,6 @@ class TestSocketManager:
# Access the disconnect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func
@@ -259,11 +255,10 @@ class TestSocketManager:
assert "123" not in socket_manager.user_rooms
@pytest.mark.asyncio
async def test_disconnect_handler_unknown_socket(self, socket_manager, mock_sio):
async def test_disconnect_handler_unknown_socket(self, socket_manager, mock_sio) -> None:
"""Test disconnect handler with unknown socket."""
# Access the disconnect handler directly
handlers = {}
original_event = socket_manager.sio.event
def mock_event(func):
handlers[func.__name__] = func

View File

@@ -1,4 +1,5 @@
"""Tests for sound normalizer service."""
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, E501, PLC0415
import tempfile
from pathlib import Path
@@ -28,7 +29,7 @@ class TestSoundNormalizerService:
mock_settings.NORMALIZED_AUDIO_PASSES = 2
return SoundNormalizerService(mock_session)
def test_init(self, normalizer_service):
def test_init(self, normalizer_service) -> None:
"""Test normalizer service initialization."""
assert normalizer_service.session is not None
assert normalizer_service.sound_repo is not None
@@ -40,7 +41,7 @@ class TestSoundNormalizerService:
assert "TTS" in normalizer_service.type_directories
assert "EXT" in normalizer_service.type_directories
def test_get_normalized_path(self, normalizer_service):
def test_get_normalized_path(self, normalizer_service) -> None:
"""Test normalized path generation."""
sound = Sound(
id=1,
@@ -57,7 +58,7 @@ class TestSoundNormalizerService:
assert "sounds/normalized/soundboard" in str(normalized_path)
assert normalized_path.name == "test_audio.mp3"
def test_get_original_path(self, normalizer_service):
def test_get_original_path(self, normalizer_service) -> None:
"""Test original path generation."""
sound = Sound(
id=1,
@@ -74,7 +75,7 @@ class TestSoundNormalizerService:
assert "sounds/originals/soundboard" in str(original_path)
assert original_path.name == "test_audio.wav"
def test_get_file_hash(self, normalizer_service):
def test_get_file_hash(self, normalizer_service) -> None:
"""Test file hash calculation."""
# Create a temporary file
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
@@ -90,7 +91,7 @@ class TestSoundNormalizerService:
finally:
temp_path.unlink()
def test_get_file_size(self, normalizer_service):
def test_get_file_size(self, normalizer_service) -> None:
"""Test file size calculation."""
# Create a temporary file
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
@@ -107,7 +108,7 @@ class TestSoundNormalizerService:
temp_path.unlink()
@patch("app.utils.audio.ffmpeg.probe")
def test_get_audio_duration_success(self, mock_probe, normalizer_service):
def test_get_audio_duration_success(self, mock_probe, normalizer_service) -> None:
"""Test successful audio duration extraction."""
mock_probe.return_value = {"format": {"duration": "123.456"}}
@@ -120,7 +121,7 @@ class TestSoundNormalizerService:
mock_probe.assert_called_once_with(str(temp_path))
@patch("app.utils.audio.ffmpeg.probe")
def test_get_audio_duration_failure(self, mock_probe, normalizer_service):
def test_get_audio_duration_failure(self, mock_probe, normalizer_service) -> None:
"""Test audio duration extraction failure."""
mock_probe.side_effect = Exception("FFmpeg error")
@@ -133,7 +134,7 @@ class TestSoundNormalizerService:
mock_probe.assert_called_once_with(str(temp_path))
@pytest.mark.asyncio
async def test_normalize_sound_already_normalized(self, normalizer_service):
async def test_normalize_sound_already_normalized(self, normalizer_service) -> None:
"""Test normalizing a sound that's already normalized."""
sound = Sound(
id=1,
@@ -154,7 +155,7 @@ class TestSoundNormalizerService:
assert result["id"] == 1
@pytest.mark.asyncio
async def test_normalize_sound_force_already_normalized(self, normalizer_service):
async def test_normalize_sound_force_already_normalized(self, normalizer_service) -> None:
"""Test force normalizing a sound that's already normalized."""
sound = Sound(
id=1,
@@ -173,7 +174,7 @@ class TestSoundNormalizerService:
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
patch.object(
normalizer_service, "_normalize_audio_two_pass",
) as mock_normalize,
),
patch(
"app.services.sound_normalizer.get_audio_duration", return_value=6000,
),
@@ -203,7 +204,7 @@ class TestSoundNormalizerService:
normalizer_service.sound_repo.update.assert_called_once()
@pytest.mark.asyncio
async def test_normalize_sound_file_not_found(self, normalizer_service):
async def test_normalize_sound_file_not_found(self, normalizer_service) -> None:
"""Test normalizing a sound where original file doesn't exist."""
sound = Sound(
id=1,
@@ -228,7 +229,7 @@ class TestSoundNormalizerService:
assert result["filename"] == "missing.mp3"
@pytest.mark.asyncio
async def test_normalize_sound_one_pass(self, normalizer_service):
async def test_normalize_sound_one_pass(self, normalizer_service) -> None:
"""Test normalizing a sound using one-pass method."""
sound = Sound(
id=1,
@@ -275,7 +276,7 @@ class TestSoundNormalizerService:
mock_normalize.assert_called_once()
@pytest.mark.asyncio
async def test_normalize_sound_normalization_error(self, normalizer_service):
async def test_normalize_sound_normalization_error(self, normalizer_service) -> None:
"""Test handling normalization errors."""
sound = Sound(
id=1,
@@ -312,7 +313,7 @@ class TestSoundNormalizerService:
assert result["filename"] == "test.mp3"
@pytest.mark.asyncio
async def test_normalize_all_sounds(self, normalizer_service):
async def test_normalize_all_sounds(self, normalizer_service) -> None:
"""Test normalizing all unnormalized sounds."""
sounds = [
Sound(
@@ -382,7 +383,7 @@ class TestSoundNormalizerService:
assert len(results["files"]) == 2
@pytest.mark.asyncio
async def test_normalize_sounds_by_type(self, normalizer_service):
async def test_normalize_sounds_by_type(self, normalizer_service) -> None:
"""Test normalizing sounds by type."""
sdb_sounds = [
Sound(
@@ -432,7 +433,7 @@ class TestSoundNormalizerService:
)
@pytest.mark.asyncio
async def test_normalize_sounds_with_errors(self, normalizer_service):
async def test_normalize_sounds_with_errors(self, normalizer_service) -> None:
"""Test normalizing sounds with some errors."""
sounds = [
Sound(
@@ -512,7 +513,7 @@ class TestSoundNormalizerService:
self,
mock_ffmpeg,
normalizer_service,
):
) -> None:
"""Test one-pass audio normalization for MP3."""
input_path = Path("/fake/input.wav")
output_path = Path("/fake/output.mp3")
@@ -547,7 +548,7 @@ class TestSoundNormalizerService:
self,
mock_ffmpeg,
normalizer_service,
):
) -> None:
"""Test two-pass audio normalization analysis phase."""
input_path = Path("/fake/input.wav")
output_path = Path("/fake/output.mp3")
@@ -562,7 +563,7 @@ class TestSoundNormalizerService:
# Mock analysis output with valid JSON
analysis_json = """{
"input_i": "-23.0",
"input_lra": "11.0",
"input_lra": "11.0",
"input_tp": "-2.0",
"input_thresh": "-33.0",
"target_offset": "0.0"

View File

@@ -1,4 +1,5 @@
"""Tests for sound scanner service."""
# ruff: noqa: ANN001, ANN201, ARG002, PLR2004, SLF001, PLC0415, SIM117
import tempfile
from pathlib import Path
@@ -24,7 +25,7 @@ class TestSoundScannerService:
"""Create a scanner service with mock session."""
return SoundScannerService(mock_session)
def test_init(self, scanner_service):
def test_init(self, scanner_service) -> None:
"""Test scanner service initialization."""
assert scanner_service.session is not None
assert scanner_service.sound_repo is not None
@@ -32,7 +33,7 @@ class TestSoundScannerService:
assert ".mp3" in scanner_service.supported_extensions
assert ".wav" in scanner_service.supported_extensions
def test_get_file_hash(self, scanner_service):
def test_get_file_hash(self, scanner_service) -> None:
"""Test file hash calculation."""
# Create a temporary file
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
@@ -48,7 +49,7 @@ class TestSoundScannerService:
finally:
temp_path.unlink()
def test_get_file_size(self, scanner_service):
def test_get_file_size(self, scanner_service) -> None:
"""Test file size calculation."""
# Create a temporary file
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
@@ -64,7 +65,7 @@ class TestSoundScannerService:
finally:
temp_path.unlink()
def test_extract_name_from_filename(self, scanner_service):
def test_extract_name_from_filename(self, scanner_service) -> None:
"""Test name extraction from filename."""
test_cases = [
("hello_world.mp3", "Hello World"),
@@ -79,7 +80,7 @@ class TestSoundScannerService:
assert result == expected_name
@patch("app.utils.audio.ffmpeg.probe")
def test_get_audio_duration_success(self, mock_probe, scanner_service):
def test_get_audio_duration_success(self, mock_probe, scanner_service) -> None:
"""Test successful audio duration extraction."""
mock_probe.return_value = {"format": {"duration": "123.456"}}
@@ -92,7 +93,7 @@ class TestSoundScannerService:
mock_probe.assert_called_once_with(str(temp_path))
@patch("app.utils.audio.ffmpeg.probe")
def test_get_audio_duration_failure(self, mock_probe, scanner_service):
def test_get_audio_duration_failure(self, mock_probe, scanner_service) -> None:
"""Test audio duration extraction failure."""
mock_probe.side_effect = Exception("FFmpeg error")
@@ -105,13 +106,13 @@ class TestSoundScannerService:
mock_probe.assert_called_once_with(str(temp_path))
@pytest.mark.asyncio
async def test_scan_directory_nonexistent(self, scanner_service):
async def test_scan_directory_nonexistent(self, scanner_service) -> None:
"""Test scanning a non-existent directory."""
with pytest.raises(ValueError, match="Directory does not exist"):
await scanner_service.scan_directory("/non/existent/path")
@pytest.mark.asyncio
async def test_scan_directory_not_directory(self, scanner_service):
async def test_scan_directory_not_directory(self, scanner_service) -> None:
"""Test scanning a path that is not a directory."""
# Create a temporary file
with tempfile.NamedTemporaryFile() as f:
@@ -119,7 +120,7 @@ class TestSoundScannerService:
await scanner_service.scan_directory(f.name)
@pytest.mark.asyncio
async def test_sync_audio_file_unchanged(self, scanner_service):
async def test_sync_audio_file_unchanged(self, scanner_service) -> None:
"""Test syncing file that is unchanged."""
# Existing sound with same hash as file
existing_sound = Sound(
@@ -166,7 +167,7 @@ class TestSoundScannerService:
temp_path.unlink()
@pytest.mark.asyncio
async def test_sync_audio_file_new(self, scanner_service):
async def test_sync_audio_file_new(self, scanner_service) -> None:
"""Test syncing a new audio file."""
created_sound = Sound(
id=1,
@@ -221,7 +222,7 @@ class TestSoundScannerService:
temp_path.unlink()
@pytest.mark.asyncio
async def test_sync_audio_file_updated(self, scanner_service):
async def test_sync_audio_file_updated(self, scanner_service) -> None:
"""Test syncing a file that was modified (different hash)."""
# Existing sound with different hash than file
existing_sound = Sound(
@@ -280,7 +281,7 @@ class TestSoundScannerService:
temp_path.unlink()
@pytest.mark.asyncio
async def test_sync_audio_file_custom_type(self, scanner_service):
async def test_sync_audio_file_custom_type(self, scanner_service) -> None:
"""Test syncing file with custom type."""
created_sound = Sound(
id=1,

View File

@@ -1,4 +1,5 @@
"""Tests for VLC player service."""
# ruff: noqa: ANN001, ANN201, PLR2004, SLF001, SIM117, E501, ANN202, PLC0415
import asyncio
from pathlib import Path
@@ -6,7 +7,6 @@ from unittest.mock import AsyncMock, Mock, patch
import pytest
from app.models.credit_transaction import CreditTransaction
from app.models.sound import Sound
from app.models.user import User
from app.services.vlc_player import VLCPlayerService, get_vlc_player_service
@@ -62,20 +62,20 @@ class TestVLCPlayerService:
normalized_filename="normalized.mp3",
)
def test_init(self, vlc_service):
def test_init(self, vlc_service) -> None:
"""Test VLC service initialization."""
assert vlc_service.vlc_executable is not None
assert isinstance(vlc_service.vlc_executable, str)
@patch("app.services.vlc_player.subprocess.run")
def test_find_vlc_executable_found_in_path(self, mock_run):
def test_find_vlc_executable_found_in_path(self, mock_run) -> None:
"""Test VLC executable detection when found in PATH."""
mock_run.return_value.returncode = 0
service = VLCPlayerService()
assert service.vlc_executable == "vlc"
@patch("app.services.vlc_player.subprocess.run")
def test_find_vlc_executable_found_by_path(self, mock_run):
def test_find_vlc_executable_found_by_path(self, mock_run) -> None:
"""Test VLC executable detection when found by absolute path."""
mock_run.return_value.returncode = 1 # which command fails
@@ -93,7 +93,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.subprocess.run")
@patch("app.services.vlc_player.Path")
def test_find_vlc_executable_fallback(self, mock_path, mock_run):
def test_find_vlc_executable_fallback(self, mock_path, mock_run) -> None:
"""Test VLC executable detection fallback to default."""
# Mock all paths as non-existent
mock_path_instance = Mock()
@@ -111,7 +111,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_play_sound_success(
self, mock_subprocess, vlc_service, sample_sound,
):
) -> None:
"""Test successful sound playback."""
# Mock subprocess
mock_process = Mock()
@@ -144,7 +144,7 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
async def test_play_sound_file_not_found(
self, vlc_service, sample_sound,
):
) -> None:
"""Test sound playback when file doesn't exist."""
# Mock the file path utility to return a non-existent path
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
@@ -160,7 +160,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_play_sound_subprocess_error(
self, mock_subprocess, vlc_service, sample_sound,
):
) -> None:
"""Test sound playback when subprocess fails."""
# Mock the file path utility to return an existing path
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
@@ -177,7 +177,7 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_success(self, mock_subprocess, vlc_service):
async def test_stop_all_vlc_instances_success(self, mock_subprocess, vlc_service) -> None:
"""Test successful stopping of all VLC instances."""
# Mock pgrep process (find VLC processes)
mock_find_process = Mock()
@@ -214,7 +214,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_no_processes(
self, mock_subprocess, vlc_service,
):
) -> None:
"""Test stopping VLC instances when none are running."""
# Mock pgrep process (no VLC processes found)
mock_find_process = Mock()
@@ -234,7 +234,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_partial_kill(
self, mock_subprocess, vlc_service,
):
) -> None:
"""Test stopping VLC instances when some processes remain."""
# Mock pgrep process (find VLC processes)
mock_find_process = Mock()
@@ -267,7 +267,7 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_error(self, mock_subprocess, vlc_service):
async def test_stop_all_vlc_instances_error(self, mock_subprocess, vlc_service) -> None:
"""Test stopping VLC instances when an error occurs."""
# Mock subprocess exception
mock_subprocess.side_effect = Exception("Command failed")
@@ -280,7 +280,7 @@ class TestVLCPlayerService:
assert "error" in result
assert result["message"] == "Failed to stop VLC processes"
def test_get_vlc_player_service_singleton(self):
def test_get_vlc_player_service_singleton(self) -> None:
"""Test that get_vlc_player_service returns the same instance."""
with patch("app.services.vlc_player.VLCPlayerService") as mock_service_class:
mock_instance = Mock()
@@ -306,7 +306,7 @@ class TestVLCPlayerService:
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_play_sound_with_play_count_tracking(
self, mock_subprocess, vlc_service_with_db, sample_sound,
):
) -> None:
"""Test sound playback with play count tracking."""
# Mock subprocess
mock_process = Mock()
@@ -371,7 +371,7 @@ class TestVLCPlayerService:
# mocking or using a real async test framework setup
@pytest.mark.asyncio
async def test_record_play_count_success(self, vlc_service_with_db):
async def test_record_play_count_success(self, vlc_service_with_db) -> None:
"""Test successful play count recording."""
# Mock session and repositories
mock_session = AsyncMock()
@@ -436,14 +436,14 @@ class TestVLCPlayerService:
)
@pytest.mark.asyncio
async def test_record_play_count_no_session_factory(self, vlc_service):
async def test_record_play_count_no_session_factory(self, vlc_service) -> None:
"""Test play count recording when no session factory is available."""
# This should not raise an error and should log a warning
await vlc_service._record_play_count(1, "Test Sound")
# The method should return early without doing anything
@pytest.mark.asyncio
async def test_record_play_count_always_creates_record(self, vlc_service_with_db):
async def test_record_play_count_always_creates_record(self, vlc_service_with_db) -> None:
"""Test play count recording always creates a new SoundPlayed record."""
# Mock session and repositories
mock_session = AsyncMock()
@@ -493,7 +493,7 @@ class TestVLCPlayerService:
# Verify commit happened
mock_session.commit.assert_called_once()
def test_uses_shared_sound_path_utility(self, vlc_service, sample_sound):
def test_uses_shared_sound_path_utility(self, vlc_service, sample_sound) -> None:
"""Test that VLC service uses the shared sound path utility."""
with patch("app.services.vlc_player.get_sound_file_path") as mock_path:
mock_file_path = Mock(spec=Path)