"""Suite E — Role separation (SuperAdmin vs Author).""" import sys from unittest.mock import MagicMock import pytest from fastapi import HTTPException # totp.py imports qrcode at module level — stub for unit tests sys.modules.setdefault("qrcode", MagicMock()) from app.dependencies import ( get_current_author, get_current_author_scoped, get_current_superadmin, ) from app.models.user import User def _make_user(role: str, user_id: str = "author-1") -> User: return User( id=user_id, email="test@example.com", password_hash="x", role=role, is_active=True, ) @pytest.mark.asyncio async def test_get_current_author_rejects_superadmin(monkeypatch): user = _make_user("superadmin") async def fake_auth(authorization, db): return user monkeypatch.setattr("app.dependencies._get_authenticated_user", fake_auth) with pytest.raises(HTTPException) as exc: await get_current_author(authorization="Bearer x", db=None) # type: ignore[arg-type] assert exc.value.status_code == 403 assert "superadmin" in exc.value.detail.lower() @pytest.mark.asyncio async def test_get_current_author_accepts_author(monkeypatch): user = _make_user("author") async def fake_auth(authorization, db): return user monkeypatch.setattr("app.dependencies._get_authenticated_user", fake_auth) result = await get_current_author(authorization="Bearer x", db=None) # type: ignore[arg-type] assert result.role == "author" @pytest.mark.asyncio async def test_author_slug_must_match_user_id(): user = _make_user("author", "author-abc") with pytest.raises(HTTPException) as exc: await get_current_author_scoped(author_slug="other-author", current_user=user) assert exc.value.status_code == 403 @pytest.mark.asyncio async def test_superadmin_dependency_rejects_author(): user = _make_user("author") with pytest.raises(HTTPException) as exc: await get_current_superadmin(current_user=user) assert exc.value.status_code == 403 @pytest.mark.asyncio async def test_superadmin_dependency_accepts_superadmin(monkeypatch): user = _make_user("superadmin") user.totp_secret = "JBSWY3DPEHPK3PXP" def fake_verify(secret, code): return code == "123456" monkeypatch.setattr("app.core.access.totp.verify_totp", fake_verify) result = await get_current_superadmin(current_user=user, x_totp_code="123456") assert result.role == "superadmin" @pytest.mark.asyncio async def test_superadmin_dependency_requires_totp(): user = _make_user("superadmin") user.totp_secret = "JBSWY3DPEHPK3PXP" with pytest.raises(HTTPException) as exc: await get_current_superadmin(current_user=user, x_totp_code="") assert exc.value.status_code == 403