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:
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user