youtube-summarizer/backend/tests/unit/test_auth_service.py

252 lines
9.0 KiB
Python

"""Unit tests for authentication service."""
import pytest
from datetime import datetime, timedelta
from unittest.mock import Mock, patch, MagicMock
import sys
from pathlib import Path
# Add parent directories to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
from backend.services.auth_service import AuthService
from backend.core.config import settings
class TestAuthService:
"""Test cases for AuthService."""
def test_hash_password(self):
"""Test password hashing."""
password = "TestPassword123!"
hashed = AuthService.hash_password(password)
assert hashed != password
assert len(hashed) > 20
assert hashed.startswith("$2b$") # bcrypt hash prefix
def test_verify_password(self):
"""Test password verification."""
password = "TestPassword123!"
hashed = AuthService.hash_password(password)
assert AuthService.verify_password(password, hashed) is True
assert AuthService.verify_password("WrongPassword", hashed) is False
def test_create_access_token(self):
"""Test access token creation."""
data = {"sub": "user123", "email": "test@example.com"}
token = AuthService.create_access_token(data)
assert token is not None
assert len(token) > 20
assert "." in token # JWT format check
def test_create_access_token_with_expiry(self):
"""Test access token with custom expiry."""
data = {"sub": "user123"}
expires_delta = timedelta(minutes=5)
token = AuthService.create_access_token(data, expires_delta)
assert token is not None
assert len(token) > 20
def test_decode_access_token(self):
"""Test access token decoding."""
data = {"sub": "user123", "email": "test@example.com"}
token = AuthService.create_access_token(data)
decoded = AuthService.decode_access_token(token)
assert decoded is not None
assert decoded["sub"] == "user123"
assert decoded["email"] == "test@example.com"
assert decoded["type"] == "access"
assert "exp" in decoded
def test_decode_invalid_token(self):
"""Test decoding invalid token."""
invalid_token = "invalid.token.here"
decoded = AuthService.decode_access_token(invalid_token)
assert decoded is None
def test_create_email_verification_token(self):
"""Test email verification token creation."""
user_id = "user123"
token = AuthService.create_email_verification_token(user_id)
assert token is not None
assert len(token) > 20
def test_verify_email_token(self):
"""Test email verification token validation."""
user_id = "user123"
token = AuthService.create_email_verification_token(user_id)
verified_id = AuthService.verify_email_token(token)
assert verified_id == user_id
def test_verify_invalid_email_token(self):
"""Test invalid email verification token."""
invalid_token = "invalid.token.here"
verified_id = AuthService.verify_email_token(invalid_token)
assert verified_id is None
def test_create_password_reset_token(self):
"""Test password reset token creation."""
user_id = "user123"
token = AuthService.create_password_reset_token(user_id)
assert token is not None
assert len(token) > 20
def test_verify_password_reset_token(self):
"""Test password reset token validation."""
user_id = "user123"
token = AuthService.create_password_reset_token(user_id)
verified_id = AuthService.verify_password_reset_token(token)
assert verified_id == user_id
def test_verify_invalid_password_reset_token(self):
"""Test invalid password reset token."""
invalid_token = "invalid.token.here"
verified_id = AuthService.verify_password_reset_token(invalid_token)
assert verified_id is None
@patch('backend.services.auth_service.Session')
def test_create_refresh_token(self, mock_session):
"""Test refresh token creation."""
mock_db = MagicMock()
user_id = "user123"
token = AuthService.create_refresh_token(user_id, mock_db)
assert token is not None
assert len(token) > 20
assert mock_db.add.called
assert mock_db.commit.called
@patch('backend.services.auth_service.Session')
def test_verify_refresh_token(self, mock_session):
"""Test refresh token verification."""
mock_db = MagicMock()
mock_token_obj = MagicMock()
mock_token_obj.revoked = False
mock_token_obj.expires_at = datetime.utcnow() + timedelta(days=1)
mock_db.query.return_value.filter.return_value.first.return_value = mock_token_obj
# Create a real token for testing
import secrets
test_token = secrets.token_urlsafe(32)
result = AuthService.verify_refresh_token(test_token, mock_db)
assert result == mock_token_obj
assert mock_db.query.called
@patch('backend.services.auth_service.Session')
def test_revoke_refresh_token(self, mock_session):
"""Test refresh token revocation."""
mock_db = MagicMock()
mock_token_obj = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = mock_token_obj
import secrets
test_token = secrets.token_urlsafe(32)
result = AuthService.revoke_refresh_token(test_token, mock_db)
assert result is True
assert mock_token_obj.revoked is True
assert mock_db.commit.called
@patch('backend.services.auth_service.Session')
def test_revoke_all_user_tokens(self, mock_session):
"""Test revoking all user refresh tokens."""
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.update.return_value = 3
user_id = "user123"
count = AuthService.revoke_all_user_tokens(user_id, mock_db)
assert count == 3
assert mock_db.commit.called
@patch('backend.services.auth_service.Session')
def test_authenticate_user_success(self, mock_session):
"""Test successful user authentication."""
mock_db = MagicMock()
mock_user = MagicMock()
mock_user.password_hash = AuthService.hash_password("correct_password")
mock_user.last_login = None
mock_db.query.return_value.filter.return_value.first.return_value = mock_user
result = AuthService.authenticate_user("test@example.com", "correct_password", mock_db)
assert result == mock_user
assert mock_user.last_login is not None
assert mock_db.commit.called
@patch('backend.services.auth_service.Session')
def test_authenticate_user_wrong_password(self, mock_session):
"""Test authentication with wrong password."""
mock_db = MagicMock()
mock_user = MagicMock()
mock_user.password_hash = AuthService.hash_password("correct_password")
mock_db.query.return_value.filter.return_value.first.return_value = mock_user
result = AuthService.authenticate_user("test@example.com", "wrong_password", mock_db)
assert result is None
@patch('backend.services.auth_service.Session')
def test_authenticate_user_not_found(self, mock_session):
"""Test authentication with non-existent user."""
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.first.return_value = None
result = AuthService.authenticate_user("nonexistent@example.com", "password", mock_db)
assert result is None
@patch('backend.services.auth_service.Session')
def test_get_current_user_valid(self, mock_session):
"""Test getting current user with valid token."""
mock_db = MagicMock()
mock_user = MagicMock()
mock_user.is_active = True
user_id = "user123"
data = {"sub": user_id}
token = AuthService.create_access_token(data)
mock_db.query.return_value.filter.return_value.first.return_value = mock_user
result = AuthService.get_current_user(token, mock_db)
assert result == mock_user
assert mock_db.query.called
@patch('backend.services.auth_service.Session')
def test_get_current_user_invalid_token(self, mock_session):
"""Test getting current user with invalid token."""
mock_db = MagicMock()
invalid_token = "invalid.token.here"
result = AuthService.get_current_user(invalid_token, mock_db)
assert result is None
if __name__ == "__main__":
pytest.main([__file__, "-v"])