"""dependencies.auth 단위 테스트. 실행: cd src/backend && python -m pytest tests/test_auth_dependency.py -v """ from __future__ import annotations import time from uuid import UUID import jwt import pytest from fastapi import HTTPException from starlette.requests import Request from config import get_settings from dependencies.auth import ( ANONYMOUS_USER, get_current_user, get_current_user_optional, get_user_from_ws_token, is_anonymous, ) TEST_SECRET = "test-secret-for-hs256-verification-000000" TEST_USER_ID = "11111111-2222-3333-4444-555555555555" def _request() -> Request: """최소 스코프로 만든 더미 Request. get_current_user 는 request 를 session_tracker.record 로만 넘기고, record 는 헤더에서 IP 를 못 찾으면 즉시 반환하므로 빈 헤더면 부작용 없음. """ return Request( { "type": "http", "method": "GET", "path": "/", "headers": [], "query_string": b"", "client": None, } ) def _token(payload: dict | None = None, secret: str = TEST_SECRET) -> str: base = { "sub": TEST_USER_ID, "email": "alice@example.com", "aud": "authenticated", "exp": int(time.time()) + 3600, "iat": int(time.time()), } if payload: base.update(payload) return jwt.encode(base, secret, algorithm="HS256") @pytest.fixture(autouse=True) def _enable_auth(monkeypatch): """테스트 중 auth_enabled=True, jwt secret 주입 후 Settings 캐시 리셋.""" get_settings.cache_clear() monkeypatch.setenv("AUTH_ENABLED", "true") monkeypatch.setenv("SUPABASE_JWT_SECRET", TEST_SECRET) yield get_settings.cache_clear() # --- auth_enabled=True ------------------------------------------------------ @pytest.mark.asyncio async def test_happy_path(): user = await get_current_user(_request(), authorization=f"Bearer {_token()}") assert user.id == UUID(TEST_USER_ID) assert user.email == "alice@example.com" assert user.tier == "free" @pytest.mark.asyncio async def test_missing_header_raises_401(): with pytest.raises(HTTPException) as exc: await get_current_user(_request(), authorization=None) assert exc.value.status_code == 401 @pytest.mark.asyncio async def test_malformed_bearer_raises_401(): with pytest.raises(HTTPException) as exc: await get_current_user(_request(), authorization="NotBearer xyz") assert exc.value.status_code == 401 @pytest.mark.asyncio async def test_invalid_signature_raises_401(): bad = _token(secret="different-secret") with pytest.raises(HTTPException) as exc: await get_current_user(_request(), authorization=f"Bearer {bad}") assert exc.value.status_code == 401 @pytest.mark.asyncio async def test_expired_token_raises_401(): expired = _token({"exp": int(time.time()) - 10}) with pytest.raises(HTTPException) as exc: await get_current_user(_request(), authorization=f"Bearer {expired}") assert exc.value.status_code == 401 @pytest.mark.asyncio async def test_tier_from_app_metadata(): t = _token({"app_metadata": {"tier": "premium"}}) user = await get_current_user(_request(), authorization=f"Bearer {t}") assert user.tier == "premium" # --- optional ------------------------------------------------------------- @pytest.mark.asyncio async def test_optional_returns_anon_when_missing(): user = await get_current_user_optional(_request(), authorization=None) assert is_anonymous(user) @pytest.mark.asyncio async def test_optional_validates_when_present(): user = await get_current_user_optional(_request(), authorization=f"Bearer {_token()}") assert not is_anonymous(user) assert user.id == UUID(TEST_USER_ID) # --- WS token ------------------------------------------------------------- @pytest.mark.asyncio async def test_ws_token_happy_path(): user = await get_user_from_ws_token(token=_token()) assert user.id == UUID(TEST_USER_ID) @pytest.mark.asyncio async def test_ws_token_missing_raises_401(): with pytest.raises(HTTPException) as exc: await get_user_from_ws_token(token=None) assert exc.value.status_code == 401 # --- auth_enabled=False --------------------------------------------------- @pytest.mark.asyncio async def test_disabled_returns_anon(monkeypatch): monkeypatch.setenv("AUTH_ENABLED", "false") get_settings.cache_clear() user = await get_current_user(_request(), authorization=None) assert is_anonymous(user) user = await get_current_user_optional(_request(), authorization=f"Bearer garbage") assert is_anonymous(user) user = await get_user_from_ws_token(token=None) assert is_anonymous(user) assert ANONYMOUS_USER.tier == "free"