""" Pytest fixtures for SwingMusic backend tests. """ import os import pytest import tempfile import shutil from pathlib import Path from unittest.mock import patch, MagicMock @pytest.fixture(scope="session") def test_data_dir(): """Create a temporary directory for test data.""" dir_path = tempfile.mkdtemp(prefix="swingmusic_test_") yield Path(dir_path) shutil.rmtree(dir_path, ignore_errors=True) @pytest.fixture(scope="session") def test_config_dir(test_data_dir): """Create a test configuration directory.""" config_dir = test_data_dir / "config" config_dir.mkdir(exist_ok=True) return config_dir @pytest.fixture(scope="session") def test_music_dir(test_data_dir): """Create a test music library directory with sample structure.""" music_dir = test_data_dir / "music" music_dir.mkdir(exist_ok=True) # Create sample artist/album structure artist_dir = music_dir / "Test Artist" artist_dir.mkdir(exist_ok=True) album_dir = artist_dir / "Test Album" album_dir.mkdir(exist_ok=True) # Create a dummy audio file (empty for testing) (album_dir / "01 Test Track.mp3").touch() return music_dir @pytest.fixture def mock_env(test_data_dir, test_config_dir, test_music_dir): """Set up test environment variables.""" env_vars = { "SWINGMUSIC_CONFIG_DIR": str(test_config_dir), "SWINGMUSIC_MUSIC_DIR": str(test_music_dir), "SWINGMUSIC_SECRET_KEY": "test-secret-key-for-integration-tests", "SWINGMUSIC_JWT_SECRET": "test-jwt-secret-for-integration-tests", "SWINGMUSIC_PAIR_CODE_TTL_SECONDS": "60", "SWINGMUSIC_PAIR_CODE_MAX_ACTIVE": "10", } with patch.dict(os.environ, env_vars, clear=False): yield env_vars @pytest.fixture def app(mock_env): """Create a test Flask application.""" # Import here to avoid circular imports and ensure env is set first from swingmusic.app_builder import build test_app = build() test_app.config.update({ "TESTING": True, "JWT_ACCESS_TOKEN_EXPIRES": 3600, "JWT_REFRESH_TOKEN_EXPIRES": 86400, }) yield test_app @pytest.fixture def client(app): """Create a test client for the Flask application.""" return app.test_client() @pytest.fixture def runner(app): """Create a test CLI runner for the Flask application.""" return app.test_cli_runner() @pytest.fixture def db_session(app): """Create a test database session.""" from swingmusic.db.userdata import UserTable from swingmusic.db.production import get_engine engine = get_engine() yield engine # Cleanup after each test engine.dispose() @pytest.fixture def test_user_data(): """Sample user data for testing.""" return { "username": "testuser", "password": "testpassword123", "email": "test@example.com", } @pytest.fixture def admin_user_data(): """Sample admin user data for testing.""" return { "username": "adminuser", "password": "adminpassword123", "email": "admin@example.com", "roles": ["admin", "user"], } @pytest.fixture def auth_headers(client, test_user_data): """Get authentication headers for a test user.""" # First, bootstrap the owner if no users exist response = client.get("/auth/bootstrap/status") status = response.get_json() if not status.get("setup_completed"): # Bootstrap owner client.post("/auth/bootstrap/owner", json={ "username": "owner", "password": "ownerpassword123", "root_dirs": [], }) # Create a test user via invite response = client.post("/auth/login", json={ "username": "owner", "password": "ownerpassword123", }) owner_token = response.get_json().get("accesstoken") # Create invite invite_response = client.post( "/auth/invite/create", json={"roles": ["user"]}, headers={"Authorization": f"Bearer {owner_token}"}, ) invite_token = invite_response.get_json().get("token") # Accept invite with test user client.post("/auth/invite/accept", json={ "token": invite_token, "username": test_user_data["username"], "password": test_user_data["password"], }) # Login as test user response = client.post("/auth/login", json={ "username": test_user_data["username"], "password": test_user_data["password"], }) token = response.get_json().get("accesstoken") return {"Authorization": f"Bearer {token}"} @pytest.fixture def admin_auth_headers(client, admin_user_data): """Get authentication headers for an admin user.""" # Similar to auth_headers but with admin role response = client.get("/auth/bootstrap/status") status = response.get_json() if not status.get("setup_completed"): client.post("/auth/bootstrap/owner", json={ "username": "owner", "password": "ownerpassword123", "root_dirs": [], }) response = client.post("/auth/login", json={ "username": "owner", "password": "ownerpassword123", }) owner_token = response.get_json().get("accesstoken") invite_response = client.post( "/auth/invite/create", json={"roles": ["admin", "user"]}, headers={"Authorization": f"Bearer {owner_token}"}, ) invite_token = invite_response.get_json().get("token") client.post("/auth/invite/accept", json={ "token": invite_token, "username": admin_user_data["username"], "password": admin_user_data["password"], }) response = client.post("/auth/login", json={ "username": admin_user_data["username"], "password": admin_user_data["password"], }) token = response.get_json().get("accesstoken") return {"Authorization": f"Bearer {token}"} @pytest.fixture def sample_trackhash(): """Sample trackhash for testing.""" return "abc123def456" @pytest.fixture def sample_download_job(): """Sample download job data for testing.""" return { "source_url": "https://open.spotify.com/track/1234567890", "source": "spotify", "quality": "high", "trackhash": "testhash123", "title": "Test Track", "artist": "Test Artist", "album": "Test Album", "item_type": "track", }