149 lines
5.4 KiB
Python
149 lines
5.4 KiB
Python
import unittest
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from fastapi import HTTPException
|
|
from fastapi.testclient import TestClient
|
|
|
|
from music_server.app import create_app
|
|
from music_server.auth import require_bearer_token
|
|
from music_server.services.token_service import TokenService
|
|
from music_server.settings import get_settings
|
|
|
|
|
|
class HealthRouteTests(unittest.TestCase):
|
|
def test_healthz_returns_ok(self):
|
|
client = TestClient(create_app())
|
|
response = client.get("/healthz")
|
|
|
|
self.assertEqual(200, response.status_code)
|
|
self.assertEqual({"status": "ok"}, response.json())
|
|
|
|
|
|
class SettingsAndAuthTests(unittest.TestCase):
|
|
def test_get_settings_reflects_runtime_env_changes(self):
|
|
with patch.dict(
|
|
"os.environ",
|
|
{
|
|
"PUBLIC_MUSIC_ACCESS_TOKEN": "token-one",
|
|
"CATALOG_DB_PATH": "./tmp/catalog-one.db",
|
|
"PLAYER_DB_PATH": "./tmp/player-one.db",
|
|
"MUSIC_SERVER_DISABLE_AUTH": "0",
|
|
},
|
|
clear=False,
|
|
):
|
|
first = get_settings()
|
|
|
|
with patch.dict(
|
|
"os.environ",
|
|
{
|
|
"PUBLIC_MUSIC_ACCESS_TOKEN": "token-two",
|
|
"CATALOG_DB_PATH": "./tmp/catalog-two.db",
|
|
"PLAYER_DB_PATH": "./tmp/player-two.db",
|
|
"MUSIC_SERVER_DISABLE_AUTH": "1",
|
|
},
|
|
clear=False,
|
|
):
|
|
second = get_settings()
|
|
|
|
self.assertEqual("token-one", first.access_token)
|
|
self.assertEqual("./tmp/catalog-one.db", first.catalog_db_path)
|
|
self.assertEqual("./tmp/player-one.db", first.player_db_path)
|
|
self.assertEqual("token-two", second.access_token)
|
|
self.assertEqual("./tmp/catalog-two.db", second.catalog_db_path)
|
|
self.assertEqual("./tmp/player-two.db", second.player_db_path)
|
|
self.assertFalse(first.disable_auth)
|
|
self.assertTrue(second.disable_auth)
|
|
|
|
def test_get_settings_reads_cache_relay_switch(self):
|
|
with patch.dict(
|
|
"os.environ",
|
|
{"MUSIC_SERVER_CACHE_RELAY_ENABLED": "0"},
|
|
clear=False,
|
|
):
|
|
disabled = get_settings()
|
|
|
|
with patch.dict(
|
|
"os.environ",
|
|
{"MUSIC_SERVER_CACHE_RELAY_ENABLED": "1"},
|
|
clear=False,
|
|
):
|
|
enabled = get_settings()
|
|
|
|
self.assertFalse(disabled.cache_relay_enabled)
|
|
self.assertTrue(enabled.cache_relay_enabled)
|
|
|
|
def test_require_bearer_token_accepts_case_insensitive_scheme(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
player_db_path = Path(tmpdir) / "player.db"
|
|
service = TokenService(str(player_db_path))
|
|
issued = service.issue_token(label="health-auth")
|
|
with patch.dict(
|
|
"os.environ",
|
|
{"PLAYER_DB_PATH": str(player_db_path)},
|
|
clear=False,
|
|
):
|
|
require_bearer_token(
|
|
authorization=f" bearer {issued.plaintext_token} ",
|
|
x_music_client_id="client-case",
|
|
x_music_client_label="Case Client",
|
|
)
|
|
|
|
def test_require_bearer_token_is_bypassed_when_auth_disabled(self):
|
|
with patch.dict(
|
|
"os.environ",
|
|
{"MUSIC_SERVER_DISABLE_AUTH": "1"},
|
|
clear=False,
|
|
):
|
|
require_bearer_token(
|
|
authorization=None,
|
|
x_music_client_id=None,
|
|
)
|
|
|
|
def test_require_bearer_token_raises_specific_error_codes(self):
|
|
with self.assertRaises(HTTPException) as missing:
|
|
require_bearer_token(
|
|
authorization=None,
|
|
x_music_client_id="client-a",
|
|
)
|
|
self.assertEqual(401, missing.exception.status_code)
|
|
self.assertEqual("authorization_missing", missing.exception.detail)
|
|
|
|
with self.assertRaises(HTTPException) as invalid:
|
|
require_bearer_token(
|
|
authorization="Basic abc",
|
|
x_music_client_id="client-a",
|
|
)
|
|
self.assertEqual(401, invalid.exception.status_code)
|
|
self.assertEqual("authorization_invalid", invalid.exception.detail)
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
player_db_path = Path(tmpdir) / "player.db"
|
|
service = TokenService(str(player_db_path))
|
|
issued = service.issue_token(label="health-auth")
|
|
with patch.dict(
|
|
"os.environ",
|
|
{"PLAYER_DB_PATH": str(player_db_path)},
|
|
clear=False,
|
|
):
|
|
with self.assertRaises(HTTPException) as missing_client_id:
|
|
require_bearer_token(
|
|
authorization=f"Bearer {issued.plaintext_token}",
|
|
x_music_client_id=None,
|
|
)
|
|
self.assertEqual(401, missing_client_id.exception.status_code)
|
|
self.assertEqual("client_id_missing", missing_client_id.exception.detail)
|
|
|
|
with self.assertRaises(HTTPException) as wrong:
|
|
require_bearer_token(
|
|
authorization="Bearer not-exists",
|
|
x_music_client_id="client-a",
|
|
)
|
|
self.assertEqual(401, wrong.exception.status_code)
|
|
self.assertEqual("token_not_found", wrong.exception.detail)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|