Files
sdb2-backend/tests/api/v1/test_vlc_endpoints.py

439 lines
16 KiB
Python

"""Tests for VLC player API endpoints."""
from unittest.mock import AsyncMock
import pytest
from fastapi import FastAPI
from httpx import AsyncClient
from app.api.v1.sounds import get_credit_service, get_sound_repository, get_vlc_player
from app.models.sound import Sound
from app.models.user import User
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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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,
) -> None:
"""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)