Refactor test files for improved readability and consistency

- Removed unnecessary blank lines and adjusted formatting in test files.
- Ensured consistent use of commas in function calls and assertions across various test cases.
- Updated import statements for better organization and clarity.
- Enhanced mock setups in tests for better isolation and reliability.
- Improved assertions to follow a consistent style for better readability.
This commit is contained in:
JSC
2025-07-31 21:37:04 +02:00
parent e69098d633
commit 8847131f24
42 changed files with 602 additions and 616 deletions

View File

@@ -1,7 +1,7 @@
"""Tests for credit service."""
import json
from unittest.mock import AsyncMock, Mock, patch
from unittest.mock import AsyncMock, patch
import pytest
from sqlmodel.ext.asyncio.session import AsyncSession
@@ -42,14 +42,14 @@ class TestCreditService:
async def test_check_credits_sufficient(self, credit_service, sample_user):
"""Test checking credits when user has sufficient credits."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
result = await credit_service.check_credits(1, CreditActionType.VLC_PLAY_SOUND)
assert result is True
mock_repo.get_by_id.assert_called_once_with(1)
mock_session.close.assert_called_once()
@@ -66,14 +66,14 @@ class TestCreditService:
credits=0, # No credits
plan_id=1,
)
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = poor_user
result = await credit_service.check_credits(1, CreditActionType.VLC_PLAY_SOUND)
assert result is False
mock_session.close.assert_called_once()
@@ -81,14 +81,14 @@ class TestCreditService:
async def test_check_credits_user_not_found(self, credit_service):
"""Test checking credits when user is not found."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = None
result = await credit_service.check_credits(999, CreditActionType.VLC_PLAY_SOUND)
assert result is False
mock_session.close.assert_called_once()
@@ -96,16 +96,16 @@ class TestCreditService:
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user):
"""Test successful credit validation and reservation."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
user, action = await credit_service.validate_and_reserve_credits(
1, CreditActionType.VLC_PLAY_SOUND
1, CreditActionType.VLC_PLAY_SOUND,
)
assert user == sample_user
assert action.action_type == CreditActionType.VLC_PLAY_SOUND
assert action.cost == 1
@@ -123,17 +123,17 @@ class TestCreditService:
credits=0,
plan_id=1,
)
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = poor_user
with pytest.raises(InsufficientCreditsError) as exc_info:
await credit_service.validate_and_reserve_credits(
1, CreditActionType.VLC_PLAY_SOUND
1, CreditActionType.VLC_PLAY_SOUND,
)
assert exc_info.value.required == 1
assert exc_info.value.available == 0
mock_session.close.assert_called_once()
@@ -142,42 +142,42 @@ class TestCreditService:
async def test_validate_and_reserve_credits_user_not_found(self, credit_service):
"""Test credit validation when user is not found."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = None
with pytest.raises(ValueError, match="User 999 not found"):
await credit_service.validate_and_reserve_credits(
999, CreditActionType.VLC_PLAY_SOUND
999, CreditActionType.VLC_PLAY_SOUND,
)
mock_session.close.assert_called_once()
@pytest.mark.asyncio
async def test_deduct_credits_success(self, credit_service, sample_user):
"""Test successful credit deduction."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class, \
patch("app.services.credit.socket_manager") as mock_socket_manager:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.deduct_credits(
1, CreditActionType.VLC_PLAY_SOUND, True, {"test": "data"}
1, CreditActionType.VLC_PLAY_SOUND, True, {"test": "data"},
)
# Verify user credits were updated
mock_repo.update.assert_called_once_with(sample_user, {"credits": 9})
# Verify transaction was created
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()
# Verify socket event was emitted
mock_socket_manager.send_to_user.assert_called_once_with(
"1", "user_credits_changed", {
@@ -187,9 +187,9 @@ class TestCreditService:
"credits_deducted": 1,
"action_type": "vlc_play_sound",
"success": True,
}
},
)
# Check transaction details
added_transaction = mock_session.add.call_args[0][0]
assert isinstance(added_transaction, CreditTransaction)
@@ -205,28 +205,28 @@ class TestCreditService:
async def test_deduct_credits_failed_action_requires_success(self, credit_service, sample_user):
"""Test credit deduction when action failed but requires success."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class, \
patch("app.services.credit.socket_manager") as mock_socket_manager:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.deduct_credits(
1, CreditActionType.VLC_PLAY_SOUND, False # Action failed
1, CreditActionType.VLC_PLAY_SOUND, False, # Action failed
)
# Verify user credits were NOT updated (action requires success)
mock_repo.update.assert_not_called()
# Verify transaction was still created for auditing
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()
# Verify no socket event was emitted since no credits were actually deducted
mock_socket_manager.send_to_user.assert_not_called()
# Check transaction details
added_transaction = mock_session.add.call_args[0][0]
assert added_transaction.amount == 0 # No deduction for failed action
@@ -246,22 +246,22 @@ class TestCreditService:
credits=0,
plan_id=1,
)
with patch("app.services.credit.UserRepository") as mock_repo_class, \
patch("app.services.credit.socket_manager") as mock_socket_manager:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = poor_user
mock_socket_manager.send_to_user = AsyncMock()
with pytest.raises(InsufficientCreditsError):
await credit_service.deduct_credits(
1, CreditActionType.VLC_PLAY_SOUND, True
1, CreditActionType.VLC_PLAY_SOUND, True,
)
# Verify no socket event was emitted since credits could not be deducted
mock_socket_manager.send_to_user.assert_not_called()
mock_session.rollback.assert_called_once()
mock_session.close.assert_called_once()
@@ -269,25 +269,25 @@ class TestCreditService:
async def test_add_credits(self, credit_service, sample_user):
"""Test adding credits to user account."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class, \
patch("app.services.credit.socket_manager") as mock_socket_manager:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
mock_socket_manager.send_to_user = AsyncMock()
transaction = await credit_service.add_credits(
1, 5, "Bonus credits", {"reason": "signup"}
1, 5, "Bonus credits", {"reason": "signup"},
)
# Verify user credits were updated
mock_repo.update.assert_called_once_with(sample_user, {"credits": 15})
# Verify transaction was created
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()
# Verify socket event was emitted
mock_socket_manager.send_to_user.assert_called_once_with(
"1", "user_credits_changed", {
@@ -297,9 +297,9 @@ class TestCreditService:
"credits_added": 5,
"description": "Bonus credits",
"success": True,
}
},
)
# Check transaction details
added_transaction = mock_session.add.call_args[0][0]
assert added_transaction.amount == 5
@@ -312,7 +312,7 @@ class TestCreditService:
"""Test adding invalid amount of credits."""
with pytest.raises(ValueError, match="Amount must be positive"):
await credit_service.add_credits(1, 0, "Invalid")
with pytest.raises(ValueError, match="Amount must be positive"):
await credit_service.add_credits(1, -5, "Invalid")
@@ -320,14 +320,14 @@ class TestCreditService:
async def test_get_user_balance(self, credit_service, sample_user):
"""Test getting user credit balance."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = sample_user
balance = await credit_service.get_user_balance(1)
assert balance == 10
mock_session.close.assert_called_once()
@@ -335,15 +335,15 @@ class TestCreditService:
async def test_get_user_balance_user_not_found(self, credit_service):
"""Test getting balance for non-existent user."""
mock_session = credit_service.db_session_factory()
with patch("app.services.credit.UserRepository") as mock_repo_class:
mock_repo = AsyncMock()
mock_repo_class.return_value = mock_repo
mock_repo.get_by_id.return_value = None
with pytest.raises(ValueError, match="User 999 not found"):
await credit_service.get_user_balance(999)
mock_session.close.assert_called_once()
@@ -355,4 +355,4 @@ class TestInsufficientCreditsError:
error = InsufficientCreditsError(5, 2)
assert error.required == 5
assert error.available == 2
assert str(error) == "Insufficient credits: 5 required, 2 available"
assert str(error) == "Insufficient credits: 5 required, 2 available"

View File

@@ -53,7 +53,7 @@ class TestExtractionService:
@patch("app.services.extraction.yt_dlp.YoutubeDL")
@pytest.mark.asyncio
async def test_detect_service_info_youtube(
self, mock_ydl_class, extraction_service
self, mock_ydl_class, extraction_service,
):
"""Test service detection for YouTube."""
mock_ydl = Mock()
@@ -67,7 +67,7 @@ class TestExtractionService:
}
result = await extraction_service._detect_service_info(
"https://www.youtube.com/watch?v=test123"
"https://www.youtube.com/watch?v=test123",
)
assert result is not None
@@ -78,7 +78,7 @@ class TestExtractionService:
@patch("app.services.extraction.yt_dlp.YoutubeDL")
@pytest.mark.asyncio
async def test_detect_service_info_failure(
self, mock_ydl_class, extraction_service
self, mock_ydl_class, extraction_service,
):
"""Test service detection failure."""
mock_ydl = Mock()
@@ -106,7 +106,7 @@ class TestExtractionService:
status="pending",
)
extraction_service.extraction_repo.create = AsyncMock(
return_value=mock_extraction
return_value=mock_extraction,
)
result = await extraction_service.create_extraction(url, user_id)
@@ -134,7 +134,7 @@ class TestExtractionService:
status="pending",
)
extraction_service.extraction_repo.create = AsyncMock(
return_value=mock_extraction
return_value=mock_extraction,
)
result = await extraction_service.create_extraction(url, user_id)
@@ -160,7 +160,7 @@ class TestExtractionService:
status="pending",
)
extraction_service.extraction_repo.create = AsyncMock(
return_value=mock_extraction
return_value=mock_extraction,
)
result = await extraction_service.create_extraction(url, user_id)
@@ -186,11 +186,11 @@ class TestExtractionService:
)
extraction_service.extraction_repo.get_by_id = AsyncMock(
return_value=mock_extraction
return_value=mock_extraction,
)
extraction_service.extraction_repo.update = AsyncMock()
extraction_service.extraction_repo.get_by_service_and_id = AsyncMock(
return_value=None
return_value=None,
)
# Mock service detection
@@ -202,14 +202,14 @@ class TestExtractionService:
with (
patch.object(
extraction_service, "_detect_service_info", return_value=service_info
extraction_service, "_detect_service_info", return_value=service_info,
),
patch.object(extraction_service, "_extract_media") as mock_extract,
patch.object(
extraction_service, "_move_files_to_final_location"
extraction_service, "_move_files_to_final_location",
) as mock_move,
patch.object(
extraction_service, "_create_sound_record"
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,
@@ -223,7 +223,7 @@ class TestExtractionService:
# Verify service detection was called
extraction_service._detect_service_info.assert_called_once_with(
"https://www.youtube.com/watch?v=test123"
"https://www.youtube.com/watch?v=test123",
)
# Verify extraction was updated with service info
@@ -289,15 +289,15 @@ class TestExtractionService:
with (
patch(
"app.services.extraction.get_audio_duration", return_value=240000
"app.services.extraction.get_audio_duration", return_value=240000,
),
patch("app.services.extraction.get_file_size", return_value=1024),
patch(
"app.services.extraction.get_file_hash", return_value="test_hash"
"app.services.extraction.get_file_hash", return_value="test_hash",
),
):
extraction_service.sound_repo.create = AsyncMock(
return_value=mock_sound
return_value=mock_sound,
)
result = await extraction_service._create_sound_record(
@@ -336,7 +336,7 @@ class TestExtractionService:
mock_normalizer = Mock()
mock_normalizer.normalize_sound = AsyncMock(
return_value={"status": "normalized"}
return_value={"status": "normalized"},
)
with patch(
@@ -368,7 +368,7 @@ class TestExtractionService:
mock_normalizer = Mock()
mock_normalizer.normalize_sound = AsyncMock(
return_value={"status": "error", "error": "Test error"}
return_value={"status": "error", "error": "Test error"},
)
with patch(
@@ -395,7 +395,7 @@ class TestExtractionService:
)
extraction_service.extraction_repo.get_by_id = AsyncMock(
return_value=extraction
return_value=extraction,
)
result = await extraction_service.get_extraction_by_id(1)
@@ -443,7 +443,7 @@ class TestExtractionService:
]
extraction_service.extraction_repo.get_by_user = AsyncMock(
return_value=extractions
return_value=extractions,
)
result = await extraction_service.get_user_extractions(1)
@@ -470,7 +470,7 @@ class TestExtractionService:
]
extraction_service.extraction_repo.get_pending_extractions = AsyncMock(
return_value=pending_extractions
return_value=pending_extractions,
)
result = await extraction_service.get_pending_extractions()

View File

@@ -1,6 +1,5 @@
"""Tests for extraction background processor."""
import asyncio
from unittest.mock import AsyncMock, Mock, patch
import pytest
@@ -30,7 +29,7 @@ class TestExtractionProcessor:
"""Test starting and stopping the processor."""
# Mock the _process_queue method to avoid actual processing
with patch.object(
processor, "_process_queue", new_callable=AsyncMock
processor, "_process_queue", new_callable=AsyncMock,
) as mock_process:
# Start the processor
await processor.start()
@@ -138,12 +137,12 @@ class TestExtractionProcessor:
# Mock the extraction service
mock_service = Mock()
mock_service.process_extraction = AsyncMock(
return_value={"status": "completed", "id": extraction_id}
return_value={"status": "completed", "id": extraction_id},
)
with (
patch(
"app.services.extraction_processor.AsyncSession"
"app.services.extraction_processor.AsyncSession",
) as mock_session_class,
patch(
"app.services.extraction_processor.ExtractionService",
@@ -168,7 +167,7 @@ class TestExtractionProcessor:
with (
patch(
"app.services.extraction_processor.AsyncSession"
"app.services.extraction_processor.AsyncSession",
) as mock_session_class,
patch(
"app.services.extraction_processor.ExtractionService",
@@ -193,12 +192,12 @@ class TestExtractionProcessor:
# Mock extraction service
mock_service = Mock()
mock_service.get_pending_extractions = AsyncMock(
return_value=[{"id": 100, "status": "pending"}]
return_value=[{"id": 100, "status": "pending"}],
)
with (
patch(
"app.services.extraction_processor.AsyncSession"
"app.services.extraction_processor.AsyncSession",
) as mock_session_class,
patch(
"app.services.extraction_processor.ExtractionService",
@@ -222,15 +221,15 @@ class TestExtractionProcessor:
return_value=[
{"id": 100, "status": "pending"},
{"id": 101, "status": "pending"},
]
],
)
with (
patch(
"app.services.extraction_processor.AsyncSession"
"app.services.extraction_processor.AsyncSession",
) as mock_session_class,
patch.object(
processor, "_process_single_extraction", new_callable=AsyncMock
processor, "_process_single_extraction", new_callable=AsyncMock,
) as mock_process,
patch(
"app.services.extraction_processor.ExtractionService",
@@ -267,15 +266,15 @@ class TestExtractionProcessor:
{"id": 100, "status": "pending"},
{"id": 101, "status": "pending"},
{"id": 102, "status": "pending"},
]
],
)
with (
patch(
"app.services.extraction_processor.AsyncSession"
"app.services.extraction_processor.AsyncSession",
) as mock_session_class,
patch.object(
processor, "_process_single_extraction", new_callable=AsyncMock
processor, "_process_single_extraction", new_callable=AsyncMock,
) as mock_process,
patch(
"app.services.extraction_processor.ExtractionService",

View File

@@ -4,14 +4,12 @@ import asyncio
import threading
import time
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, Mock, patch
from unittest.mock import AsyncMock, Mock, patch
import pytest
from sqlmodel.ext.asyncio.session import AsyncSession
from app.models.sound import Sound
from app.models.sound_played import SoundPlayed
from app.models.user import User
from app.services.player import (
PlayerMode,
PlayerService,
@@ -21,7 +19,6 @@ from app.services.player import (
initialize_player_service,
shutdown_player_service,
)
from app.utils.audio import get_sound_file_path
class TestPlayerState:
@@ -200,7 +197,7 @@ class TestPlayerService:
mock_file_path = Mock(spec=Path)
mock_file_path.exists.return_value = True
mock_path.return_value = mock_file_path
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
mock_media = Mock()
player_service._vlc_instance.media_new.return_value = mock_media
@@ -738,7 +735,7 @@ class TestPlayerService:
# Verify sound play count was updated
mock_sound_repo.update.assert_called_once_with(
mock_sound, {"play_count": 6}
mock_sound, {"play_count": 6},
)
# Verify SoundPlayed record was created with None user_id for player
@@ -768,7 +765,7 @@ class TestPlayerService:
mock_file_path = Mock(spec=Path)
mock_file_path.exists.return_value = False # File doesn't exist
mock_path.return_value = mock_file_path
# This should fail because file doesn't exist
result = asyncio.run(player_service.play(0))
# Verify the utility was called
@@ -817,4 +814,4 @@ class TestPlayerServiceGlobalFunctions:
"""Test getting player service when not initialized."""
with patch("app.services.player.player_service", None):
with pytest.raises(RuntimeError, match="Player service not initialized"):
get_player_service()
get_player_service()

View File

@@ -153,7 +153,6 @@ class TestPlaylistService:
test_user: User,
) -> None:
"""Test getting non-existent playlist."""
with pytest.raises(HTTPException) as exc_info:
await playlist_service.get_playlist_by_id(99999)
@@ -168,7 +167,6 @@ class TestPlaylistService:
test_session: AsyncSession,
) -> None:
"""Test getting existing main playlist."""
# Create main playlist manually
main_playlist = Playlist(
user_id=None,
@@ -193,7 +191,6 @@ class TestPlaylistService:
test_user: User,
) -> None:
"""Test that service fails if main playlist doesn't exist."""
# Should raise an HTTPException if no main playlist exists
with pytest.raises(HTTPException) as exc_info:
await playlist_service.get_main_playlist()
@@ -207,7 +204,6 @@ class TestPlaylistService:
test_user: User,
) -> None:
"""Test creating a new playlist successfully."""
user_id = test_user.id # Extract user_id while session is available
playlist = await playlist_service.create_playlist(
user_id=user_id,
@@ -246,7 +242,7 @@ class TestPlaylistService:
test_session.add(playlist)
await test_session.commit()
await test_session.refresh(playlist)
# Extract name before async call
playlist_name = playlist.name
@@ -280,10 +276,10 @@ class TestPlaylistService:
test_session.add(current_playlist)
await test_session.commit()
await test_session.refresh(current_playlist)
# Verify the existing current playlist
assert current_playlist.is_current is True
# Extract ID before async call
current_playlist_id = current_playlist.id
@@ -323,10 +319,10 @@ class TestPlaylistService:
test_session.add(playlist)
await test_session.commit()
await test_session.refresh(playlist)
# Extract IDs before async call
playlist_id = playlist.id
updated_playlist = await playlist_service.update_playlist(
playlist_id=playlist_id,
user_id=user_id,
@@ -359,7 +355,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(test_playlist)
current_playlist = Playlist(
user_id=user_id,
name="Current Playlist",
@@ -372,7 +368,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(test_playlist)
await test_session.refresh(current_playlist)
# Extract IDs before async calls
test_playlist_id = test_playlist.id
current_playlist_id = current_playlist.id
@@ -416,7 +412,7 @@ class TestPlaylistService:
test_session.add(playlist)
await test_session.commit()
await test_session.refresh(playlist)
# Extract ID before async call
playlist_id = playlist.id
@@ -445,7 +441,7 @@ class TestPlaylistService:
is_deletable=False,
)
test_session.add(main_playlist)
# Create current playlist within this test
user_id = test_user.id
current_playlist = Playlist(
@@ -459,7 +455,7 @@ class TestPlaylistService:
test_session.add(current_playlist)
await test_session.commit()
await test_session.refresh(current_playlist)
# Extract ID before async call
current_playlist_id = current_playlist.id
@@ -481,7 +477,7 @@ class TestPlaylistService:
"""Test deleting non-deletable playlist fails."""
# Extract user ID immediately
user_id = test_user.id
# Create non-deletable playlist
non_deletable = Playlist(
user_id=user_id,
@@ -491,7 +487,7 @@ class TestPlaylistService:
test_session.add(non_deletable)
await test_session.commit()
await test_session.refresh(non_deletable)
# Extract ID before async call
non_deletable_id = non_deletable.id
@@ -521,7 +517,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(playlist)
sound = Sound(
name="Test Sound",
filename="test.mp3",
@@ -535,7 +531,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(playlist)
await test_session.refresh(sound)
# Extract IDs before async calls
playlist_id = playlist.id
sound_id = sound.id
@@ -571,7 +567,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(playlist)
sound = Sound(
name="Test Sound",
filename="test.mp3",
@@ -585,7 +581,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(playlist)
await test_session.refresh(sound)
# Extract IDs before async calls
playlist_id = playlist.id
sound_id = sound.id
@@ -628,7 +624,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(playlist)
sound = Sound(
name="Test Sound",
filename="test.mp3",
@@ -642,7 +638,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(playlist)
await test_session.refresh(sound)
# Extract IDs before async calls
playlist_id = playlist.id
sound_id = sound.id
@@ -685,7 +681,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(playlist)
sound = Sound(
name="Test Sound",
filename="test.mp3",
@@ -699,7 +695,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(playlist)
await test_session.refresh(sound)
# Extract IDs before async calls
playlist_id = playlist.id
sound_id = sound.id
@@ -734,7 +730,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(test_playlist)
current_playlist = Playlist(
user_id=user_id,
name="Current Playlist",
@@ -747,7 +743,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(test_playlist)
await test_session.refresh(current_playlist)
# Extract IDs before async calls
test_playlist_id = test_playlist.id
current_playlist_id = current_playlist.id
@@ -758,7 +754,7 @@ class TestPlaylistService:
# Set test_playlist as current
updated_playlist = await playlist_service.set_current_playlist(
test_playlist_id, user_id
test_playlist_id, user_id,
)
assert updated_playlist.is_current is True
@@ -786,7 +782,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(current_playlist)
main_playlist = Playlist(
user_id=None,
name="Main Playlist",
@@ -799,7 +795,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(current_playlist)
await test_session.refresh(main_playlist)
# Extract IDs before async calls
current_playlist_id = current_playlist.id
main_playlist_id = main_playlist.id
@@ -839,7 +835,7 @@ class TestPlaylistService:
is_deletable=True,
)
test_session.add(playlist)
sound = Sound(
name="Test Sound",
filename="test.mp3",
@@ -853,7 +849,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(playlist)
await test_session.refresh(sound)
# Extract IDs before async calls
playlist_id = playlist.id
sound_id = sound.id
@@ -897,7 +893,7 @@ class TestPlaylistService:
play_count=10,
)
test_session.add(sound)
main_playlist = Playlist(
user_id=None,
name="Main Playlist",
@@ -910,7 +906,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(sound)
await test_session.refresh(main_playlist)
# Extract IDs before async calls
sound_id = sound.id
main_playlist_id = main_playlist.id
@@ -943,7 +939,7 @@ class TestPlaylistService:
play_count=10,
)
test_session.add(sound)
main_playlist = Playlist(
user_id=None,
name="Main Playlist",
@@ -956,7 +952,7 @@ class TestPlaylistService:
await test_session.commit()
await test_session.refresh(sound)
await test_session.refresh(main_playlist)
# Extract IDs before async calls
sound_id = sound.id
main_playlist_id = main_playlist.id

View File

@@ -98,7 +98,7 @@ class TestSocketManager:
@patch("app.services.socket.extract_access_token_from_cookies")
@patch("app.services.socket.JWTUtils.decode_access_token")
async def test_connect_handler_success(
self, mock_decode, mock_extract_token, socket_manager, mock_sio
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
"""Test successful connection with valid token."""
# Setup mocks
@@ -133,7 +133,7 @@ class TestSocketManager:
@pytest.mark.asyncio
@patch("app.services.socket.extract_access_token_from_cookies")
async def test_connect_handler_no_token(
self, mock_extract_token, socket_manager, mock_sio
self, mock_extract_token, socket_manager, mock_sio,
):
"""Test connection with no access token."""
# Setup mocks
@@ -167,7 +167,7 @@ class TestSocketManager:
@patch("app.services.socket.extract_access_token_from_cookies")
@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
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
"""Test connection with invalid token."""
# Setup mocks
@@ -202,7 +202,7 @@ class TestSocketManager:
@patch("app.services.socket.extract_access_token_from_cookies")
@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
self, mock_decode, mock_extract_token, socket_manager, mock_sio,
):
"""Test connection with token missing user ID."""
# Setup mocks

View File

@@ -55,7 +55,7 @@ class TestSoundNormalizerService:
normalized_path = normalizer_service._get_normalized_path(sound)
assert "sounds/normalized/soundboard" in str(normalized_path)
assert "test_audio.mp3" == normalized_path.name
assert normalized_path.name == "test_audio.mp3"
def test_get_original_path(self, normalizer_service):
"""Test original path generation."""
@@ -72,7 +72,7 @@ class TestSoundNormalizerService:
original_path = normalizer_service._get_original_path(sound)
assert "sounds/originals/soundboard" in str(original_path)
assert "test_audio.wav" == original_path.name
assert original_path.name == "test_audio.wav"
def test_get_file_hash(self, normalizer_service):
"""Test file hash calculation."""
@@ -172,14 +172,14 @@ class TestSoundNormalizerService:
patch.object(normalizer_service, "_get_original_path") as mock_orig_path,
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
patch.object(
normalizer_service, "_normalize_audio_two_pass"
normalizer_service, "_normalize_audio_two_pass",
) as mock_normalize,
patch(
"app.services.sound_normalizer.get_audio_duration", return_value=6000
"app.services.sound_normalizer.get_audio_duration", return_value=6000,
),
patch("app.services.sound_normalizer.get_file_size", return_value=2048),
patch(
"app.services.sound_normalizer.get_file_hash", return_value="new_hash"
"app.services.sound_normalizer.get_file_hash", return_value="new_hash",
),
):
# Setup path mocks
@@ -245,14 +245,14 @@ class TestSoundNormalizerService:
patch.object(normalizer_service, "_get_original_path") as mock_orig_path,
patch.object(normalizer_service, "_get_normalized_path") as mock_norm_path,
patch.object(
normalizer_service, "_normalize_audio_one_pass"
normalizer_service, "_normalize_audio_one_pass",
) as mock_normalize,
patch(
"app.services.sound_normalizer.get_audio_duration", return_value=5500
"app.services.sound_normalizer.get_audio_duration", return_value=5500,
),
patch("app.services.sound_normalizer.get_file_size", return_value=1500),
patch(
"app.services.sound_normalizer.get_file_hash", return_value="norm_hash"
"app.services.sound_normalizer.get_file_hash", return_value="norm_hash",
),
):
# Setup path mocks
@@ -300,7 +300,7 @@ class TestSoundNormalizerService:
with (
patch("pathlib.Path.exists", return_value=True),
patch.object(
normalizer_service, "_normalize_audio_two_pass"
normalizer_service, "_normalize_audio_two_pass",
) as mock_normalize,
):
mock_normalize.side_effect = Exception("Normalization failed")
@@ -339,7 +339,7 @@ class TestSoundNormalizerService:
# Mock repository calls
normalizer_service.sound_repo.get_unnormalized_sounds = AsyncMock(
return_value=sounds
return_value=sounds,
)
# Mock individual normalization
@@ -399,7 +399,7 @@ class TestSoundNormalizerService:
# Mock repository calls
normalizer_service.sound_repo.get_unnormalized_sounds_by_type = AsyncMock(
return_value=sdb_sounds
return_value=sdb_sounds,
)
# Mock individual normalization
@@ -428,7 +428,7 @@ class TestSoundNormalizerService:
# Verify correct repository method was called
normalizer_service.sound_repo.get_unnormalized_sounds_by_type.assert_called_once_with(
"SDB"
"SDB",
)
@pytest.mark.asyncio
@@ -459,7 +459,7 @@ class TestSoundNormalizerService:
# Mock repository calls
normalizer_service.sound_repo.get_unnormalized_sounds = AsyncMock(
return_value=sounds
return_value=sounds,
)
# Mock individual normalization with one success and one error
@@ -529,7 +529,7 @@ class TestSoundNormalizerService:
# Verify ffmpeg chain was called correctly
mock_ffmpeg.input.assert_called_once_with(str(input_path))
mock_ffmpeg.filter.assert_called_once_with(
mock_stream, "loudnorm", I=-23, TP=-2, LRA=7
mock_stream, "loudnorm", I=-23, TP=-2, LRA=7,
)
mock_ffmpeg.output.assert_called_once()
mock_ffmpeg.run.assert_called_once()

View File

@@ -153,7 +153,7 @@ class TestSoundScannerService:
"files": [],
}
await scanner_service._sync_audio_file(
temp_path, "SDB", existing_sound, results
temp_path, "SDB", existing_sound, results,
)
assert results["skipped"] == 1
@@ -257,7 +257,7 @@ class TestSoundScannerService:
"files": [],
}
await scanner_service._sync_audio_file(
temp_path, "SDB", existing_sound, results
temp_path, "SDB", existing_sound, results,
)
assert results["updated"] == 1
@@ -296,7 +296,7 @@ class TestSoundScannerService:
# Mock file operations
with (
patch(
"app.services.sound_scanner.get_file_hash", return_value="custom_hash"
"app.services.sound_scanner.get_file_hash", return_value="custom_hash",
),
patch("app.services.sound_scanner.get_audio_duration", return_value=60000),
patch("app.services.sound_scanner.get_file_size", return_value=2048),
@@ -316,7 +316,7 @@ class TestSoundScannerService:
"files": [],
}
await scanner_service._sync_audio_file(
temp_path, "CUSTOM", None, results
temp_path, "CUSTOM", None, results,
)
assert results["added"] == 1

View File

@@ -7,10 +7,8 @@ from unittest.mock import AsyncMock, Mock, patch
import pytest
from app.models.sound import Sound
from app.models.sound_played import SoundPlayed
from app.models.user import User
from app.services.vlc_player import VLCPlayerService, get_vlc_player_service
from app.utils.audio import get_sound_file_path
class TestVLCPlayerService:
@@ -79,16 +77,16 @@ class TestVLCPlayerService:
def test_find_vlc_executable_found_by_path(self, mock_run):
"""Test VLC executable detection when found by absolute path."""
mock_run.return_value.returncode = 1 # which command fails
# Mock Path to return True for the first absolute path
with patch("app.services.vlc_player.Path") as mock_path:
def path_side_effect(path_str):
mock_instance = Mock()
mock_instance.exists.return_value = str(path_str) == "/usr/bin/vlc"
return mock_instance
mock_path.side_effect = path_side_effect
service = VLCPlayerService()
assert service.vlc_executable == "/usr/bin/vlc"
@@ -100,10 +98,10 @@ class TestVLCPlayerService:
mock_path_instance = Mock()
mock_path_instance.exists.return_value = False
mock_path.return_value = mock_path_instance
# Mock which command as failing
mock_run.return_value.returncode = 1
service = VLCPlayerService()
assert service.vlc_executable == "vlc"
@@ -111,26 +109,26 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_play_sound_success(
self, mock_subprocess, vlc_service, sample_sound
self, mock_subprocess, vlc_service, sample_sound,
):
"""Test successful sound playback."""
# Mock subprocess
mock_process = Mock()
mock_process.pid = 12345
mock_subprocess.return_value = mock_process
# Mock the file path utility to avoid Path issues
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
mock_path = Mock()
mock_path.exists.return_value = True
mock_get_path.return_value = mock_path
result = await vlc_service.play_sound(sample_sound)
assert result is True
mock_subprocess.assert_called_once()
args = mock_subprocess.call_args
# Check command arguments
cmd_args = args[1] # keyword arguments
assert "--play-and-exit" in args[0]
@@ -144,7 +142,7 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
async def test_play_sound_file_not_found(
self, vlc_service, sample_sound
self, vlc_service, sample_sound,
):
"""Test sound playback when file doesn't exist."""
# Mock the file path utility to return a non-existent path
@@ -152,15 +150,15 @@ class TestVLCPlayerService:
mock_path = Mock()
mock_path.exists.return_value = False
mock_get_path.return_value = mock_path
result = await vlc_service.play_sound(sample_sound)
assert result is False
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_play_sound_subprocess_error(
self, mock_subprocess, vlc_service, sample_sound
self, mock_subprocess, vlc_service, sample_sound,
):
"""Test sound playback when subprocess fails."""
# Mock the file path utility to return an existing path
@@ -168,12 +166,12 @@ class TestVLCPlayerService:
mock_path = Mock()
mock_path.exists.return_value = True
mock_get_path.return_value = mock_path
# Mock subprocess exception
mock_subprocess.side_effect = Exception("Subprocess failed")
result = await vlc_service.play_sound(sample_sound)
assert result is False
@pytest.mark.asyncio
@@ -184,27 +182,27 @@ class TestVLCPlayerService:
mock_find_process = Mock()
mock_find_process.returncode = 0
mock_find_process.communicate = AsyncMock(
return_value=(b"12345\n67890\n", b"")
return_value=(b"12345\n67890\n", b""),
)
# Mock pkill process (kill VLC processes)
mock_kill_process = Mock()
mock_kill_process.communicate = AsyncMock(return_value=(b"", b""))
# Mock verify process (check remaining processes)
mock_verify_process = Mock()
mock_verify_process.returncode = 1 # No processes found
mock_verify_process.communicate = AsyncMock(return_value=(b"", b""))
# Set up subprocess mock to return different processes for each call
mock_subprocess.side_effect = [
mock_find_process,
mock_kill_process,
mock_verify_process,
]
result = await vlc_service.stop_all_vlc_instances()
assert result["success"] is True
assert result["processes_found"] == 2
assert result["processes_killed"] == 2
@@ -214,18 +212,18 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_no_processes(
self, mock_subprocess, vlc_service
self, mock_subprocess, vlc_service,
):
"""Test stopping VLC instances when none are running."""
# Mock pgrep process (no VLC processes found)
mock_find_process = Mock()
mock_find_process.returncode = 1 # No processes found
mock_find_process.communicate = AsyncMock(return_value=(b"", b""))
mock_subprocess.return_value = mock_find_process
result = await vlc_service.stop_all_vlc_instances()
assert result["success"] is True
assert result["processes_found"] == 0
assert result["processes_killed"] == 0
@@ -234,33 +232,33 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@patch("app.services.vlc_player.asyncio.create_subprocess_exec")
async def test_stop_all_vlc_instances_partial_kill(
self, mock_subprocess, vlc_service
self, mock_subprocess, vlc_service,
):
"""Test stopping VLC instances when some processes remain."""
# Mock pgrep process (find VLC processes)
mock_find_process = Mock()
mock_find_process.returncode = 0
mock_find_process.communicate = AsyncMock(
return_value=(b"12345\n67890\n11111\n", b"")
return_value=(b"12345\n67890\n11111\n", b""),
)
# Mock pkill process (kill VLC processes)
mock_kill_process = Mock()
mock_kill_process.communicate = AsyncMock(return_value=(b"", b""))
# Mock verify process (one process remains)
mock_verify_process = Mock()
mock_verify_process.returncode = 0
mock_verify_process.communicate = AsyncMock(return_value=(b"11111\n", b""))
mock_subprocess.side_effect = [
mock_find_process,
mock_kill_process,
mock_verify_process,
]
result = await vlc_service.stop_all_vlc_instances()
assert result["success"] is True
assert result["processes_found"] == 3
assert result["processes_killed"] == 2
@@ -272,9 +270,9 @@ class TestVLCPlayerService:
"""Test stopping VLC instances when an error occurs."""
# Mock subprocess exception
mock_subprocess.side_effect = Exception("Command failed")
result = await vlc_service.stop_all_vlc_instances()
assert result["success"] is False
assert result["processes_found"] == 0
assert result["processes_killed"] == 0
@@ -286,16 +284,16 @@ class TestVLCPlayerService:
with patch("app.services.vlc_player.VLCPlayerService") as mock_service_class:
mock_instance = Mock()
mock_service_class.return_value = mock_instance
# Clear the global instance
import app.services.vlc_player
app.services.vlc_player.vlc_player_service = None
# First call should create new instance
service1 = get_vlc_player_service()
assert service1 == mock_instance
mock_service_class.assert_called_once()
# Second call should return same instance
service2 = get_vlc_player_service()
assert service2 == mock_instance
@@ -306,22 +304,22 @@ class TestVLCPlayerService:
@pytest.mark.asyncio
@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
self, mock_subprocess, vlc_service_with_db, sample_sound,
):
"""Test sound playback with play count tracking."""
# Mock subprocess
mock_process = Mock()
mock_process.pid = 12345
mock_subprocess.return_value = mock_process
# Mock session and repositories
mock_session = AsyncMock()
vlc_service_with_db.db_session_factory.return_value = mock_session
# Mock repositories
mock_sound_repo = AsyncMock()
mock_user_repo = AsyncMock()
with patch("app.services.vlc_player.SoundRepository", return_value=mock_sound_repo):
with patch("app.services.vlc_player.UserRepository", return_value=mock_user_repo):
with patch("app.services.vlc_player.socket_manager") as mock_socket:
@@ -331,7 +329,7 @@ class TestVLCPlayerService:
mock_path = Mock()
mock_path.exists.return_value = True
mock_get_path.return_value = mock_path
# Mock sound repository responses
updated_sound = Sound(
id=1,
@@ -345,7 +343,7 @@ class TestVLCPlayerService:
)
mock_sound_repo.get_by_id.return_value = sample_sound
mock_sound_repo.update.return_value = updated_sound
# Mock admin user
admin_user = User(
id=1,
@@ -354,20 +352,20 @@ class TestVLCPlayerService:
role="admin",
)
mock_user_repo.get_by_id.return_value = admin_user
# Mock socket broadcast
mock_socket.broadcast_to_all = AsyncMock()
result = await vlc_service_with_db.play_sound(sample_sound)
# Wait a bit for the async task to complete
await asyncio.sleep(0.1)
assert result is True
# Verify subprocess was called
mock_subprocess.assert_called_once()
# Note: The async task runs in the background, so we can't easily
# verify the database operations in this test without more complex
# mocking or using a real async test framework setup
@@ -378,10 +376,10 @@ class TestVLCPlayerService:
# Mock session and repositories
mock_session = AsyncMock()
vlc_service_with_db.db_session_factory.return_value = mock_session
mock_sound_repo = AsyncMock()
mock_user_repo = AsyncMock()
# Create test sound and user
test_sound = Sound(
id=1,
@@ -399,7 +397,7 @@ class TestVLCPlayerService:
name="Admin User",
role="admin",
)
with patch("app.services.vlc_player.SoundRepository", return_value=mock_sound_repo):
with patch("app.services.vlc_player.UserRepository", return_value=mock_user_repo):
with patch("app.services.vlc_player.socket_manager") as mock_socket:
@@ -407,26 +405,26 @@ class TestVLCPlayerService:
# Setup mocks
mock_sound_repo.get_by_id.return_value = test_sound
mock_user_repo.get_by_id.return_value = admin_user
# Mock socket broadcast
mock_socket.broadcast_to_all = AsyncMock()
await vlc_service_with_db._record_play_count(1, "Test Sound")
# Verify sound repository calls
mock_sound_repo.get_by_id.assert_called_once_with(1)
mock_sound_repo.update.assert_called_once_with(
test_sound, {"play_count": 1}
test_sound, {"play_count": 1},
)
# Verify user repository calls
mock_user_repo.get_by_id.assert_called_once_with(1)
# Verify session operations
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()
mock_session.close.assert_called_once()
# Verify socket broadcast
mock_socket.broadcast_to_all.assert_called_once_with(
"sound_played",
@@ -451,10 +449,10 @@ class TestVLCPlayerService:
# Mock session and repositories
mock_session = AsyncMock()
vlc_service_with_db.db_session_factory.return_value = mock_session
mock_sound_repo = AsyncMock()
mock_user_repo = AsyncMock()
# Create test sound and user
test_sound = Sound(
id=1,
@@ -472,27 +470,27 @@ class TestVLCPlayerService:
name="Admin User",
role="admin",
)
with patch("app.services.vlc_player.SoundRepository", return_value=mock_sound_repo):
with patch("app.services.vlc_player.UserRepository", return_value=mock_user_repo):
with patch("app.services.vlc_player.socket_manager") as mock_socket:
# Setup mocks
mock_sound_repo.get_by_id.return_value = test_sound
mock_user_repo.get_by_id.return_value = admin_user
# Mock socket broadcast
mock_socket.broadcast_to_all = AsyncMock()
await vlc_service_with_db._record_play_count(1, "Test Sound")
# Verify sound play count was updated
mock_sound_repo.update.assert_called_once_with(
test_sound, {"play_count": 6}
test_sound, {"play_count": 6},
)
# Verify new SoundPlayed record was always added
mock_session.add.assert_called_once()
# Verify commit happened
mock_session.commit.assert_called_once()
@@ -502,10 +500,10 @@ class TestVLCPlayerService:
mock_file_path = Mock(spec=Path)
mock_file_path.exists.return_value = False # File doesn't exist
mock_path.return_value = mock_file_path
# This should fail because file doesn't exist
result = asyncio.run(vlc_service.play_sound(sample_sound))
# Verify the utility was called and returned False
mock_path.assert_called_once_with(sample_sound)
assert result is False
assert result is False