from datetime import datetime, timedelta, timezone import pytest from jose import jwt from typing import Literal from fooder.exc import Unauthorized from fooder.utils.jwt import AccessToken, RefreshToken, Token PAST = datetime(2000, 1, 1, tzinfo=timezone.utc) class WrongKeyToken(Token): secret_key = "wrong-secret" expire_delta = timedelta(minutes=30) class TestAccessToken: def test_encode_decode_roundtrip(self): now = datetime.now(timezone.utc) token = AccessToken(exp=AccessToken.calculate_exp(now), sub=42) decoded = AccessToken.decode(token.encode()) assert decoded.sub == token.sub def test_calculate_exp(self): now = datetime.now(timezone.utc) assert AccessToken.calculate_exp(now) > now def test_decode_wrong_key_raises(self): now = datetime.now(timezone.utc) token = WrongKeyToken(exp=WrongKeyToken.calculate_exp(now), sub=1) with pytest.raises(Unauthorized): AccessToken.decode(token.encode()) def test_decode_expired_raises(self): token = AccessToken(exp=PAST, sub=1) with pytest.raises(Unauthorized): AccessToken.decode(token.encode()) def test_encoded_fields(self): now = datetime.now(timezone.utc) token = AccessToken(exp=AccessToken.calculate_exp(now), sub=42) payload = jwt.decode(token.encode(), "", options={"verify_signature": False}) assert "secret_key" not in payload assert "expire_delta" not in payload class TestRefreshToken: def test_encode_decode_roundtrip(self): now = datetime.now(timezone.utc) token = RefreshToken(exp=RefreshToken.calculate_exp(now), sub=7) decoded = RefreshToken.decode(token.encode()) assert decoded.sub == token.sub def test_calculate_exp(self): now = datetime.now(timezone.utc) assert RefreshToken.calculate_exp(now) > now def test_refresh_token_not_decodable_as_access_token(self): now = datetime.now(timezone.utc) token = RefreshToken(exp=RefreshToken.calculate_exp(now), sub=1) with pytest.raises(Unauthorized): AccessToken.decode(token.encode()) def test_access_token_not_decodable_as_refresh_token(self): now = datetime.now(timezone.utc) token = AccessToken(exp=AccessToken.calculate_exp(now), sub=1) with pytest.raises(Unauthorized): RefreshToken.decode(token.encode()) def test_encoded_fields(self): now = datetime.now(timezone.utc) token = AccessToken(exp=RefreshToken.calculate_exp(now), sub=42) payload = jwt.decode(token.encode(), "", options={"verify_signature": False}) assert "secret_key" not in payload assert "expire_delta" not in payload