initial commit

This commit is contained in:
2025-10-19 22:09:35 +03:00
commit 6d593b4554
114 changed files with 23622 additions and 0 deletions

195
tests/test_user.py Normal file
View File

@ -0,0 +1,195 @@
"""Unit tests for user API endpoints."""
from unittest.mock import AsyncMock, Mock, patch
import pytest
from src.app.api.v1.users import erase_user, patch_user, read_user, read_users, write_user
from src.app.core.exceptions.http_exceptions import DuplicateValueException, ForbiddenException, NotFoundException
from src.app.schemas.user import UserCreate, UserRead, UserUpdate
class TestWriteUser:
"""Test user creation endpoint."""
@pytest.mark.asyncio
async def test_create_user_success(self, mock_db, sample_user_data, sample_user_read):
"""Test successful user creation."""
user_create = UserCreate(**sample_user_data)
with patch("src.app.api.v1.users.crud_users") as mock_crud:
# Mock that email and username don't exist
mock_crud.exists = AsyncMock(side_effect=[False, False]) # email, then username
mock_crud.create = AsyncMock(return_value=Mock(id=1))
mock_crud.get = AsyncMock(return_value=sample_user_read)
with patch("src.app.api.v1.users.get_password_hash") as mock_hash:
mock_hash.return_value = "hashed_password"
result = await write_user(Mock(), user_create, mock_db)
assert result == sample_user_read
mock_crud.exists.assert_any_call(db=mock_db, email=user_create.email)
mock_crud.exists.assert_any_call(db=mock_db, username=user_create.username)
mock_crud.create.assert_called_once()
@pytest.mark.asyncio
async def test_create_user_duplicate_email(self, mock_db, sample_user_data):
"""Test user creation with duplicate email."""
user_create = UserCreate(**sample_user_data)
with patch("src.app.api.v1.users.crud_users") as mock_crud:
# Mock that email already exists
mock_crud.exists = AsyncMock(return_value=True)
with pytest.raises(DuplicateValueException, match="Email is already registered"):
await write_user(Mock(), user_create, mock_db)
@pytest.mark.asyncio
async def test_create_user_duplicate_username(self, mock_db, sample_user_data):
"""Test user creation with duplicate username."""
user_create = UserCreate(**sample_user_data)
with patch("src.app.api.v1.users.crud_users") as mock_crud:
# Mock email doesn't exist, but username does
mock_crud.exists = AsyncMock(side_effect=[False, True])
with pytest.raises(DuplicateValueException, match="Username not available"):
await write_user(Mock(), user_create, mock_db)
class TestReadUser:
"""Test user retrieval endpoint."""
@pytest.mark.asyncio
async def test_read_user_success(self, mock_db, sample_user_read):
"""Test successful user retrieval."""
username = "test_user"
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=sample_user_read)
result = await read_user(Mock(), username, mock_db)
assert result == sample_user_read
mock_crud.get.assert_called_once_with(
db=mock_db, username=username, is_deleted=False, schema_to_select=UserRead
)
@pytest.mark.asyncio
async def test_read_user_not_found(self, mock_db):
"""Test user retrieval when user doesn't exist."""
username = "nonexistent_user"
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=None)
with pytest.raises(NotFoundException, match="User not found"):
await read_user(Mock(), username, mock_db)
class TestReadUsers:
"""Test users list endpoint."""
@pytest.mark.asyncio
async def test_read_users_success(self, mock_db):
"""Test successful users list retrieval."""
mock_users_data = {"data": [{"id": 1}, {"id": 2}], "count": 2}
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get_multi = AsyncMock(return_value=mock_users_data)
with patch("src.app.api.v1.users.paginated_response") as mock_paginated:
expected_response = {"data": [{"id": 1}, {"id": 2}], "pagination": {}}
mock_paginated.return_value = expected_response
result = await read_users(Mock(), mock_db, page=1, items_per_page=10)
assert result == expected_response
mock_crud.get_multi.assert_called_once()
mock_paginated.assert_called_once()
class TestPatchUser:
"""Test user update endpoint."""
@pytest.mark.asyncio
async def test_patch_user_success(self, mock_db, current_user_dict, sample_user_read):
"""Test successful user update."""
username = current_user_dict["username"]
user_update = UserUpdate(name="New Name")
user_dict = sample_user_read.model_dump()
user_dict["username"] = username
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=user_dict)
mock_crud.exists = AsyncMock(return_value=False)
mock_crud.update = AsyncMock(return_value=None)
result = await patch_user(Mock(), user_update, username, current_user_dict, mock_db)
assert result == {"message": "User updated"}
mock_crud.update.assert_called_once()
@pytest.mark.asyncio
async def test_patch_user_forbidden(self, mock_db, current_user_dict, sample_user_read):
"""Test user update when user tries to update another user."""
username = "different_user"
user_update = UserUpdate(name="New Name")
user_dict = sample_user_read.model_dump()
user_dict["username"] = username
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=user_dict)
with pytest.raises(ForbiddenException):
await patch_user(Mock(), user_update, username, current_user_dict, mock_db)
class TestEraseUser:
"""Test user deletion endpoint."""
@pytest.mark.asyncio
async def test_erase_user_success(self, mock_db, current_user_dict, sample_user_read):
"""Test successful user deletion."""
username = current_user_dict["username"]
sample_user_read.username = username
token = "mock_token"
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=sample_user_read)
mock_crud.delete = AsyncMock(return_value=None)
with patch("src.app.api.v1.users.blacklist_token", new_callable=AsyncMock) as mock_blacklist:
result = await erase_user(Mock(), username, current_user_dict, mock_db, token)
assert result == {"message": "User deleted"}
mock_crud.delete.assert_called_once_with(db=mock_db, username=username)
mock_blacklist.assert_called_once_with(token=token, db=mock_db)
@pytest.mark.asyncio
async def test_erase_user_not_found(self, mock_db, current_user_dict):
"""Test user deletion when user doesn't exist."""
username = "nonexistent_user"
token = "mock_token"
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=None)
with pytest.raises(NotFoundException, match="User not found"):
await erase_user(Mock(), username, current_user_dict, mock_db, token)
@pytest.mark.asyncio
async def test_erase_user_forbidden(self, mock_db, current_user_dict, sample_user_read):
"""Test user deletion when user tries to delete another user."""
username = "different_user"
sample_user_read.username = username
token = "mock_token"
with patch("src.app.api.v1.users.crud_users") as mock_crud:
mock_crud.get = AsyncMock(return_value=sample_user_read)
with pytest.raises(ForbiddenException):
await erase_user(Mock(), username, current_user_dict, mock_db, token)