# Example: be/tests/core/test_security.py import pytest from datetime import timedelta from jose import jwt, JWTError import time from app.core.security import ( hash_password, verify_password, create_access_token, verify_access_token, ) from app.config import settings # Import settings for testing JWT config # --- Password Hashing Tests --- def test_hash_password_returns_string(): password = "testpassword" hashed = hash_password(password) assert isinstance(hashed, str) assert password != hashed # Ensure it's not plain text def test_verify_password_correct(): password = "correct_password" hashed = hash_password(password) assert verify_password(password, hashed) is True def test_verify_password_incorrect(): hashed = hash_password("correct_password") assert verify_password("wrong_password", hashed) is False def test_verify_password_invalid_hash_format(): # Passlib's verify handles many format errors gracefully assert verify_password("any_password", "invalid_hash_string") is False # --- JWT Tests --- def test_create_access_token(): subject = "testuser@example.com" token = create_access_token(subject=subject) assert isinstance(token, str) # Decode manually for basic check (verification done in verify_access_token tests) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) assert payload["sub"] == subject assert "exp" in payload assert isinstance(payload["exp"], int) def test_verify_access_token_valid(): subject = "test_subject_valid" token = create_access_token(subject=subject) payload = verify_access_token(token) assert payload is not None assert payload["sub"] == subject def test_verify_access_token_invalid_signature(): subject = "test_subject_invalid_sig" token = create_access_token(subject=subject) # Attempt to verify with a wrong key wrong_key = settings.SECRET_KEY + "wrong" with pytest.raises(JWTError): # Decoding with wrong key should raise JWTError internally jwt.decode(token, wrong_key, algorithms=[settings.ALGORITHM]) # Our verify function should catch this and return None assert verify_access_token(token + "tamper") is None # Tampering token often invalidates sig # Note: Testing verify_access_token directly returning None for wrong key is tricky # as the error happens *during* jwt.decode. We rely on it catching JWTError. def test_verify_access_token_expired(): # Create a token that expires almost immediately subject = "test_subject_expired" expires_delta = timedelta(seconds=-1) # Expired 1 second ago token = create_access_token(subject=subject, expires_delta=expires_delta) # Wait briefly just in case of timing issues, though negative delta should guarantee expiry time.sleep(0.1) # Decoding expired token raises ExpiredSignatureError internally with pytest.raises(JWTError): # Specifically ExpiredSignatureError, but JWTError catches it jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) # Our verify function should catch this and return None assert verify_access_token(token) is None def test_verify_access_token_malformed(): assert verify_access_token("this.is.not.a.valid.token") is None