"""Tests for token scope hierarchy + back-compat with legacy bare-string tokens.""" from __future__ import annotations import pytest from media_server.config import Settings, TokenSpec def test_bare_string_token_promotes_to_admin_scope(): """Legacy `label: ` form must still work and grant admin.""" s = Settings(api_tokens={"legacy": "deadbeef-deadbeef-deadbeef-deadbeef"}) spec = s.api_tokens["legacy"] assert isinstance(spec, TokenSpec) assert spec.token == "deadbeef-deadbeef-deadbeef-deadbeef" assert spec.scopes == ["admin"] assert spec.grants("admin") assert spec.grants("control") assert spec.grants("read") def test_dict_token_with_explicit_scopes(): s = Settings(api_tokens={ "ha": {"token": "aaaaaaaaaaaaaaaa", "scopes": ["read", "control"]}, }) spec = s.api_tokens["ha"] assert spec.grants("control") assert spec.grants("read") assert not spec.grants("admin") def test_read_only_scope_grants_only_read(): spec = TokenSpec(token="xxxxxxxxxxxxxxxx", scopes=["read"]) assert spec.grants("read") assert not spec.grants("control") assert not spec.grants("admin") def test_admin_scope_implies_control_and_read(): spec = TokenSpec(token="xxxxxxxxxxxxxxxx", scopes=["admin"]) assert spec.grants("read") assert spec.grants("control") assert spec.grants("admin") def test_unknown_scope_rejected(): with pytest.raises(ValueError, match="unknown scopes"): TokenSpec(token="xxxxxxxxxxxxxxxx", scopes=["root"]) def test_empty_scopes_rejected(): with pytest.raises(ValueError, match="at least one"): TokenSpec(token="xxxxxxxxxxxxxxxx", scopes=[]) def test_short_token_rejected(): with pytest.raises(ValueError): TokenSpec(token="short", scopes=["read"])