Refactor test cases for improved readability and consistency
- Adjusted function signatures in various test files to enhance clarity by aligning parameters. - Updated patching syntax for better readability across test cases. - Improved formatting and spacing in test assertions and mock setups. - Ensured consistent use of async/await patterns in async test functions. - Enhanced comments for better understanding of test intentions.
This commit is contained in:
@@ -48,7 +48,9 @@ class TestCreditService:
|
||||
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)
|
||||
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)
|
||||
@@ -72,7 +74,9 @@ class TestCreditService:
|
||||
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)
|
||||
result = await credit_service.check_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND,
|
||||
)
|
||||
|
||||
assert result is False
|
||||
mock_session.close.assert_called_once()
|
||||
@@ -87,13 +91,17 @@ class TestCreditService:
|
||||
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)
|
||||
result = await credit_service.check_credits(
|
||||
999, CreditActionType.VLC_PLAY_SOUND,
|
||||
)
|
||||
|
||||
assert result is False
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_success(self, credit_service, sample_user) -> None:
|
||||
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()
|
||||
|
||||
@@ -103,7 +111,8 @@ class TestCreditService:
|
||||
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
|
||||
@@ -112,7 +121,9 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_insufficient(self, credit_service) -> None:
|
||||
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(
|
||||
@@ -131,7 +142,8 @@ class TestCreditService:
|
||||
|
||||
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
|
||||
@@ -139,7 +151,9 @@ class TestCreditService:
|
||||
mock_session.close.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_and_reserve_credits_user_not_found(self, credit_service) -> None:
|
||||
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()
|
||||
|
||||
@@ -150,7 +164,8 @@ class TestCreditService:
|
||||
|
||||
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()
|
||||
@@ -160,15 +175,20 @@ class TestCreditService:
|
||||
"""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:
|
||||
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()
|
||||
|
||||
await credit_service.deduct_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND, success=True, metadata={"test": "data"},
|
||||
1,
|
||||
CreditActionType.VLC_PLAY_SOUND,
|
||||
success=True,
|
||||
metadata={"test": "data"},
|
||||
)
|
||||
|
||||
# Verify user credits were updated
|
||||
@@ -180,7 +200,9 @@ class TestCreditService:
|
||||
|
||||
# Verify socket event was emitted
|
||||
mock_socket_manager.send_to_user.assert_called_once_with(
|
||||
"1", "user_credits_changed", {
|
||||
"1",
|
||||
"user_credits_changed",
|
||||
{
|
||||
"user_id": "1",
|
||||
"credits_before": 10,
|
||||
"credits_after": 9,
|
||||
@@ -202,19 +224,25 @@ 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) -> None:
|
||||
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()
|
||||
|
||||
with patch("app.services.credit.UserRepository") as mock_repo_class, \
|
||||
patch("app.services.credit.socket_manager") as mock_socket_manager:
|
||||
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()
|
||||
|
||||
await credit_service.deduct_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND, success=False, # Action failed
|
||||
1,
|
||||
CreditActionType.VLC_PLAY_SOUND,
|
||||
success=False, # Action failed
|
||||
)
|
||||
|
||||
# Verify user credits were NOT updated (action requires success)
|
||||
@@ -247,8 +275,10 @@ class TestCreditService:
|
||||
plan_id=1,
|
||||
)
|
||||
|
||||
with patch("app.services.credit.UserRepository") as mock_repo_class, \
|
||||
patch("app.services.credit.socket_manager") as mock_socket_manager:
|
||||
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
|
||||
@@ -256,7 +286,9 @@ class TestCreditService:
|
||||
|
||||
with pytest.raises(InsufficientCreditsError):
|
||||
await credit_service.deduct_credits(
|
||||
1, CreditActionType.VLC_PLAY_SOUND, success=True,
|
||||
1,
|
||||
CreditActionType.VLC_PLAY_SOUND,
|
||||
success=True,
|
||||
)
|
||||
|
||||
# Verify no socket event was emitted since credits could not be deducted
|
||||
@@ -270,15 +302,20 @@ class TestCreditService:
|
||||
"""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:
|
||||
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()
|
||||
|
||||
await credit_service.add_credits(
|
||||
1, 5, "Bonus credits", {"reason": "signup"},
|
||||
1,
|
||||
5,
|
||||
"Bonus credits",
|
||||
{"reason": "signup"},
|
||||
)
|
||||
|
||||
# Verify user credits were updated
|
||||
@@ -290,7 +327,9 @@ class TestCreditService:
|
||||
|
||||
# Verify socket event was emitted
|
||||
mock_socket_manager.send_to_user.assert_called_once_with(
|
||||
"1", "user_credits_changed", {
|
||||
"1",
|
||||
"user_credits_changed",
|
||||
{
|
||||
"user_id": "1",
|
||||
"credits_before": 10,
|
||||
"credits_after": 15,
|
||||
|
||||
@@ -53,7 +53,9 @@ 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,
|
||||
) -> None:
|
||||
"""Test service detection for YouTube."""
|
||||
mock_ydl = Mock()
|
||||
@@ -78,7 +80,9 @@ 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,
|
||||
) -> None:
|
||||
"""Test service detection failure."""
|
||||
mock_ydl = Mock()
|
||||
@@ -170,7 +174,9 @@ class TestExtractionService:
|
||||
assert result["status"] == "pending"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_extraction_with_service_detection(self, extraction_service) -> None:
|
||||
async def test_process_extraction_with_service_detection(
|
||||
self, extraction_service,
|
||||
) -> None:
|
||||
"""Test extraction processing with service detection."""
|
||||
extraction_id = 1
|
||||
|
||||
@@ -202,14 +208,18 @@ 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"),
|
||||
patch.object(extraction_service, "_add_to_main_playlist"),
|
||||
@@ -289,11 +299,13 @@ 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(
|
||||
|
||||
@@ -29,7 +29,9 @@ 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,
|
||||
):
|
||||
# Start the processor
|
||||
await processor.start()
|
||||
@@ -229,7 +231,9 @@ class TestExtractionProcessor:
|
||||
"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,
|
||||
),
|
||||
patch(
|
||||
"app.services.extraction_processor.ExtractionService",
|
||||
@@ -274,7 +278,9 @@ class TestExtractionProcessor:
|
||||
"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,
|
||||
),
|
||||
patch(
|
||||
"app.services.extraction_processor.ExtractionService",
|
||||
|
||||
@@ -131,11 +131,15 @@ class TestPlayerService:
|
||||
yield mock
|
||||
|
||||
@pytest.fixture
|
||||
def player_service(self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager):
|
||||
def player_service(
|
||||
self, mock_db_session_factory, mock_vlc_instance, mock_socket_manager,
|
||||
):
|
||||
"""Create a player service instance for testing."""
|
||||
return PlayerService(mock_db_session_factory)
|
||||
|
||||
def test_init_creates_player_service(self, mock_db_session_factory, mock_vlc_instance) -> None:
|
||||
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)
|
||||
@@ -152,7 +156,9 @@ class TestPlayerService:
|
||||
assert service._loop is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_initializes_service(self, player_service, mock_vlc_instance) -> None:
|
||||
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()
|
||||
@@ -197,7 +203,9 @@ class TestPlayerService:
|
||||
mock_file_path.exists.return_value = True
|
||||
mock_path.return_value = mock_file_path
|
||||
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
):
|
||||
mock_media = Mock()
|
||||
player_service._vlc_instance.media_new.return_value = mock_media
|
||||
player_service._player.play.return_value = 0 # Success
|
||||
@@ -252,7 +260,9 @@ class TestPlayerService:
|
||||
"""Test pausing when not playing does nothing."""
|
||||
player_service.state.status = PlayerStatus.STOPPED
|
||||
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock) as mock_broadcast:
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
) as mock_broadcast:
|
||||
await player_service.pause()
|
||||
|
||||
assert player_service.state.status == PlayerStatus.STOPPED
|
||||
@@ -264,8 +274,12 @@ class TestPlayerService:
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
player_service.state.current_sound_position = 5000
|
||||
|
||||
with patch.object(player_service, "_process_play_count", new_callable=AsyncMock):
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
with patch.object(
|
||||
player_service, "_process_play_count", new_callable=AsyncMock,
|
||||
):
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
):
|
||||
await player_service.stop_playback()
|
||||
|
||||
assert player_service.state.status == PlayerStatus.STOPPED
|
||||
@@ -314,7 +328,9 @@ class TestPlayerService:
|
||||
"""Test seeking when stopped does nothing."""
|
||||
player_service.state.status = PlayerStatus.STOPPED
|
||||
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock) as mock_broadcast:
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
) as mock_broadcast:
|
||||
await player_service.seek(15000)
|
||||
|
||||
player_service._player.set_position.assert_not_called()
|
||||
@@ -364,7 +380,9 @@ class TestPlayerService:
|
||||
mock_playlist = Mock()
|
||||
mock_playlist.id = 1
|
||||
mock_playlist.name = "Test Playlist"
|
||||
mock_repo.get_current_playlist.return_value = mock_playlist # Return current playlist directly
|
||||
mock_repo.get_current_playlist.return_value = (
|
||||
mock_playlist # Return current playlist directly
|
||||
)
|
||||
|
||||
# Mock sounds
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
@@ -372,7 +390,9 @@ class TestPlayerService:
|
||||
mock_sounds = [sound1, sound2]
|
||||
mock_repo.get_playlist_sounds.return_value = mock_sounds
|
||||
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
):
|
||||
await player_service.reload_playlist()
|
||||
|
||||
assert player_service.state.playlist_id == 1
|
||||
@@ -394,7 +414,9 @@ class TestPlayerService:
|
||||
sound2 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
sounds = [sound1, sound2]
|
||||
|
||||
with patch.object(player_service, "_stop_playback", new_callable=AsyncMock) as mock_stop:
|
||||
with patch.object(
|
||||
player_service, "_stop_playback", new_callable=AsyncMock,
|
||||
) as mock_stop:
|
||||
await player_service._handle_playlist_id_changed(1, 2, sounds)
|
||||
|
||||
# Should stop playback and set first track as current
|
||||
@@ -404,11 +426,15 @@ 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) -> None:
|
||||
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
|
||||
|
||||
with patch.object(player_service, "_stop_playback", new_callable=AsyncMock) as mock_stop:
|
||||
with patch.object(
|
||||
player_service, "_stop_playback", new_callable=AsyncMock,
|
||||
) as mock_stop:
|
||||
await player_service._handle_playlist_id_changed(1, 2, [])
|
||||
|
||||
mock_stop.assert_called_once()
|
||||
@@ -417,7 +443,9 @@ 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) -> None:
|
||||
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)
|
||||
@@ -426,11 +454,15 @@ class TestPlayerService:
|
||||
await player_service._handle_same_playlist_track_check(1, 0, sounds)
|
||||
|
||||
# Should update sound object reference but keep same index
|
||||
assert player_service.state.current_sound_index == 0 # Should be set to 0 from new_index
|
||||
assert (
|
||||
player_service.state.current_sound_index == 0
|
||||
) # Should be set to 0 from new_index
|
||||
assert player_service.state.current_sound == sound1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_same_playlist_track_exists_different_index(self, player_service) -> None:
|
||||
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)
|
||||
@@ -450,7 +482,9 @@ class TestPlayerService:
|
||||
sound2 = Sound(id=3, name="Song 3", filename="song3.mp3", duration=60000)
|
||||
sounds = [sound1, sound2] # Track with ID 1 is missing
|
||||
|
||||
with patch.object(player_service, "_handle_track_removed", new_callable=AsyncMock) as mock_removed:
|
||||
with patch.object(
|
||||
player_service, "_handle_track_removed", new_callable=AsyncMock,
|
||||
) as mock_removed:
|
||||
await player_service._handle_same_playlist_track_check(1, 0, sounds)
|
||||
mock_removed.assert_called_once_with(1, sounds)
|
||||
|
||||
@@ -461,7 +495,9 @@ class TestPlayerService:
|
||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
sounds = [sound1]
|
||||
|
||||
with patch.object(player_service, "_stop_playback", new_callable=AsyncMock) as mock_stop:
|
||||
with patch.object(
|
||||
player_service, "_stop_playback", new_callable=AsyncMock,
|
||||
) as mock_stop:
|
||||
await player_service._handle_track_removed(1, sounds)
|
||||
|
||||
mock_stop.assert_called_once()
|
||||
@@ -474,7 +510,9 @@ class TestPlayerService:
|
||||
"""Test handling when current track is removed with empty playlist."""
|
||||
player_service.state.status = PlayerStatus.PLAYING
|
||||
|
||||
with patch.object(player_service, "_stop_playback", new_callable=AsyncMock) as mock_stop:
|
||||
with patch.object(
|
||||
player_service, "_stop_playback", new_callable=AsyncMock,
|
||||
) as mock_stop:
|
||||
await player_service._handle_track_removed(1, [])
|
||||
|
||||
mock_stop.assert_called_once()
|
||||
@@ -562,14 +600,20 @@ class TestPlayerService:
|
||||
mock_playlist = Mock()
|
||||
mock_playlist.id = 2 # Different ID
|
||||
mock_playlist.name = "New Playlist"
|
||||
mock_repo.get_current_playlist.return_value = mock_playlist # Return current playlist directly
|
||||
mock_repo.get_current_playlist.return_value = (
|
||||
mock_playlist # Return current playlist directly
|
||||
)
|
||||
|
||||
sound1 = Sound(id=1, name="Song 1", filename="song1.mp3", duration=30000)
|
||||
mock_sounds = [sound1]
|
||||
mock_repo.get_playlist_sounds.return_value = mock_sounds
|
||||
|
||||
with patch.object(player_service, "_stop_playback", new_callable=AsyncMock) as mock_stop:
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
with patch.object(
|
||||
player_service, "_stop_playback", new_callable=AsyncMock,
|
||||
) as mock_stop:
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
):
|
||||
await player_service.reload_playlist()
|
||||
|
||||
# Should stop and reset to first track
|
||||
@@ -597,7 +641,9 @@ class TestPlayerService:
|
||||
mock_playlist = Mock()
|
||||
mock_playlist.id = 1
|
||||
mock_playlist.name = "Same Playlist"
|
||||
mock_repo.get_current_playlist.return_value = mock_playlist # Return current playlist directly
|
||||
mock_repo.get_current_playlist.return_value = (
|
||||
mock_playlist # Return current playlist directly
|
||||
)
|
||||
|
||||
# Track 2 moved to index 0
|
||||
sound1 = Sound(id=2, name="Song 2", filename="song2.mp3", duration=45000)
|
||||
@@ -605,7 +651,9 @@ class TestPlayerService:
|
||||
mock_sounds = [sound1, sound2] # Track 2 now at index 0
|
||||
mock_repo.get_playlist_sounds.return_value = mock_sounds
|
||||
|
||||
with patch.object(player_service, "_broadcast_state", new_callable=AsyncMock):
|
||||
with patch.object(
|
||||
player_service, "_broadcast_state", new_callable=AsyncMock,
|
||||
):
|
||||
await player_service.reload_playlist()
|
||||
|
||||
# Should update index but keep same track
|
||||
@@ -614,7 +662,6 @@ class TestPlayerService:
|
||||
assert player_service.state.current_sound_id == 2 # Same track
|
||||
assert player_service.state.current_sound == sound1
|
||||
|
||||
|
||||
def test_get_next_index_continuous_mode(self, player_service) -> None:
|
||||
"""Test getting next index in continuous mode."""
|
||||
player_service.state.mode = PlayerMode.CONTINUOUS
|
||||
@@ -734,7 +781,8 @@ 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
|
||||
|
||||
@@ -98,7 +98,11 @@ 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,
|
||||
) -> None:
|
||||
"""Test successful connection with valid token."""
|
||||
# Setup mocks
|
||||
@@ -132,7 +136,10 @@ 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,
|
||||
) -> None:
|
||||
"""Test connection with no access token."""
|
||||
# Setup mocks
|
||||
@@ -165,7 +172,11 @@ 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,
|
||||
) -> None:
|
||||
"""Test connection with invalid token."""
|
||||
# Setup mocks
|
||||
@@ -199,7 +210,11 @@ 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,
|
||||
) -> None:
|
||||
"""Test connection with token missing user ID."""
|
||||
# Setup mocks
|
||||
@@ -254,7 +269,9 @@ 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) -> None:
|
||||
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 = {}
|
||||
|
||||
@@ -154,7 +154,9 @@ class TestSoundNormalizerService:
|
||||
assert result["id"] == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_force_already_normalized(self, normalizer_service) -> None:
|
||||
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,
|
||||
@@ -172,14 +174,17 @@ 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",
|
||||
),
|
||||
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 +250,17 @@ 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
|
||||
@@ -275,7 +283,9 @@ class TestSoundNormalizerService:
|
||||
mock_normalize.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_normalize_sound_normalization_error(self, normalizer_service) -> None:
|
||||
async def test_normalize_sound_normalization_error(
|
||||
self, normalizer_service,
|
||||
) -> None:
|
||||
"""Test handling normalization errors."""
|
||||
sound = Sound(
|
||||
id=1,
|
||||
@@ -300,7 +310,8 @@ 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")
|
||||
@@ -529,7 +540,11 @@ 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()
|
||||
|
||||
@@ -153,7 +153,10 @@ 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 +260,10 @@ 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 +302,8 @@ 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 +323,10 @@ class TestSoundScannerService:
|
||||
"files": [],
|
||||
}
|
||||
await scanner_service._sync_audio_file(
|
||||
temp_path, "CUSTOM", None, results,
|
||||
temp_path,
|
||||
"CUSTOM",
|
||||
None,
|
||||
results,
|
||||
)
|
||||
|
||||
assert results["added"] == 1
|
||||
|
||||
@@ -80,6 +80,7 @@ class TestVLCPlayerService:
|
||||
|
||||
# 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"
|
||||
@@ -105,11 +106,13 @@ class TestVLCPlayerService:
|
||||
service = VLCPlayerService()
|
||||
assert service.vlc_executable == "vlc"
|
||||
|
||||
|
||||
@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,
|
||||
) -> None:
|
||||
"""Test successful sound playback."""
|
||||
# Mock subprocess
|
||||
@@ -142,7 +145,9 @@ class TestVLCPlayerService:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_play_sound_file_not_found(
|
||||
self, vlc_service, sample_sound,
|
||||
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
|
||||
@@ -158,7 +163,10 @@ class TestVLCPlayerService:
|
||||
@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,
|
||||
) -> None:
|
||||
"""Test sound playback when subprocess fails."""
|
||||
# Mock the file path utility to return an existing path
|
||||
@@ -176,7 +184,9 @@ 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) -> None:
|
||||
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()
|
||||
@@ -212,7 +222,9 @@ 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,
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when none are running."""
|
||||
# Mock pgrep process (no VLC processes found)
|
||||
@@ -232,7 +244,9 @@ 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,
|
||||
) -> None:
|
||||
"""Test stopping VLC instances when some processes remain."""
|
||||
# Mock pgrep process (find VLC processes)
|
||||
@@ -266,7 +280,9 @@ 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) -> None:
|
||||
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")
|
||||
@@ -287,6 +303,7 @@ class TestVLCPlayerService:
|
||||
|
||||
# Clear the global instance
|
||||
import app.services.vlc_player
|
||||
|
||||
app.services.vlc_player.vlc_player_service = None
|
||||
|
||||
# First call should create new instance
|
||||
@@ -304,7 +321,10 @@ 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,
|
||||
) -> None:
|
||||
"""Test sound playback with play count tracking."""
|
||||
# Mock subprocess
|
||||
@@ -320,11 +340,17 @@ class TestVLCPlayerService:
|
||||
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.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:
|
||||
# Mock the file path utility
|
||||
with patch("app.services.vlc_player.get_sound_file_path") as mock_get_path:
|
||||
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
|
||||
@@ -397,8 +423,12 @@ class TestVLCPlayerService:
|
||||
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.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
|
||||
@@ -412,7 +442,8 @@ class TestVLCPlayerService:
|
||||
# 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
|
||||
@@ -442,7 +473,9 @@ class TestVLCPlayerService:
|
||||
# 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) -> None:
|
||||
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()
|
||||
@@ -469,28 +502,33 @@ class TestVLCPlayerService:
|
||||
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.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
|
||||
# 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()
|
||||
# Mock socket broadcast
|
||||
mock_socket.broadcast_to_all = AsyncMock()
|
||||
|
||||
await vlc_service_with_db._record_play_count(1, "Test Sound")
|
||||
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},
|
||||
)
|
||||
# Verify sound play count was updated
|
||||
mock_sound_repo.update.assert_called_once_with(
|
||||
test_sound,
|
||||
{"play_count": 6},
|
||||
)
|
||||
|
||||
# Verify new SoundPlayed record was always added
|
||||
mock_session.add.assert_called_once()
|
||||
# Verify new SoundPlayed record was always added
|
||||
mock_session.add.assert_called_once()
|
||||
|
||||
# Verify commit happened
|
||||
mock_session.commit.assert_called_once()
|
||||
# Verify commit happened
|
||||
mock_session.commit.assert_called_once()
|
||||
|
||||
def test_uses_shared_sound_path_utility(self, vlc_service, sample_sound) -> None:
|
||||
"""Test that VLC service uses the shared sound path utility."""
|
||||
|
||||
Reference in New Issue
Block a user