Files
taylanbakircioglu 6aae0f4309 Initial commit
2025-10-27 12:14:03 +03:00

318 lines
12 KiB
Plaintext

"""
Tests for authentication and authorization
"""
import pytest
from unittest.mock import AsyncMock, patch
from fastapi import HTTPException
import bcrypt
class TestAuthentication:
"""Test authentication functionality"""
@pytest.mark.asyncio
async def test_successful_login(self, mock_db_connection):
"""Test successful user login"""
# Mock user data
password = "testpassword"
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
user_data = {
"id": 1,
"username": "testuser",
"email": "test@example.com",
"password_hash": hashed_password.decode('utf-8'),
"is_active": True,
"role": "admin"
}
mock_db_connection.fetchrow.return_value = user_data
mock_db_connection.execute.return_value = None # last_login update
with patch('routers.auth.get_database_connection', return_value=mock_db_connection), \
patch('routers.auth.close_database_connection'), \
patch('routers.auth.log_user_activity'):
from routers.auth import login
from models.auth import LoginRequest
login_request = LoginRequest(username="testuser", password=password)
result = await login(login_request, None)
assert "access_token" in result
assert result["token_type"] == "bearer"
assert result["user"]["username"] == "testuser"
assert result["user"]["email"] == "test@example.com"
@pytest.mark.asyncio
async def test_login_user_not_found(self, mock_db_connection):
"""Test login with non-existent user"""
mock_db_connection.fetchrow.return_value = None # User not found
with patch('routers.auth.get_database_connection', return_value=mock_db_connection), \
patch('routers.auth.close_database_connection'):
from routers.auth import login
from models.auth import LoginRequest
login_request = LoginRequest(username="nonexistent", password="password")
with pytest.raises(HTTPException) as exc_info:
await login(login_request, None)
assert exc_info.value.status_code == 401
assert "Invalid username or password" in str(exc_info.value.detail)
@pytest.mark.asyncio
async def test_login_inactive_user(self, mock_db_connection):
"""Test login with inactive user"""
user_data = {
"id": 1,
"username": "testuser",
"is_active": False,
"password_hash": "dummy_hash"
}
mock_db_connection.fetchrow.return_value = user_data
with patch('routers.auth.get_database_connection', return_value=mock_db_connection), \
patch('routers.auth.close_database_connection'):
from routers.auth import login
from models.auth import LoginRequest
login_request = LoginRequest(username="testuser", password="password")
with pytest.raises(HTTPException) as exc_info:
await login(login_request, None)
assert exc_info.value.status_code == 401
assert "Account is deactivated" in str(exc_info.value.detail)
@pytest.mark.asyncio
async def test_login_wrong_password(self, mock_db_connection):
"""Test login with wrong password"""
# Create a hash for a different password
correct_password = "correctpassword"
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(correct_password.encode('utf-8'), salt)
user_data = {
"id": 1,
"username": "testuser",
"password_hash": hashed_password.decode('utf-8'),
"is_active": True
}
mock_db_connection.fetchrow.return_value = user_data
with patch('routers.auth.get_database_connection', return_value=mock_db_connection), \
patch('routers.auth.close_database_connection'):
from routers.auth import login
from models.auth import LoginRequest
login_request = LoginRequest(username="testuser", password="wrongpassword")
with pytest.raises(HTTPException) as exc_info:
await login(login_request, None)
assert exc_info.value.status_code == 401
assert "Invalid username or password" in str(exc_info.value.detail)
class TestAuthorization:
"""Test authorization and access control"""
@pytest.mark.asyncio
async def test_admin_user_cluster_access(self, mock_db_connection):
"""Test admin user gets access to all clusters"""
# Mock admin user
mock_db_connection.fetchval.side_effect = [
1, # Cluster exists
True # User is admin
]
with patch('routers.backend.get_database_connection', return_value=mock_db_connection):
from routers.backend import validate_user_cluster_access
# Should not raise exception for admin
result = await validate_user_cluster_access(1, 1, mock_db_connection)
assert result == True
@pytest.mark.asyncio
async def test_cluster_not_found(self, mock_db_connection):
"""Test access validation when cluster doesn't exist"""
mock_db_connection.fetchval.return_value = None # Cluster not found
with patch('routers.backend.get_database_connection', return_value=mock_db_connection):
from routers.backend import validate_user_cluster_access
with pytest.raises(HTTPException) as exc_info:
await validate_user_cluster_access(1, 999, mock_db_connection)
assert exc_info.value.status_code == 404
assert "Cluster not found" in str(exc_info.value.detail)
@pytest.mark.asyncio
async def test_regular_user_with_pool_access(self, mock_db_connection):
"""Test regular user with proper pool access"""
mock_db_connection.fetchval.side_effect = [
1, # Cluster exists
False, # User is not admin
True # user_pool_access table exists
]
# Mock user has access to the pool
mock_db_connection.fetchrow.return_value = {
"access_level": "full",
"id": 1,
"name": "test-cluster"
}
with patch('routers.backend.get_database_connection', return_value=mock_db_connection):
from routers.backend import validate_user_cluster_access
result = await validate_user_cluster_access(1, 1, mock_db_connection)
assert result == True
@pytest.mark.asyncio
async def test_regular_user_no_pool_access(self, mock_db_connection):
"""Test regular user without pool access"""
mock_db_connection.fetchval.side_effect = [
1, # Cluster exists
False, # User is not admin
True # user_pool_access table exists
]
# Mock user has no access to the pool
mock_db_connection.fetchrow.return_value = None
with patch('routers.backend.get_database_connection', return_value=mock_db_connection):
from routers.backend import validate_user_cluster_access
with pytest.raises(HTTPException) as exc_info:
await validate_user_cluster_access(1, 1, mock_db_connection)
assert exc_info.value.status_code == 403
assert "You don't have access to this cluster" in str(exc_info.value.detail)
@pytest.mark.asyncio
async def test_backward_compatibility_no_user_pool_table(self, mock_db_connection):
"""Test backward compatibility when user_pool_access table doesn't exist"""
mock_db_connection.fetchval.side_effect = [
1, # Cluster exists
False, # User is not admin
False # user_pool_access table doesn't exist
]
with patch('routers.backend.get_database_connection', return_value=mock_db_connection):
from routers.backend import validate_user_cluster_access
# Should allow access for backward compatibility
result = await validate_user_cluster_access(1, 1, mock_db_connection)
assert result == True
class TestTokenValidation:
"""Test JWT token validation"""
def test_get_current_user_from_valid_token(self):
"""Test extracting user from valid JWT token"""
# Mock JWT payload
mock_payload = {
"user_id": 1,
"username": "testuser",
"email": "test@example.com",
"role": "admin"
}
with patch('auth_middleware.jwt.decode', return_value=mock_payload):
from auth_middleware import get_current_user_from_token
# This would normally be async, but we're mocking the JWT decode
# In actual test, you'd need to handle the async nature properly
pass # Placeholder for actual token validation test
def test_get_current_user_from_invalid_token(self):
"""Test handling of invalid JWT token"""
from jose import JWTError
with patch('auth_middleware.jwt.decode', side_effect=JWTError("Invalid token")):
# Test would verify that invalid tokens are rejected
pass # Placeholder for actual invalid token test
def test_get_current_user_from_expired_token(self):
"""Test handling of expired JWT token"""
from jose import ExpiredSignatureError
with patch('auth_middleware.jwt.decode', side_effect=ExpiredSignatureError("Token expired")):
# Test would verify that expired tokens are rejected
pass # Placeholder for actual expired token test
class TestUserCreation:
"""Test user creation functionality"""
@pytest.mark.asyncio
async def test_create_user_success(self, mock_db_connection, mock_get_current_user):
"""Test successful user creation"""
mock_db_connection.fetchrow.side_effect = [None, None] # Username and email don't exist
mock_db_connection.fetchval.return_value = 2 # New user ID
with patch('routers.user.get_database_connection', return_value=mock_db_connection), \
patch('routers.user.close_database_connection'), \
patch('routers.user.get_current_user_from_token', return_value=mock_get_current_user), \
patch('routers.user.log_user_activity'):
from routers.user import create_user
from models.user import UserCreate
user_data = UserCreate(
username="newuser",
email="new@example.com",
password="password123",
full_name="New User",
role="user"
)
result = await create_user(user_data, "Bearer token")
assert "User created successfully" in result["message"]
assert result["user"]["username"] == "newuser"
@pytest.mark.asyncio
async def test_create_user_duplicate_username(self, mock_db_connection, mock_get_current_user):
"""Test user creation with duplicate username"""
mock_db_connection.fetchrow.return_value = {"id": 1} # Username exists
with patch('routers.user.get_database_connection', return_value=mock_db_connection), \
patch('routers.user.close_database_connection'), \
patch('routers.user.get_current_user_from_token', return_value=mock_get_current_user):
from routers.user import create_user
from models.user import UserCreate
user_data = UserCreate(
username="existinguser",
email="new@example.com",
password="password123"
)
with pytest.raises(HTTPException) as exc_info:
await create_user(user_data, "Bearer token")
assert exc_info.value.status_code == 400
assert "Username already exists" in str(exc_info.value.detail)