Files
musicseerr/backend/tests/routes/test_plex_auth.py
T
2026-04-18 23:12:53 +01:00

137 lines
4.7 KiB
Python

from __future__ import annotations
import os
import tempfile
os.environ.setdefault("ROOT_APP_DIR", tempfile.mkdtemp())
from unittest.mock import AsyncMock, MagicMock
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from api.v1.routes.plex_auth import router as auth_router
from api.v1.schemas.settings import PlexOAuthPinResponse, PlexOAuthPollResponse
from core.dependencies import get_plex_repository, get_preferences_service
from core.exceptions import PlexApiError
@pytest.fixture
def mock_preferences():
mock = MagicMock()
mock.get_setting = MagicMock(return_value="existing-client-id")
mock.save_setting = MagicMock()
mock.get_or_create_setting = MagicMock(return_value="existing-client-id")
return mock
@pytest.fixture
def mock_repo():
mock = MagicMock()
pin = MagicMock()
pin.id = 12345
pin.code = "ABCD"
mock.create_oauth_pin = AsyncMock(return_value=pin)
mock.poll_oauth_pin = AsyncMock(return_value=None)
return mock
@pytest.fixture
def auth_client(mock_preferences, mock_repo):
app = FastAPI()
app.include_router(auth_router)
app.dependency_overrides[get_preferences_service] = lambda: mock_preferences
app.dependency_overrides[get_plex_repository] = lambda: mock_repo
return TestClient(app)
class TestCreatePlexPin:
def test_creates_pin(self, auth_client):
resp = auth_client.post("/plex/auth/pin")
assert resp.status_code == 200
data = resp.json()
assert data["pin_id"] == 12345
assert data["pin_code"] == "ABCD"
assert "auth_url" in data
assert "clientID=existing-client-id" in data["auth_url"]
def test_generates_client_id_if_missing(self, auth_client, mock_preferences):
import uuid
generated = str(uuid.uuid4())
mock_preferences.get_or_create_setting = MagicMock(return_value=generated)
resp = auth_client.post("/plex/auth/pin")
assert resp.status_code == 200
mock_preferences.get_or_create_setting.assert_called_once()
call_args = mock_preferences.get_or_create_setting.call_args
assert call_args[0][0] == "plex_client_id"
def test_502_on_plex_error(self, auth_client, mock_repo):
mock_repo.create_oauth_pin = AsyncMock(side_effect=PlexApiError("timeout"))
resp = auth_client.post("/plex/auth/pin")
assert resp.status_code == 502
def test_500_on_unexpected_error(self, auth_client, mock_repo):
mock_repo.create_oauth_pin = AsyncMock(side_effect=RuntimeError("bad"))
resp = auth_client.post("/plex/auth/pin")
assert resp.status_code == 500
class TestGetOrCreateSettingNoDeadlock:
def test_get_or_create_setting_does_not_deadlock(self, tmp_path):
import threading
from core.config import Settings
from services.preferences_service import PreferencesService
config_path = tmp_path / "config.json"
settings = Settings(
root_app_dir=tmp_path,
config_file_path=config_path,
cache_dir=tmp_path / "cache",
library_db_path=tmp_path / "cache" / "library.db",
queue_db_path=tmp_path / "cache" / "queue.db",
)
result = None
exc = None
def run():
nonlocal result, exc
try:
prefs = PreferencesService(settings)
result = prefs.get_or_create_setting("plex_client_id", lambda: "test-client-id")
result = (result, prefs.get_or_create_setting("plex_client_id", lambda: "other"))
except (OSError, ValueError, RuntimeError) as e:
exc = e
t = threading.Thread(target=run)
t.start()
t.join(timeout=5)
assert not t.is_alive(), "Deadlock detected: PreferencesService hung for 5s"
assert exc is None
assert result[0] == "test-client-id"
assert result[1] == "test-client-id"
class TestPollPlexPin:
def test_poll_pending(self, auth_client):
resp = auth_client.get("/plex/auth/poll?pin_id=12345")
assert resp.status_code == 200
data = resp.json()
assert data["completed"] is False
assert data["auth_token"] == ""
def test_poll_completed(self, auth_client, mock_repo):
mock_repo.poll_oauth_pin = AsyncMock(return_value="auth-token-abc")
resp = auth_client.get("/plex/auth/poll?pin_id=12345")
assert resp.status_code == 200
data = resp.json()
assert data["completed"] is True
assert data["auth_token"] == "auth-token-abc"
def test_poll_502_on_error(self, auth_client, mock_repo):
mock_repo.poll_oauth_pin = AsyncMock(side_effect=Exception("network error"))
resp = auth_client.get("/plex/auth/poll?pin_id=12345")
assert resp.status_code == 502