86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
# 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 |