diff --git a/tests/api/v1/test_player_endpoints.py b/tests/api/v1/test_player_endpoints.py index 6fe39fd..457f3dc 100644 --- a/tests/api/v1/test_player_endpoints.py +++ b/tests/api/v1/test_player_endpoints.py @@ -516,10 +516,9 @@ class TestPlayerEndpoints: "status": PlayerStatus.PLAYING.value, "mode": PlayerMode.CONTINUOUS.value, "volume": 50, - "current_sound_id": 1, - "current_sound_index": 0, - "current_sound_position": 5000, - "current_sound_duration": 30000, + "position_ms": 5000, + "duration_ms": 30000, + "index": 0, "current_sound": { "id": 1, "name": "Test Song", @@ -530,11 +529,13 @@ class TestPlayerEndpoints: "thumbnail": None, "play_count": 0, }, - "playlist_id": 1, - "playlist_name": "Test Playlist", - "playlist_length": 1, - "playlist_duration": 30000, - "playlist_sounds": [], + "playlist": { + "id": 1, + "name": "Test Playlist", + "length": 1, + "duration": 30000, + "sounds": [], + }, } mock_player_service.get_state.return_value = mock_state diff --git a/tests/api/v1/test_vlc_endpoints.py b/tests/api/v1/test_vlc_endpoints.py index f3c51da..80e238b 100644 --- a/tests/api/v1/test_vlc_endpoints.py +++ b/tests/api/v1/test_vlc_endpoints.py @@ -1,12 +1,16 @@ """Tests for VLC player API endpoints.""" -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, Mock, patch import pytest from httpx import AsyncClient +from fastapi import FastAPI from app.models.sound import Sound from app.models.user import User +from app.api.v1.sounds import get_vlc_player, get_sound_repository, get_credit_service + + class TestVLCEndpoints: @@ -15,101 +19,168 @@ class TestVLCEndpoints: @pytest.mark.asyncio async def test_play_sound_with_vlc_success( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test successful sound playback via VLC.""" - # Mock the VLC player service and sound repository methods - with patch("app.services.vlc_player.VLCPlayerService.play_sound") as mock_play_sound: - mock_play_sound.return_value = True + # Set up mocks + mock_vlc_service = AsyncMock() + mock_repo = AsyncMock() + mock_credit_service = AsyncMock() + + # Set up test data + mock_sound = Sound( + id=1, + type="SDB", + name="Test Sound", + filename="test.mp3", + duration=5000, + size=1024, + hash="test_hash", + ) + + # Configure mocks + mock_repo.get_by_id.return_value = mock_sound + mock_credit_service.validate_and_reserve_credits.return_value = None + mock_credit_service.deduct_credits.return_value = None + mock_vlc_service.play_sound.return_value = True + + # Override dependencies + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + test_app.dependency_overrides[get_sound_repository] = lambda: mock_repo + test_app.dependency_overrides[get_credit_service] = lambda: mock_credit_service + + try: + response = await authenticated_client.post("/api/v1/sounds/vlc/play/1") - with patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_by_id: - mock_sound = Sound( - id=1, - type="SDB", - name="Test Sound", - filename="test.mp3", - duration=5000, - size=1024, - hash="test_hash", - ) - mock_get_by_id.return_value = mock_sound - - response = await authenticated_client.post("/api/v1/sounds/vlc/play/1") - - assert response.status_code == 200 - data = response.json() - assert data["success"] is True - assert data["sound_id"] == 1 - assert data["sound_name"] == "Test Sound" - assert "Test Sound" in data["message"] - - # Verify service calls - mock_get_by_id.assert_called_once_with(1) - mock_play_sound.assert_called_once_with(mock_sound) + assert response.status_code == 200 + data = response.json() + assert data["sound_id"] == 1 + assert data["sound_name"] == "Test Sound" + assert "Test Sound" in data["message"] + + # Verify service calls + mock_repo.get_by_id.assert_called_once_with(1) + mock_vlc_service.play_sound.assert_called_once_with(mock_sound) + finally: + # Clean up dependency overrides (except get_db which is needed for other tests) + test_app.dependency_overrides.pop(get_vlc_player, None) + test_app.dependency_overrides.pop(get_sound_repository, None) + test_app.dependency_overrides.pop(get_credit_service, None) @pytest.mark.asyncio async def test_play_sound_with_vlc_sound_not_found( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test VLC playback when sound is not found.""" - # Mock the sound repository to return None - with patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_by_id: - mock_get_by_id.return_value = None - + # Set up mocks + mock_vlc_service = AsyncMock() + mock_repo = AsyncMock() + mock_credit_service = AsyncMock() + + # Configure mocks + mock_repo.get_by_id.return_value = None + + # Override dependencies + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + test_app.dependency_overrides[get_sound_repository] = lambda: mock_repo + test_app.dependency_overrides[get_credit_service] = lambda: mock_credit_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/play/999") assert response.status_code == 404 data = response.json() assert "Sound with ID 999 not found" in data["detail"] + finally: + # Clean up dependency overrides (except get_db which is needed for other tests) + test_app.dependency_overrides.pop(get_vlc_player, None) + test_app.dependency_overrides.pop(get_sound_repository, None) + test_app.dependency_overrides.pop(get_credit_service, None) @pytest.mark.asyncio async def test_play_sound_with_vlc_launch_failure( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test VLC playback when VLC launch fails.""" - # Mock the VLC player service to fail - with patch("app.services.vlc_player.VLCPlayerService.play_sound") as mock_play_sound: - mock_play_sound.return_value = False + # Set up mocks + mock_vlc_service = AsyncMock() + mock_repo = AsyncMock() + mock_credit_service = AsyncMock() + + # Set up test data + mock_sound = Sound( + id=1, + type="SDB", + name="Test Sound", + filename="test.mp3", + duration=5000, + size=1024, + hash="test_hash", + ) + + # Configure mocks + mock_repo.get_by_id.return_value = mock_sound + mock_credit_service.validate_and_reserve_credits.return_value = None + mock_credit_service.deduct_credits.return_value = None + mock_vlc_service.play_sound.return_value = False + + # Override dependencies + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + test_app.dependency_overrides[get_sound_repository] = lambda: mock_repo + test_app.dependency_overrides[get_credit_service] = lambda: mock_credit_service + + try: + response = await authenticated_client.post("/api/v1/sounds/vlc/play/1") - with patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_by_id: - mock_sound = Sound( - id=1, - type="SDB", - name="Test Sound", - filename="test.mp3", - duration=5000, - size=1024, - hash="test_hash", - ) - mock_get_by_id.return_value = mock_sound - - response = await authenticated_client.post("/api/v1/sounds/vlc/play/1") - - assert response.status_code == 500 - data = response.json() - assert "Failed to launch VLC for sound playback" in data["detail"] + assert response.status_code == 500 + data = response.json() + assert "Failed to launch VLC for sound playback" in data["detail"] + finally: + # Clean up dependency overrides (except get_db which is needed for other tests) + test_app.dependency_overrides.pop(get_vlc_player, None) + test_app.dependency_overrides.pop(get_sound_repository, None) + test_app.dependency_overrides.pop(get_credit_service, None) @pytest.mark.asyncio async def test_play_sound_with_vlc_service_exception( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test VLC playback when service raises an exception.""" - # Mock the sound repository to raise an exception - with patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_by_id: - mock_get_by_id.side_effect = Exception("Database error") - + # Set up mocks + mock_vlc_service = AsyncMock() + mock_repo = AsyncMock() + mock_credit_service = AsyncMock() + + # Configure mocks + mock_repo.get_by_id.side_effect = Exception("Database error") + + # Override dependencies + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + test_app.dependency_overrides[get_sound_repository] = lambda: mock_repo + test_app.dependency_overrides[get_credit_service] = lambda: mock_credit_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/play/1") assert response.status_code == 500 data = response.json() assert "Failed to play sound" in data["detail"] + finally: + # Clean up dependency overrides (except get_db which is needed for other tests) + test_app.dependency_overrides.pop(get_vlc_player, None) + test_app.dependency_overrides.pop(get_sound_repository, None) + test_app.dependency_overrides.pop(get_credit_service, None) @pytest.mark.asyncio async def test_play_sound_with_vlc_unauthenticated( @@ -123,21 +194,26 @@ class TestVLCEndpoints: @pytest.mark.asyncio async def test_stop_all_vlc_instances_success( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test successful stopping of all VLC instances.""" - # Mock the VLC player service - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_result = { - "success": True, - "processes_found": 3, - "processes_killed": 3, - "processes_remaining": 0, - "message": "Killed 3 VLC processes", - } - mock_stop_all.return_value = mock_result - + # Set up mock + mock_vlc_service = AsyncMock() + mock_result = { + "success": True, + "processes_found": 3, + "processes_killed": 3, + "processes_remaining": 0, + "message": "Killed 3 VLC processes", + } + mock_vlc_service.stop_all_vlc_instances.return_value = mock_result + + # Override dependency + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 200 @@ -149,25 +225,33 @@ class TestVLCEndpoints: assert "Killed 3 VLC processes" in data["message"] # Verify service call - mock_stop_all.assert_called_once() + mock_vlc_service.stop_all_vlc_instances.assert_called_once() + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) @pytest.mark.asyncio async def test_stop_all_vlc_instances_no_processes( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test stopping VLC instances when none are running.""" - # Mock the VLC player service - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_result = { - "success": True, - "processes_found": 0, - "processes_killed": 0, - "message": "No VLC processes found", - } - mock_stop_all.return_value = mock_result - + # Set up mock + mock_vlc_service = AsyncMock() + mock_result = { + "success": True, + "processes_found": 0, + "processes_killed": 0, + "message": "No VLC processes found", + } + mock_vlc_service.stop_all_vlc_instances.return_value = mock_result + + # Override dependency + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 200 @@ -176,25 +260,33 @@ class TestVLCEndpoints: assert data["processes_found"] == 0 assert data["processes_killed"] == 0 assert data["message"] == "No VLC processes found" + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) @pytest.mark.asyncio async def test_stop_all_vlc_instances_partial_success( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test stopping VLC instances with partial success.""" - # Mock the VLC player service - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_result = { - "success": True, - "processes_found": 3, - "processes_killed": 2, - "processes_remaining": 1, - "message": "Killed 2 VLC processes", - } - mock_stop_all.return_value = mock_result - + # Set up mock + mock_vlc_service = AsyncMock() + mock_result = { + "success": True, + "processes_found": 3, + "processes_killed": 2, + "processes_remaining": 1, + "message": "Killed 2 VLC processes", + } + mock_vlc_service.stop_all_vlc_instances.return_value = mock_result + + # Override dependency + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 200 @@ -203,25 +295,33 @@ class TestVLCEndpoints: assert data["processes_found"] == 3 assert data["processes_killed"] == 2 assert data["processes_remaining"] == 1 + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) @pytest.mark.asyncio async def test_stop_all_vlc_instances_failure( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test stopping VLC instances when service fails.""" - # Mock the VLC player service - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_result = { - "success": False, - "processes_found": 0, - "processes_killed": 0, - "error": "Command failed", - "message": "Failed to stop VLC processes", - } - mock_stop_all.return_value = mock_result - + # Set up mock + mock_vlc_service = AsyncMock() + mock_result = { + "success": False, + "processes_found": 0, + "processes_killed": 0, + "error": "Command failed", + "message": "Failed to stop VLC processes", + } + mock_vlc_service.stop_all_vlc_instances.return_value = mock_result + + # Override dependency + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 200 @@ -229,23 +329,34 @@ class TestVLCEndpoints: assert data["success"] is False assert data["error"] == "Command failed" assert data["message"] == "Failed to stop VLC processes" + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) @pytest.mark.asyncio async def test_stop_all_vlc_instances_service_exception( self, + test_app: FastAPI, authenticated_client: AsyncClient, authenticated_user: User, ): """Test stopping VLC instances when service raises an exception.""" - # Mock the VLC player service to raise an exception - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_stop_all.side_effect = Exception("Service error") - + # Set up mock to raise an exception + mock_vlc_service = AsyncMock() + mock_vlc_service.stop_all_vlc_instances.side_effect = Exception("Service error") + + # Override dependency + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + + try: response = await authenticated_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 500 data = response.json() assert "Failed to stop VLC instances" in data["detail"] + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) @pytest.mark.asyncio async def test_stop_all_vlc_instances_unauthenticated( @@ -259,47 +370,71 @@ class TestVLCEndpoints: @pytest.mark.asyncio async def test_vlc_endpoints_with_admin_user( self, + test_app: FastAPI, authenticated_admin_client: AsyncClient, admin_user: User, ): """Test VLC endpoints work with admin user.""" - # Test play endpoint with admin - with patch("app.services.vlc_player.VLCPlayerService.play_sound") as mock_play_sound: - mock_play_sound.return_value = True + # Set up mocks + mock_vlc_service = AsyncMock() + mock_repo = AsyncMock() + mock_credit_service = AsyncMock() + + # Set up test data + mock_sound = Sound( + id=1, + type="SDB", + name="Admin Test Sound", + filename="admin_test.mp3", + duration=3000, + size=512, + hash="admin_hash", + ) + + # Configure mocks + mock_repo.get_by_id.return_value = mock_sound + mock_credit_service.validate_and_reserve_credits.return_value = None + mock_credit_service.deduct_credits.return_value = None + mock_vlc_service.play_sound.return_value = True + + # Override dependencies + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service + test_app.dependency_overrides[get_sound_repository] = lambda: mock_repo + test_app.dependency_overrides[get_credit_service] = lambda: mock_credit_service + + try: + response = await authenticated_admin_client.post("/api/v1/sounds/vlc/play/1") - with patch("app.repositories.sound.SoundRepository.get_by_id") as mock_get_by_id: - mock_sound = Sound( - id=1, - type="SDB", - name="Admin Test Sound", - filename="admin_test.mp3", - duration=3000, - size=512, - hash="admin_hash", - ) - mock_get_by_id.return_value = mock_sound - - response = await authenticated_admin_client.post("/api/v1/sounds/vlc/play/1") - - assert response.status_code == 200 - data = response.json() - assert data["success"] is True - assert data["sound_name"] == "Admin Test Sound" + assert response.status_code == 200 + data = response.json() + assert data["sound_name"] == "Admin Test Sound" + finally: + # Clean up dependency overrides (except get_db which is needed for other tests) + test_app.dependency_overrides.pop(get_vlc_player, None) + test_app.dependency_overrides.pop(get_sound_repository, None) + test_app.dependency_overrides.pop(get_credit_service, None) # Test stop-all endpoint with admin - with patch("app.services.vlc_player.VLCPlayerService.stop_all_vlc_instances") as mock_stop_all: - mock_result = { - "success": True, - "processes_found": 1, - "processes_killed": 1, - "processes_remaining": 0, - "message": "Killed 1 VLC processes", - } - mock_stop_all.return_value = mock_result - + mock_vlc_service_2 = AsyncMock() + mock_result = { + "success": True, + "processes_found": 1, + "processes_killed": 1, + "processes_remaining": 0, + "message": "Killed 1 VLC processes", + } + mock_vlc_service_2.stop_all_vlc_instances.return_value = mock_result + + # Override dependency for stop-all test + test_app.dependency_overrides[get_vlc_player] = lambda: mock_vlc_service_2 + + try: response = await authenticated_admin_client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 200 data = response.json() assert data["success"] is True - assert data["processes_killed"] == 1 \ No newline at end of file + assert data["processes_killed"] == 1 + finally: + # Clean up dependency override + test_app.dependency_overrides.pop(get_vlc_player, None) \ No newline at end of file