import uuid from datetime import datetime, timezone from unittest.mock import MagicMock, patch import pytest from fastapi import status from sqlalchemy.orm import Session from app.auth.dependencies import get_current_user # Import the router we're testing from app.routers.playlist import ( ProcessStatus, ValidationProcessResponse, ValidationResultResponse, router, validation_processes, ) from app.utils.database import get_db # Import mocks and fixtures from tests.utils.auth_test_fixtures import ( admin_user_client, db_session, non_admin_user_client, ) from tests.utils.db_mocks import MockChannelDB # --- Test Fixtures --- @pytest.fixture def mock_stream_manager(): with patch("app.routers.playlist.StreamManager") as mock: yield mock # --- Test Cases For Stream Validation --- def test_start_stream_validation_success( db_session: Session, admin_user_client, mock_stream_manager ): """Test starting a stream validation process""" mock_instance = mock_stream_manager.return_value mock_instance.validate_and_select_stream.return_value = "http://valid.stream.url" response = admin_user_client.post( "/playlist/validate-streams", json={"channel_id": "test-channel"} ) assert response.status_code == status.HTTP_202_ACCEPTED data = response.json() assert "process_id" in data assert data["status"] == ProcessStatus.PENDING assert data["message"] == "Validation process started" # Verify process was added to tracking process_id = data["process_id"] assert process_id in validation_processes # In test environment, background tasks run synchronously so status may be COMPLETED assert validation_processes[process_id]["status"] in [ ProcessStatus.PENDING, ProcessStatus.COMPLETED, ] assert validation_processes[process_id]["channel_id"] == "test-channel" def test_get_validation_status_pending(db_session: Session, admin_user_client): """Test checking status of pending validation""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.PENDING, "channel_id": "test-channel", } response = admin_user_client.get(f"/playlist/validate-streams/{process_id}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["process_id"] == process_id assert data["status"] == ProcessStatus.PENDING assert data["working_streams"] is None assert data["error"] is None def test_get_validation_status_completed(db_session: Session, admin_user_client): """Test checking status of completed validation""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.COMPLETED, "channel_id": "test-channel", "result": { "working_streams": [ {"channel_id": "test-channel", "stream_url": "http://valid.stream.url"} ] }, } response = admin_user_client.get(f"/playlist/validate-streams/{process_id}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["process_id"] == process_id assert data["status"] == ProcessStatus.COMPLETED assert len(data["working_streams"]) == 1 assert data["working_streams"][0]["channel_id"] == "test-channel" assert data["working_streams"][0]["stream_url"] == "http://valid.stream.url" assert data["error"] is None def test_get_validation_status_completed_with_error( db_session: Session, admin_user_client ): """Test checking status of completed validation with error""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.COMPLETED, "channel_id": "test-channel", "error": "No working streams found for channel test-channel", } response = admin_user_client.get(f"/playlist/validate-streams/{process_id}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["process_id"] == process_id assert data["status"] == ProcessStatus.COMPLETED assert data["working_streams"] is None assert data["error"] == "No working streams found for channel test-channel" def test_get_validation_status_failed(db_session: Session, admin_user_client): """Test checking status of failed validation""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.FAILED, "channel_id": "test-channel", "error": "Validation error occurred", } response = admin_user_client.get(f"/playlist/validate-streams/{process_id}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["process_id"] == process_id assert data["status"] == ProcessStatus.FAILED assert data["working_streams"] is None assert data["error"] == "Validation error occurred" def test_get_validation_status_not_found(db_session: Session, admin_user_client): """Test checking status of non-existent process""" random_uuid = str(uuid.uuid4()) response = admin_user_client.get(f"/playlist/validate-streams/{random_uuid}") assert response.status_code == status.HTTP_404_NOT_FOUND assert "Process not found" in response.json()["detail"] def test_run_stream_validation_success(mock_stream_manager, db_session): """Test the background validation task success case""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.PENDING, "channel_id": "test-channel", } mock_instance = mock_stream_manager.return_value mock_instance.validate_and_select_stream.return_value = "http://valid.stream.url" from app.routers.playlist import run_stream_validation run_stream_validation(process_id, "test-channel", db_session) assert validation_processes[process_id]["status"] == ProcessStatus.COMPLETED assert len(validation_processes[process_id]["result"]["working_streams"]) == 1 assert ( validation_processes[process_id]["result"]["working_streams"][0].channel_id == "test-channel" ) assert ( validation_processes[process_id]["result"]["working_streams"][0].stream_url == "http://valid.stream.url" ) def test_run_stream_validation_failure(mock_stream_manager, db_session): """Test the background validation task failure case""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.PENDING, "channel_id": "test-channel", } mock_instance = mock_stream_manager.return_value mock_instance.validate_and_select_stream.return_value = None from app.routers.playlist import run_stream_validation run_stream_validation(process_id, "test-channel", db_session) assert validation_processes[process_id]["status"] == ProcessStatus.COMPLETED assert "error" in validation_processes[process_id] assert "No working streams found" in validation_processes[process_id]["error"] def test_run_stream_validation_exception(mock_stream_manager, db_session): """Test the background validation task exception case""" process_id = str(uuid.uuid4()) validation_processes[process_id] = { "status": ProcessStatus.PENDING, "channel_id": "test-channel", } mock_instance = mock_stream_manager.return_value mock_instance.validate_and_select_stream.side_effect = Exception("Test error") from app.routers.playlist import run_stream_validation run_stream_validation(process_id, "test-channel", db_session) assert validation_processes[process_id]["status"] == ProcessStatus.FAILED assert "error" in validation_processes[process_id] assert "Test error" in validation_processes[process_id]["error"] def test_start_stream_validation_no_channel_id( db_session: Session, admin_user_client, mock_stream_manager ): """Test starting validation without channel_id""" response = admin_user_client.post("/playlist/validate-streams", json={}) assert response.status_code == status.HTTP_202_ACCEPTED data = response.json() assert "process_id" in data assert data["status"] == ProcessStatus.PENDING # Verify process was added to tracking process_id = data["process_id"] assert process_id in validation_processes assert validation_processes[process_id]["status"] in [ ProcessStatus.PENDING, ProcessStatus.COMPLETED, ] assert validation_processes[process_id]["channel_id"] is None assert "not yet implemented" in validation_processes[process_id].get("error", "") def test_run_stream_validation_no_channel_id(mock_stream_manager, db_session): """Test background validation without channel_id""" process_id = str(uuid.uuid4()) validation_processes[process_id] = {"status": ProcessStatus.PENDING} from app.routers.playlist import run_stream_validation run_stream_validation(process_id, None, db_session) assert validation_processes[process_id]["status"] == ProcessStatus.COMPLETED assert "error" in validation_processes[process_id] assert "not yet implemented" in validation_processes[process_id]["error"]