"""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"])