Files
reactbin/api/tests/unit/test_jwt_auth.py
agatha 355014f975 Feat: Add tag browser page at /tags with count-sorted tag list and library deep-link
- Extends GET /api/v1/tags with sort=count_desc and min_count query params
- New TagsComponent at /tags (public, no auth guard) shows all tags sorted by image count
- Clicking a tag navigates to /?tags=<name> for a pre-filtered library view
- LibraryComponent reads ?tags= query param on init to support deep-linking from tag browser
- Library header gains a "Browse tags" link to /tags for discoverability
- All 15 TDD tasks complete; ruff, ng lint, and ng build clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 18:40:06 +00:00

97 lines
2.8 KiB
Python

import jwt as pyjwt
import pytest
from fastapi import HTTPException
from app.auth.jwt_provider import JWTAuthProvider
SECRET = "test-secret-key"
USERNAME = "owner"
PASSWORD = "hunter2"
def make_provider(**kwargs) -> JWTAuthProvider:
defaults = dict(
secret_key=SECRET,
expiry_seconds=3600,
owner_username=USERNAME,
owner_password=PASSWORD,
)
return JWTAuthProvider(**{**defaults, **kwargs})
def test_create_token_is_valid_jwt():
provider = make_provider()
token = provider.create_token()
payload = pyjwt.decode(token, SECRET, algorithms=["HS256"])
assert payload["sub"] == "owner"
assert "iat" in payload
assert "exp" in payload
@pytest.mark.asyncio
async def test_get_identity_returns_owner():
provider = make_provider()
token = provider.create_token()
identity = await provider.get_identity(f"Bearer {token}")
assert identity.id == "owner"
assert identity.anonymous is False
@pytest.mark.asyncio
async def test_get_identity_raises_on_expired_token():
provider = make_provider(expiry_seconds=-1)
token = provider.create_token()
with pytest.raises(HTTPException) as exc_info:
await provider.get_identity(f"Bearer {token}")
assert exc_info.value.status_code == 401
@pytest.mark.asyncio
async def test_get_identity_raises_on_wrong_key():
provider = make_provider()
other = make_provider(secret_key="different-secret")
token = other.create_token()
with pytest.raises(HTTPException) as exc_info:
await provider.get_identity(f"Bearer {token}")
assert exc_info.value.status_code == 401
@pytest.mark.asyncio
async def test_get_identity_raises_on_garbage():
provider = make_provider()
with pytest.raises(HTTPException) as exc_info:
await provider.get_identity("Bearer not.a.real.token")
assert exc_info.value.status_code == 401
@pytest.mark.asyncio
async def test_get_identity_raises_on_missing_header():
provider = make_provider()
with pytest.raises(HTTPException) as exc_info:
await provider.get_identity(None)
assert exc_info.value.status_code == 401
@pytest.mark.asyncio
async def test_get_identity_raises_on_missing_bearer_prefix():
provider = make_provider()
token = provider.create_token()
with pytest.raises(HTTPException) as exc_info:
await provider.get_identity(token)
assert exc_info.value.status_code == 401
def test_verify_credentials_true():
provider = make_provider()
assert provider.verify_credentials(USERNAME, PASSWORD) is True
def test_verify_credentials_false_wrong_password():
provider = make_provider()
assert provider.verify_credentials(USERNAME, "wrongpassword") is False
def test_verify_credentials_false_wrong_username():
provider = make_provider()
assert provider.verify_credentials("notowner", PASSWORD) is False