"""Tests for VLC player API endpoints.""" 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: """Test VLC player API endpoints.""" @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.""" # 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") 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.""" # 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.""" # 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") 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.""" # 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( self, client: AsyncClient, ): """Test VLC playback without authentication.""" response = await client.post("/api/v1/sounds/vlc/play/1") assert response.status_code == 401 @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.""" # 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 data = response.json() assert data["success"] is True assert data["processes_found"] == 3 assert data["processes_killed"] == 3 assert data["processes_remaining"] == 0 assert "Killed 3 VLC processes" in data["message"] # Verify service call 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.""" # 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 data = response.json() assert data["success"] is True 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.""" # 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 data = response.json() assert data["success"] is True 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.""" # 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 data = response.json() 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.""" # 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( self, client: AsyncClient, ): """Test stopping VLC instances without authentication.""" response = await client.post("/api/v1/sounds/vlc/stop-all") assert response.status_code == 401 @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.""" # 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") 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 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 finally: # Clean up dependency override test_app.dependency_overrides.pop(get_vlc_player, None)