"""Unit tests for security module""" import pytest from datetime import datetime, timedelta from jose import jwt from unittest.mock import MagicMock from app.core.security import ( create_access_token, create_refresh_token, verify_password, get_password_hash, ) from app.core.config import settings class TestPasswordHashing: """Tests for password hashing functions""" def test_hash_password(self): """Test password hashing""" password = "test_password_123" hashed = get_password_hash(password) assert hashed != password assert len(hashed) > 0 def test_verify_correct_password(self): """Test verification of correct password""" password = "test_password_123" hashed = get_password_hash(password) assert verify_password(password, hashed) is True def test_verify_incorrect_password(self): """Test verification of incorrect password""" password = "test_password_123" hashed = get_password_hash(password) assert verify_password("wrong_password", hashed) is False def test_hash_is_unique(self): """Test that hashes are unique for same password""" password = "test_password_123" hash1 = get_password_hash(password) hash2 = get_password_hash(password) assert hash1 != hash2 # bcrypt adds salt class TestTokenCreation: """Tests for token creation functions""" def test_create_access_token(self): """Test access token creation""" data = {"sub": "123", "username": "testuser"} token = create_access_token(data) assert token is not None assert len(token) > 0 payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) assert payload["sub"] == "123" assert payload["username"] == "testuser" assert payload["type"] == "access" def test_create_refresh_token(self): """Test refresh token creation""" data = {"sub": "123"} token = create_refresh_token(data) assert token is not None payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) assert payload["sub"] == "123" assert payload["type"] == "refresh" def test_access_token_expiration(self): """Test access token has correct expiration""" data = {"sub": "123"} token = create_access_token(data) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) exp_timestamp = payload["exp"] # Token should expire in approximately 15 minutes (accounting for timezone) expected_minutes = settings.ACCESS_TOKEN_EXPIRE_MINUTES # The timestamp is in seconds since epoch import time now_timestamp = time.time() minutes_diff = (exp_timestamp - now_timestamp) / 60 assert expected_minutes - 1 < minutes_diff < expected_minutes + 1 def test_refresh_token_expiration(self): """Test refresh token has correct expiration""" data = {"sub": "123"} token = create_refresh_token(data) payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) exp = datetime.fromtimestamp(payload["exp"]) now = datetime.utcnow() # Token should expire in approximately 7 days (with some tolerance) delta = exp - now assert delta.days >= 6 # At least 6 days assert delta.days <= 8 # Less than 8 days class TestJWTSecurity: """Tests for JWT security features""" def test_invalid_token_raises_error(self): """Test that invalid token raises JWTError""" with pytest.raises(jwt.JWTError): jwt.decode("invalid_token", settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) def test_token_with_wrong_secret_raises_error(self): """Test that token with wrong secret raises error""" data = {"sub": "123"} token = create_access_token(data) with pytest.raises(jwt.JWTError): jwt.decode(token, "wrong_secret", algorithms=[settings.ALGORITHM])