feat(test): add unittest for role management, add coverage report for role management

This commit is contained in:
icecheng 2025-07-25 16:20:50 +08:00
parent 30e6ca72a7
commit 69d1007ddf
23 changed files with 873 additions and 1 deletions

View File

View File

@ -1,7 +1,6 @@
import jwt
import pytest
from common.config.app_settings import app_settings
from tests.api_tests.siginin import config
from tests.base.authentication_web import AuthenticationWeb

View File

@ -0,0 +1,28 @@
# Test Coverage Report (backend modules only)
---
## Coverage Table
| File Name | Statements | Missed | Coverage |
|------------------------------------------------------------------|------------|--------|----------|
| backend/infra/permission/permission_handler.py | 55 | 0 | 100% |
| backend/infra/permission/role_handler.py | 71 | 0 | 100% |
| backend/infra/permission/user_role_handler.py | 39 | 7 | 82% |
| backend/services/permission/permission_service.py | 20 | 0 | 100% |
| backend/services/permission/role_service.py | 24 | 0 | 100% |
| backend/services/user/user_management_service.py | 39 | 0 | 100% |
---
## Summary
This test report only includes the test coverage of functions related to role management.
See the integration tests:
- tests/api_tests/permission/README.md
- tests/api_tests/role/README.md
- tests/api_tests/user/README.md
## TODO
Add tests for the previous functions.

View File

@ -0,0 +1,137 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from fastapi.exceptions import RequestValidationError
from backend.infra.permission.permission_handler import PermissionHandler
from backend.models.permission.models import PermissionDoc, RoleDoc
from beanie import PydanticObjectId
@pytest.fixture(autouse=True)
def mock_db():
with patch('backend.infra.permission.permission_handler.PermissionDoc') as MockPermissionDoc, \
patch('backend.infra.permission.permission_handler.RoleDoc') as MockRoleDoc:
yield MockPermissionDoc, MockRoleDoc
@pytest.mark.asyncio
class TestPermissionHandler:
@pytest.fixture(autouse=True)
def setup(self, mock_db):
self.MockPermissionDoc, self.MockRoleDoc = mock_db
self.handler = PermissionHandler()
async def test_create_permission_success(self):
# Test creating a permission successfully
self.MockPermissionDoc.find_one = AsyncMock(side_effect=[None, None])
mock_doc = MagicMock(spec=PermissionDoc)
self.MockPermissionDoc.return_value = mock_doc
mock_doc.insert = AsyncMock()
result = await self.handler.create_permission('key', 'name', 'desc')
assert result == mock_doc
mock_doc.insert.assert_awaited_once()
async def test_create_permission_missing_key_or_name(self):
# Test missing permission_key or permission_name raises validation error
with pytest.raises(RequestValidationError):
await self.handler.create_permission('', 'name', 'desc')
with pytest.raises(RequestValidationError):
await self.handler.create_permission('key', '', 'desc')
async def test_create_permission_duplicate(self):
# Test duplicate permission_key or permission_name raises validation error
self.MockPermissionDoc.find_one = AsyncMock(side_effect=[MagicMock(), None])
with pytest.raises(RequestValidationError):
await self.handler.create_permission('key', 'name', 'desc')
self.MockPermissionDoc.find_one = AsyncMock(side_effect=[None, MagicMock()])
with pytest.raises(RequestValidationError):
await self.handler.create_permission('key', 'name', 'desc')
async def test_update_permission_success(self):
# Test updating a permission successfully
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.is_default = False
self.MockPermissionDoc.get = AsyncMock(return_value=mock_doc)
self.MockPermissionDoc.find_one = AsyncMock(return_value=None)
mock_doc.save = AsyncMock()
result = await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc')
assert result == mock_doc
mock_doc.save.assert_awaited_once()
async def test_update_permission_missing_args(self):
# Test missing permission_id, permission_key or permission_name raises validation error
with pytest.raises(RequestValidationError):
await self.handler.update_permission(None, 'key', 'name', 'desc')
with pytest.raises(RequestValidationError):
await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), '', 'name', 'desc')
with pytest.raises(RequestValidationError):
await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', '', 'desc')
async def test_update_permission_not_found(self):
# Test updating a non-existent permission raises validation error
self.MockPermissionDoc.get = AsyncMock(return_value=None)
with pytest.raises(RequestValidationError):
await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc')
async def test_update_permission_is_default(self):
# Test updating a default permission raises validation error
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.is_default = True
self.MockPermissionDoc.get = AsyncMock(return_value=mock_doc)
with pytest.raises(RequestValidationError):
await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc')
async def test_update_permission_conflict(self):
# Test updating a permission with duplicate key or name raises validation error
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.is_default = False
self.MockPermissionDoc.get = AsyncMock(return_value=mock_doc)
self.MockPermissionDoc.find_one = AsyncMock(return_value=MagicMock())
with pytest.raises(RequestValidationError):
await self.handler.update_permission(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc')
async def test_query_permissions_success(self):
# Test querying permissions returns docs and total
mock_cursor = MagicMock()
mock_cursor.count = AsyncMock(return_value=2)
mock_cursor.skip.return_value = mock_cursor
mock_cursor.limit.return_value = mock_cursor
mock_cursor.to_list = AsyncMock(return_value=['doc1', 'doc2'])
self.MockPermissionDoc.find.return_value = mock_cursor
docs, total = await self.handler.query_permissions('key', 'name', 0, 10)
assert docs == ['doc1', 'doc2']
assert total == 2
async def test_delete_permission_success(self):
# Test deleting a permission successfully
self.MockRoleDoc.find_one = AsyncMock(return_value=None)
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.is_default = False
self.MockPermissionDoc.get = AsyncMock(return_value=mock_doc)
mock_doc.delete = AsyncMock()
await self.handler.delete_permission(PydanticObjectId('507f1f77bcf86cd799439011'))
mock_doc.delete.assert_awaited_once()
async def test_delete_permission_missing_id(self):
# Test missing permission_id raises validation error
with pytest.raises(RequestValidationError):
await self.handler.delete_permission(None)
async def test_delete_permission_referenced_by_role(self):
# Test deleting a permission referenced by a role raises validation error
self.MockRoleDoc.find_one = AsyncMock(return_value=MagicMock())
with pytest.raises(RequestValidationError):
await self.handler.delete_permission(PydanticObjectId('507f1f77bcf86cd799439011'))
async def test_delete_permission_not_found(self):
# Test deleting a non-existent permission raises validation error
self.MockRoleDoc.find_one = AsyncMock(return_value=None)
self.MockPermissionDoc.get = AsyncMock(return_value=None)
with pytest.raises(RequestValidationError):
await self.handler.delete_permission(PydanticObjectId('507f1f77bcf86cd799439011'))
async def test_delete_permission_is_default(self):
# Test deleting a default permission raises validation error
self.MockRoleDoc.find_one = AsyncMock(return_value=None)
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.is_default = True
self.MockPermissionDoc.get = AsyncMock(return_value=mock_doc)
with pytest.raises(RequestValidationError):
await self.handler.delete_permission(PydanticObjectId('507f1f77bcf86cd799439011'))

View File

@ -0,0 +1,169 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from fastapi.exceptions import RequestValidationError
from backend.infra.permission.role_handler import RoleHandler
from backend.models.permission.models import RoleDoc, PermissionDoc, UserRoleDoc
from beanie import PydanticObjectId
@pytest.fixture(autouse=True)
def mock_db():
with patch('backend.infra.permission.role_handler.RoleDoc') as MockRoleDoc, \
patch('backend.infra.permission.role_handler.PermissionDoc') as MockPermissionDoc, \
patch('backend.infra.permission.role_handler.UserRoleDoc') as MockUserRoleDoc:
yield MockRoleDoc, MockPermissionDoc, MockUserRoleDoc
@pytest.mark.asyncio
class TestRoleHandler:
@pytest.fixture(autouse=True)
def setup(self, mock_db):
self.MockRoleDoc, self.MockPermissionDoc, self.MockUserRoleDoc = mock_db
self.handler = RoleHandler()
async def test_create_role_success(self):
# Test creating a role successfully
self.MockRoleDoc.find_one = AsyncMock(side_effect=[None, None])
mock_doc = MagicMock(spec=RoleDoc)
self.MockRoleDoc.return_value = mock_doc
mock_doc.insert = AsyncMock()
result = await self.handler.create_role('key', 'name', 'desc', 1)
assert result == mock_doc
mock_doc.insert.assert_awaited_once()
async def test_create_role_missing_key_or_name(self):
# Test missing role_key or role_name raises validation error
with pytest.raises(RequestValidationError):
await self.handler.create_role('', 'name', 'desc', 1)
with pytest.raises(RequestValidationError):
await self.handler.create_role('key', '', 'desc', 1)
async def test_create_role_duplicate(self):
# Test duplicate role_key or role_name raises validation error
self.MockRoleDoc.find_one = AsyncMock(side_effect=[MagicMock(), None])
with pytest.raises(RequestValidationError):
await self.handler.create_role('key', 'name', 'desc', 1)
self.MockRoleDoc.find_one = AsyncMock(side_effect=[None, MagicMock()])
with pytest.raises(RequestValidationError):
await self.handler.create_role('key', 'name', 'desc', 1)
async def test_update_role_success(self):
# Test updating a role successfully
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.is_default = False
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
self.MockRoleDoc.find_one = AsyncMock(return_value=None)
mock_doc.save = AsyncMock()
result = await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc', 1)
assert result == mock_doc
mock_doc.save.assert_awaited_once()
async def test_update_role_missing_args(self):
# Test missing role_id, role_key or role_name raises validation error
with pytest.raises(RequestValidationError):
await self.handler.update_role(None, 'key', 'name', 'desc', 1)
with pytest.raises(RequestValidationError):
await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), '', 'name', 'desc', 1)
with pytest.raises(RequestValidationError):
await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', '', 'desc', 1)
async def test_update_role_not_found(self):
# Test updating a non-existent role raises validation error
self.MockRoleDoc.get = AsyncMock(return_value=None)
with pytest.raises(RequestValidationError):
await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc', 1)
async def test_update_role_is_default(self):
# Test updating a default role raises validation error
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.is_default = True
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
with pytest.raises(RequestValidationError):
await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc', 1)
async def test_update_role_conflict(self):
# Test updating a role with duplicate key or name raises validation error
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.is_default = False
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
self.MockRoleDoc.find_one = AsyncMock(return_value=MagicMock())
with pytest.raises(RequestValidationError):
await self.handler.update_role(PydanticObjectId('507f1f77bcf86cd799439011'), 'key', 'name', 'desc', 1)
async def test_query_roles_success(self):
# Test querying roles returns docs and total
mock_cursor = MagicMock()
mock_cursor.count = AsyncMock(return_value=2)
mock_cursor.skip.return_value = mock_cursor
mock_cursor.limit.return_value = mock_cursor
mock_cursor.to_list = AsyncMock(return_value=['doc1', 'doc2'])
self.MockRoleDoc.find.return_value = mock_cursor
docs, total = await self.handler.query_roles('key', 'name', 0, 10)
assert docs == ['doc1', 'doc2']
assert total == 2
async def test_assign_permissions_to_role_success(self):
# Test assigning permissions to a role successfully
mock_doc = MagicMock(spec=RoleDoc)
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
self.MockPermissionDoc.get = AsyncMock(return_value=MagicMock())
mock_doc.save = AsyncMock()
result = await self.handler.assign_permissions_to_role(PydanticObjectId('507f1f77bcf86cd799439011'), ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439011'])
assert result == mock_doc
mock_doc.save.assert_awaited_once()
async def test_assign_permissions_to_role_missing_args(self):
# Test missing role_id or permission_ids raises validation error
with pytest.raises(RequestValidationError):
await self.handler.assign_permissions_to_role(None, ['pid1'])
with pytest.raises(RequestValidationError):
await self.handler.assign_permissions_to_role(PydanticObjectId('507f1f77bcf86cd799439011'), None)
async def test_assign_permissions_to_role_role_not_found(self):
# Test assigning permissions to a non-existent role raises validation error
self.MockRoleDoc.get = AsyncMock(return_value=None)
with pytest.raises(RequestValidationError):
await self.handler.assign_permissions_to_role(PydanticObjectId('507f1f77bcf86cd799439011'), ['507f1f77bcf86cd799439011'])
async def test_assign_permissions_to_role_permission_not_found(self):
# Test assigning a non-existent permission raises validation error
mock_doc = MagicMock(spec=RoleDoc)
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
self.MockPermissionDoc.get = AsyncMock(side_effect=[None, MagicMock()])
with pytest.raises(RequestValidationError):
await self.handler.assign_permissions_to_role(PydanticObjectId('507f1f77bcf86cd799439011'), ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439011'])
async def test_delete_role_success(self):
# Test deleting a role successfully
self.MockUserRoleDoc.find_one = AsyncMock(return_value=None)
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.is_default = False
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
mock_doc.delete = AsyncMock()
await self.handler.delete_role(PydanticObjectId('507f1f77bcf86cd799439011'))
mock_doc.delete.assert_awaited_once()
async def test_delete_role_missing_id(self):
# Test missing role_id raises validation error
with pytest.raises(RequestValidationError):
await self.handler.delete_role(None)
async def test_delete_role_referenced_by_user(self):
# Test deleting a role referenced by a user raises validation error
self.MockUserRoleDoc.find_one = AsyncMock(return_value=MagicMock())
with pytest.raises(RequestValidationError):
await self.handler.delete_role(PydanticObjectId('507f1f77bcf86cd799439011'))
async def test_delete_role_not_found(self):
# Test deleting a non-existent role raises validation error
self.MockUserRoleDoc.find_one = AsyncMock(return_value=None)
self.MockRoleDoc.get = AsyncMock(return_value=None)
with pytest.raises(RequestValidationError):
await self.handler.delete_role(PydanticObjectId('507f1f77bcf86cd799439011'))
async def test_delete_role_is_default(self):
# Test deleting a default role raises validation error
self.MockUserRoleDoc.find_one = AsyncMock(return_value=None)
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.is_default = True
self.MockRoleDoc.get = AsyncMock(return_value=mock_doc)
with pytest.raises(RequestValidationError):
await self.handler.delete_role(PydanticObjectId('507f1f77bcf86cd799439011'))

View File

@ -0,0 +1,43 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from backend.infra.permission.user_role_handler import UserRoleHandler
from backend.models.permission.models import RoleDoc, UserRoleDoc, PermissionDoc
@pytest.fixture(autouse=True)
def mock_db():
with patch('backend.infra.permission.user_role_handler.RoleDoc') as MockRoleDoc, \
patch('backend.infra.permission.user_role_handler.UserRoleDoc') as MockUserRoleDoc, \
patch('backend.infra.permission.user_role_handler.PermissionDoc') as MockPermissionDoc:
yield MockRoleDoc, MockUserRoleDoc, MockPermissionDoc
@pytest.mark.asyncio
class TestUserRoleHandler:
@pytest.fixture(autouse=True)
def setup(self, mock_db):
self.MockRoleDoc, self.MockUserRoleDoc, self.MockPermissionDoc = mock_db
self.handler = UserRoleHandler()
async def test_assign_roles_to_user_success(self):
# Test assigning roles to a user when no UserRoleDoc exists (create new)
self.MockRoleDoc.get = AsyncMock(return_value=MagicMock())
self.MockUserRoleDoc.find_one = AsyncMock(return_value=None)
mock_user_role = MagicMock(spec=UserRoleDoc)
self.MockUserRoleDoc.return_value = mock_user_role
mock_user_role.insert = AsyncMock()
result = await self.handler.assign_roles_to_user('507f1f77bcf86cd799439011', ['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439012'])
assert result == mock_user_role
mock_user_role.insert.assert_awaited_once()
async def test_get_role_and_permission_by_user_id_with_roles_and_permissions(self):
# Test getting roles and permissions when user has roles and permissions
self.MockUserRoleDoc.find_one = AsyncMock(return_value=MagicMock(role_ids=['507f1f77bcf86cd799439010', '507f1f77bcf86cd799439017']))
mock_role1 = MagicMock(role_name='role1', permission_ids=['507f1f77bcf86cd799439011', '507f1f77bcf86cd799439012'])
mock_role2 = MagicMock(role_name='role2', permission_ids=['507f1f77bcf86cd799439014', '507f1f77bcf86cd799439013'])
self.MockRoleDoc.find.return_value.to_list = AsyncMock(return_value=[mock_role1, mock_role2])
mock_perm1 = MagicMock(permission_key='perm1')
mock_perm2 = MagicMock(permission_key='perm2')
mock_perm3 = MagicMock(permission_key='perm3')
self.MockPermissionDoc.find.return_value.to_list = AsyncMock(return_value=[mock_perm1, mock_perm2, mock_perm3])
result = await self.handler.get_role_and_permission_by_user_id('uid')
assert set(result[0]) == {'role1', 'role2'}
assert set(result[1]) == {'perm1', 'perm2', 'perm3'}

View File

@ -0,0 +1,152 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from fastapi.exceptions import RequestValidationError
from backend.services.permission.permission_service import PermissionService
from backend.models.permission.models import PermissionDoc
import datetime
@pytest.fixture
def mock_permission_handler():
# Fixture to patch PermissionHandler for isolation and mocking
with patch('backend.services.permission.permission_service.PermissionHandler') as MockHandler:
yield MockHandler
@pytest.mark.asyncio
class TestPermissionService:
@pytest.fixture(autouse=True)
def setup(self, mock_permission_handler):
# Automatically set up a PermissionService with a mocked handler for each test
self.mock_handler = mock_permission_handler.return_value
self.service = PermissionService()
self.service.permission_handler = self.mock_handler
async def test_create_permission_success(self):
# Test creating a permission successfully returns the expected PermissionDoc
mock_doc = MagicMock(spec=PermissionDoc)
self.mock_handler.create_permission = AsyncMock(return_value=mock_doc)
result = await self.service.create_permission('key', 'name', 'desc')
assert result == mock_doc
self.mock_handler.create_permission.assert_awaited_once_with('key', 'name', 'desc')
async def test_create_permission_fail(self):
# Test that an exception is raised if the handler fails to create a permission
self.mock_handler.create_permission = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.create_permission('key', 'name', 'desc')
async def test_create_permission_with_none_description(self):
# Test creating a permission with None as description
mock_doc = MagicMock(spec=PermissionDoc)
self.mock_handler.create_permission = AsyncMock(return_value=mock_doc)
result = await self.service.create_permission('key', 'name', None)
assert result == mock_doc
self.mock_handler.create_permission.assert_awaited_once_with('key', 'name', None)
async def test_create_permission_with_empty_description(self):
# Test creating a permission with empty string as description
mock_doc = MagicMock(spec=PermissionDoc)
self.mock_handler.create_permission = AsyncMock(return_value=mock_doc)
result = await self.service.create_permission('key', 'name', '')
assert result == mock_doc
self.mock_handler.create_permission.assert_awaited_once_with('key', 'name', '')
async def test_create_permission_handler_unexpected_exception(self):
# Test handler raises unexpected exception during creation
self.mock_handler.create_permission = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.create_permission('key', 'name', 'desc')
async def test_update_permission_success(self):
# Test updating a permission successfully returns the expected PermissionDoc
mock_doc = MagicMock(spec=PermissionDoc)
self.mock_handler.update_permission = AsyncMock(return_value=mock_doc)
result = await self.service.update_permission('507f1f77bcf86cd799439011', 'key', 'name', 'desc')
assert result == mock_doc
self.mock_handler.update_permission.assert_awaited_once()
async def test_update_permission_fail(self):
# Test that an exception is raised if the handler fails to update a permission
self.mock_handler.update_permission = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.update_permission('507f1f77bcf86cd799439011', 'key', 'name', 'desc')
async def test_update_permission_with_invalid_id(self):
# Test updating a permission with invalid id (empty string)
self.mock_handler.update_permission = AsyncMock(side_effect=RequestValidationError('invalid id'))
with pytest.raises(RequestValidationError):
await self.service.update_permission('507f1f77bcf86cd799439011', 'key', 'name', 'desc')
async def test_update_permission_handler_unexpected_exception(self):
# Test handler raises unexpected exception during update
self.mock_handler.update_permission = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.update_permission('507f1f77bcf86cd799439011', 'key', 'name', 'desc')
async def test_update_permission_partial_args(self):
# Test updating a permission with only key provided
mock_doc = MagicMock(spec=PermissionDoc)
self.mock_handler.update_permission = AsyncMock(return_value=mock_doc)
result = await self.service.update_permission('507f1f77bcf86cd799439011', 'key', None, None)
assert result == mock_doc
self.mock_handler.update_permission.assert_awaited_once()
async def test_query_permissions_success(self):
# Test querying permissions returns a paginated result with correct items and meta
mock_doc = MagicMock(spec=PermissionDoc)
mock_doc.dict.return_value = {'permission_key': 'key', 'permission_name': 'name'}
self.mock_handler.query_permissions = AsyncMock(return_value=([mock_doc], 1))
result = await self.service.query_permissions('key', 'name', 1, 10)
assert result['items'] == [{'permission_key': 'key', 'permission_name': 'name'}]
assert result['total'] == 1
assert result['page'] == 1
assert result['page_size'] == 10
self.mock_handler.query_permissions.assert_awaited_once_with('key', 'name', 0, 10)
async def test_query_permissions_handler_exception(self):
# Test handler raises exception during query
self.mock_handler.query_permissions = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.query_permissions('key', 'name', 1, 10)
async def test_query_permissions_empty_result(self):
# Test query returns empty list
self.mock_handler.query_permissions = AsyncMock(return_value=([], 0))
result = await self.service.query_permissions('key', 'name', 1, 10)
assert result['items'] == []
assert result['total'] == 0
assert result['page'] == 1
assert result['page_size'] == 10
async def test_query_permissions_with_none_and_special_chars(self):
# Test query with None and special characters
self.mock_handler.query_permissions = AsyncMock(return_value=([], 0))
result = await self.service.query_permissions(None, None, 1, 10)
assert result['items'] == []
result2 = await self.service.query_permissions('!@#$', '', 1, 10)
assert result2['items'] == []
@pytest.mark.parametrize('page,page_size', [(0, 10), (1, 0), (0, 0), (-1, 10), (1, -1)])
async def test_query_permissions_invalid_page(self, page, page_size):
# Test that invalid page or page_size raises a validation error
with pytest.raises(RequestValidationError):
await self.service.query_permissions('key', 'name', page, page_size)
async def test_delete_permission_success(self):
# Test deleting a permission successfully returns None
self.mock_handler.delete_permission = AsyncMock(return_value=None)
result = await self.service.delete_permission('507f1f77bcf86cd799439011')
assert result is None
self.mock_handler.delete_permission.assert_awaited_once()
async def test_delete_permission_fail(self):
# Test that an exception is raised if the handler fails to delete a permission
self.mock_handler.delete_permission = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.delete_permission('507f1f77bcf86cd799439011')
async def test_delete_permission_handler_unexpected_exception(self):
# Test handler raises unexpected exception during delete
self.mock_handler.delete_permission = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.delete_permission('507f1f77bcf86cd799439011')

View File

@ -0,0 +1,172 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from fastapi.exceptions import RequestValidationError
from backend.services.permission.role_service import RoleService
from backend.models.permission.models import RoleDoc
@pytest.fixture
def mock_role_handler():
# Fixture to patch RoleHandler for isolation and mocking
with patch('backend.services.permission.role_service.RoleHandler') as MockHandler:
yield MockHandler
@pytest.mark.asyncio
class TestRoleService:
@pytest.fixture(autouse=True)
def setup(self, mock_role_handler):
# Automatically set up a RoleService with a mocked handler for each test
self.mock_handler = mock_role_handler.return_value
self.service = RoleService()
self.service.role_handler = self.mock_handler
async def test_create_role_success(self):
# Test creating a role successfully returns the expected RoleDoc
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.create_role = AsyncMock(return_value=mock_doc)
result = await self.service.create_role('key', 'name', 'desc', 1)
assert result == mock_doc
self.mock_handler.create_role.assert_awaited_once_with('key', 'name', 'desc', 1)
async def test_create_role_fail(self):
# Test that an exception is raised if the handler fails to create a role
self.mock_handler.create_role = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.create_role('key', 'name', 'desc', 1)
async def test_create_role_with_none_description(self):
# Test creating a role with None as description
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.create_role = AsyncMock(return_value=mock_doc)
result = await self.service.create_role('key', 'name', None, 1)
assert result == mock_doc
self.mock_handler.create_role.assert_awaited_once_with('key', 'name', None, 1)
async def test_create_role_with_empty_description(self):
# Test creating a role with empty string as description
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.create_role = AsyncMock(return_value=mock_doc)
result = await self.service.create_role('key', 'name', '', 1)
assert result == mock_doc
self.mock_handler.create_role.assert_awaited_once_with('key', 'name', '', 1)
async def test_create_role_handler_unexpected_exception(self):
# Test handler raises unexpected exception during creation
self.mock_handler.create_role = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.create_role('key', 'name', 'desc', 1)
async def test_update_role_success(self):
# Test updating a role successfully returns the expected RoleDoc
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.update_role = AsyncMock(return_value=mock_doc)
result = await self.service.update_role('507f1f77bcf86cd799439011', 'key', 'name', 'desc', 1)
assert result == mock_doc
self.mock_handler.update_role.assert_awaited_once()
async def test_update_role_fail(self):
# Test that an exception is raised if the handler fails to update a role
self.mock_handler.update_role = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.update_role('507f1f77bcf86cd799439011', 'key', 'name', 'desc', 1)
async def test_update_role_handler_unexpected_exception(self):
# Test handler raises unexpected exception during update
self.mock_handler.update_role = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.update_role('507f1f77bcf86cd799439011', 'key', 'name', 'desc', 1)
async def test_update_role_partial_args(self):
# Test updating a role with only key provided
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.update_role = AsyncMock(return_value=mock_doc)
result = await self.service.update_role('507f1f77bcf86cd799439011', 'key', None, None, 1)
assert result == mock_doc
self.mock_handler.update_role.assert_awaited_once()
async def test_query_roles_success(self):
# Test querying roles returns a paginated result with correct items and meta
mock_doc = MagicMock(spec=RoleDoc)
mock_doc.dict.return_value = {'role_key': 'key', 'role_name': 'name'}
self.mock_handler.query_roles = AsyncMock(return_value=([mock_doc], 1))
result = await self.service.query_roles('key', 'name', 1, 10)
assert result['items'] == [{'role_key': 'key', 'role_name': 'name'}]
assert result['total'] == 1
assert result['page'] == 1
assert result['page_size'] == 10
self.mock_handler.query_roles.assert_awaited_once_with('key', 'name', 0, 10)
async def test_query_roles_handler_exception(self):
# Test handler raises exception during query
self.mock_handler.query_roles = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.query_roles('key', 'name', 1, 10)
async def test_query_roles_empty_result(self):
# Test query returns empty list
self.mock_handler.query_roles = AsyncMock(return_value=([], 0))
result = await self.service.query_roles('key', 'name', 1, 10)
assert result['items'] == []
assert result['total'] == 0
assert result['page'] == 1
assert result['page_size'] == 10
async def test_query_roles_with_none_and_special_chars(self):
# Test query with None and special characters
self.mock_handler.query_roles = AsyncMock(return_value=([], 0))
result = await self.service.query_roles(None, None, 1, 10)
assert result['items'] == []
result2 = await self.service.query_roles('!@#$', '', 1, 10)
assert result2['items'] == []
@pytest.mark.parametrize('page,page_size', [(0, 10), (1, 0), (0, 0), (-1, 10), (1, -1)])
async def test_query_roles_invalid_page(self, page, page_size):
# Test that invalid page or page_size raises a validation error
with pytest.raises(RequestValidationError):
await self.service.query_roles('key', 'name', page, page_size)
async def test_assign_permissions_to_role_success(self):
# Test assigning permissions to a role returns the updated RoleDoc
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.assign_permissions_to_role = AsyncMock(return_value=mock_doc)
result = await self.service.assign_permissions_to_role('507f1f77bcf86cd799439011', ['pid1', 'pid2'])
assert result == mock_doc
self.mock_handler.assign_permissions_to_role.assert_awaited_once()
async def test_assign_permissions_to_role_fail(self):
# Test that an exception is raised if the handler fails to assign permissions
self.mock_handler.assign_permissions_to_role = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.assign_permissions_to_role('507f1f77bcf86cd799439011', ['pid1', 'pid2'])
async def test_assign_permissions_to_role_empty_list(self):
# Test assigning permissions to a role with empty permission_ids list
mock_doc = MagicMock(spec=RoleDoc)
self.mock_handler.assign_permissions_to_role = AsyncMock(return_value=mock_doc)
result = await self.service.assign_permissions_to_role('507f1f77bcf86cd799439011', [])
assert result == mock_doc
self.mock_handler.assign_permissions_to_role.assert_awaited_once()
async def test_assign_permissions_to_role_handler_unexpected_exception(self):
# Test handler raises unexpected exception during assign
self.mock_handler.assign_permissions_to_role = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.assign_permissions_to_role('507f1f77bcf86cd799439011', ['pid1'])
async def test_delete_role_success(self):
# Test deleting a role successfully returns None
self.mock_handler.delete_role = AsyncMock(return_value=None)
result = await self.service.delete_role('507f1f77bcf86cd799439011')
assert result is None
self.mock_handler.delete_role.assert_awaited_once()
async def test_delete_role_fail(self):
# Test that an exception is raised if the handler fails to delete a role
self.mock_handler.delete_role = AsyncMock(side_effect=RequestValidationError('error'))
with pytest.raises(RequestValidationError):
await self.service.delete_role('507f1f77bcf86cd799439011')
async def test_delete_role_handler_unexpected_exception(self):
# Test handler raises unexpected exception during delete
self.mock_handler.delete_role = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.delete_role('507f1f77bcf86cd799439011')

View File

@ -0,0 +1,172 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from backend.services.user.user_management_service import UserManagementService
from backend.models.user.models import UserAccountDoc
from backend.models.permission.models import UserRoleDoc
from backend.models.user.constants import NewUserMethod
from common.constants.region import UserRegion
@pytest.fixture
def mock_handlers():
with patch('backend.services.user.user_management_service.UserProfileHandler') as MockProfileHandler, \
patch('backend.services.user.user_management_service.UserAuthHandler') as MockAuthHandler, \
patch('backend.services.user.user_management_service.UserRoleHandler') as MockRoleHandler:
yield MockProfileHandler, MockAuthHandler, MockRoleHandler
@pytest.mark.asyncio
class TestUserManagementService:
@pytest.fixture(autouse=True)
def setup(self, mock_handlers):
# Automatically set up a UserManagementService with mocked handlers for each test
MockProfileHandler, MockAuthHandler, MockRoleHandler = mock_handlers
self.mock_profile_handler = MockProfileHandler.return_value
self.mock_auth_handler = MockAuthHandler.return_value
self.mock_role_handler = MockRoleHandler.return_value
self.service = UserManagementService()
self.service.user_profile_handler = self.mock_profile_handler
self.service.user_auth_handler = self.mock_auth_handler
self.service.user_role_handler = self.mock_role_handler
async def test_create_new_user_account_email(self):
# Test creating a new user account with EMAIL method
mock_account = MagicMock(spec=UserAccountDoc)
self.mock_profile_handler.create_new_user_account = AsyncMock(return_value=mock_account)
result = await self.service.create_new_user_account(NewUserMethod.EMAIL, UserRegion.ZH_CN)
assert result == mock_account
self.mock_profile_handler.create_new_user_account.assert_awaited_once()
async def test_create_new_user_account_mobile(self):
# Test creating a new user account with MOBILE method
mock_account = MagicMock(spec=UserAccountDoc)
self.mock_profile_handler.create_new_user_account = AsyncMock(return_value=mock_account)
result = await self.service.create_new_user_account(NewUserMethod.MOBILE, UserRegion.ZH_CN)
assert result == mock_account
self.mock_profile_handler.create_new_user_account.assert_awaited_once()
async def test_create_new_user_account_handler_exception(self):
# Test handler exception is propagated when creating a new user account
self.mock_profile_handler.create_new_user_account = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.create_new_user_account(NewUserMethod.EMAIL, UserRegion.ZH_CN)
async def test_initialize_new_user_data_email(self):
# Test initializing new user data with EMAIL method
self.mock_profile_handler.create_basic_profile = AsyncMock()
self.mock_auth_handler.save_email_auth_method = AsyncMock()
self.mock_profile_handler.create_provider_profile = AsyncMock()
result = await self.service.initialize_new_user_data('uid', NewUserMethod.EMAIL, email_address='a@b.com')
assert result is True
self.mock_profile_handler.create_basic_profile.assert_awaited_once()
self.mock_auth_handler.save_email_auth_method.assert_awaited_once()
self.mock_profile_handler.create_provider_profile.assert_awaited_once()
async def test_initialize_new_user_data_mobile(self):
# Test initializing new user data with MOBILE method
self.mock_profile_handler.create_basic_profile = AsyncMock()
self.mock_profile_handler.create_provider_profile = AsyncMock()
result = await self.service.initialize_new_user_data('uid', NewUserMethod.MOBILE, mobile_number='123456')
assert result is True
self.mock_profile_handler.create_basic_profile.assert_awaited_once()
self.mock_profile_handler.create_provider_profile.assert_awaited_once()
async def test_initialize_new_user_data_other(self):
# Test initializing new user data with unsupported method returns False
result = await self.service.initialize_new_user_data('uid', 'OTHER')
assert result is False
async def test_initialize_new_user_data_email_handler_exception(self):
# Test exception in create_basic_profile is propagated
self.mock_profile_handler.create_basic_profile = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.initialize_new_user_data('uid', NewUserMethod.EMAIL, email_address='a@b.com')
async def test_initialize_new_user_data_mobile_handler_exception(self):
# Test exception in create_basic_profile for MOBILE is propagated
self.mock_profile_handler.create_basic_profile = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.initialize_new_user_data('uid', NewUserMethod.MOBILE, mobile_number='123456')
async def test_initialize_new_user_data_email_missing_email(self):
# Test initializing new user data with EMAIL method but missing email_address
self.mock_profile_handler.create_basic_profile = AsyncMock()
self.mock_auth_handler.save_email_auth_method = AsyncMock()
self.mock_profile_handler.create_provider_profile = AsyncMock()
# Should still call with None, but may not be valid in real logic
result = await self.service.initialize_new_user_data('uid', NewUserMethod.EMAIL)
assert result is True
self.mock_profile_handler.create_basic_profile.assert_awaited_once()
self.mock_auth_handler.save_email_auth_method.assert_awaited_once()
self.mock_profile_handler.create_provider_profile.assert_awaited_once()
async def test_initialize_new_user_data_mobile_missing_mobile(self):
# Test initializing new user data with MOBILE method but missing mobile_number
self.mock_profile_handler.create_basic_profile = AsyncMock()
self.mock_profile_handler.create_provider_profile = AsyncMock()
result = await self.service.initialize_new_user_data('uid', NewUserMethod.MOBILE)
assert result is True
self.mock_profile_handler.create_basic_profile.assert_awaited_once()
self.mock_profile_handler.create_provider_profile.assert_awaited_once()
async def test_initialize_new_user_data_provider_profile_exception(self):
# Test exception in create_provider_profile is propagated
self.mock_profile_handler.create_basic_profile = AsyncMock()
self.mock_auth_handler.save_email_auth_method = AsyncMock()
self.mock_profile_handler.create_provider_profile = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.initialize_new_user_data('uid', NewUserMethod.EMAIL, email_address='a@b.com')
async def test_get_account_by_id(self):
# Test getting account by user id
mock_account = MagicMock(spec=UserAccountDoc)
self.mock_profile_handler.get_account_by_id = AsyncMock(return_value=mock_account)
result = await self.service.get_account_by_id('uid')
assert result == mock_account
self.mock_profile_handler.get_account_by_id.assert_awaited_once_with('uid')
async def test_get_account_by_id_none(self):
# Test getting account by user id returns None if not found
self.mock_profile_handler.get_account_by_id = AsyncMock(return_value=None)
result = await self.service.get_account_by_id('uid')
assert result is None
self.mock_profile_handler.get_account_by_id.assert_awaited_once_with('uid')
async def test_get_account_by_id_exception(self):
# Test exception in get_account_by_id is propagated
self.mock_profile_handler.get_account_by_id = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.get_account_by_id('uid')
async def test_assign_roles_to_user(self):
# Test assigning roles to user
mock_user_role = MagicMock(spec=UserRoleDoc)
self.mock_role_handler.assign_roles_to_user = AsyncMock(return_value=mock_user_role)
result = await self.service.assign_roles_to_user('uid', ['rid1', 'rid2'])
assert result == mock_user_role
self.mock_role_handler.assign_roles_to_user.assert_awaited_once_with('uid', ['rid1', 'rid2'])
async def test_assign_roles_to_user_handler_exception(self):
# Test exception in assign_roles_to_user is propagated
self.mock_role_handler.assign_roles_to_user = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.assign_roles_to_user('uid', ['rid1', 'rid2'])
async def test_get_role_and_permission_by_user_id(self):
# Test getting role and permission by user id
self.mock_role_handler.get_role_and_permission_by_user_id = AsyncMock(return_value=(['role1'], ['perm1']))
result = await self.service.get_role_and_permission_by_user_id('uid')
assert result == (['role1'], ['perm1'])
self.mock_role_handler.get_role_and_permission_by_user_id.assert_awaited_once_with('uid')
async def test_get_role_and_permission_by_user_id_empty(self):
# Test getting role and permission by user id returns empty lists if no roles/permissions
self.mock_role_handler.get_role_and_permission_by_user_id = AsyncMock(return_value=([], []))
result = await self.service.get_role_and_permission_by_user_id('uid')
assert result == ([], [])
self.mock_role_handler.get_role_and_permission_by_user_id.assert_awaited_once_with('uid')
async def test_get_role_and_permission_by_user_id_exception(self):
# Test exception in get_role_and_permission_by_user_id is propagated
self.mock_role_handler.get_role_and_permission_by_user_id = AsyncMock(side_effect=Exception('db error'))
with pytest.raises(Exception):
await self.service.get_role_and_permission_by_user_id('uid')

View File