247 lines
8.7 KiB
Python
247 lines
8.7 KiB
Python
import uuid
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from domains.groups.models import GroupMemberRole, GroupType
|
|
from domains.groups import service
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_user_group_creates_private_group_and_owner_membership(monkeypatch):
|
|
created_group = None
|
|
added_membership = None
|
|
|
|
async def fake_create_group(db, group):
|
|
nonlocal created_group
|
|
created_group = group
|
|
group.id = uuid.uuid4()
|
|
return group
|
|
|
|
async def fake_add_group_member(db, membership):
|
|
nonlocal added_membership
|
|
added_membership = membership
|
|
return membership
|
|
|
|
monkeypatch.setattr(service, "create_group", fake_create_group)
|
|
monkeypatch.setattr(service, "add_group_member", fake_add_group_member)
|
|
|
|
owner_id = uuid.uuid4()
|
|
group = await service.create_user_group(object(), "team-alpha", owner_id)
|
|
|
|
assert group.type == GroupType.PRIVATE
|
|
assert group.owner_id == owner_id
|
|
assert created_group is group
|
|
assert added_membership is not None
|
|
assert added_membership.user_id == owner_id
|
|
assert added_membership.role == GroupMemberRole.MANAGER
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_admin_group_creates_public_group_without_membership(monkeypatch):
|
|
create_group_mock = AsyncMock(side_effect=lambda db, group: setattr(group, "id", uuid.uuid4()) or group)
|
|
add_group_member_mock = AsyncMock()
|
|
|
|
monkeypatch.setattr(service, "create_group", create_group_mock)
|
|
monkeypatch.setattr(service, "add_group_member", add_group_member_mock)
|
|
|
|
owner_id = uuid.uuid4()
|
|
group = await service.create_admin_group(object(), "ops-room", owner_id)
|
|
|
|
assert group.type == GroupType.PUBLIC
|
|
assert group.owner_id == owner_id
|
|
add_group_member_mock.assert_not_awaited()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_group_members_api_returns_only_real_members(monkeypatch):
|
|
group_id = uuid.uuid4()
|
|
member_id = uuid.uuid4()
|
|
members_data = [
|
|
(
|
|
SimpleNamespace(
|
|
user_id=member_id,
|
|
role=GroupMemberRole.MEMBER,
|
|
),
|
|
"neda-user",
|
|
)
|
|
]
|
|
|
|
monkeypatch.setattr(
|
|
service,
|
|
"get_group_members_with_details",
|
|
AsyncMock(return_value=members_data),
|
|
)
|
|
monkeypatch.setattr(
|
|
service,
|
|
"list_online_users",
|
|
AsyncMock(return_value=[str(member_id)]),
|
|
)
|
|
|
|
response = await service.list_group_members_api(object(), group_id)
|
|
|
|
assert response == [
|
|
{
|
|
"user_id": member_id,
|
|
"username": "neda-user",
|
|
"role": GroupMemberRole.MEMBER,
|
|
"is_online": True,
|
|
}
|
|
]
|
|
|
|
|
|
def test_group_routes_are_namespaced():
|
|
from main import app
|
|
|
|
route_map = {
|
|
(route.path, method)
|
|
for route in app.routes
|
|
if getattr(route, "methods", None)
|
|
for method in route.methods
|
|
}
|
|
|
|
assert ("/groups/", "POST") in route_map
|
|
assert ("/groups/{group_id}/members/{user_id}", "DELETE") in route_map
|
|
assert ("/admin/groups/", "POST") in route_map
|
|
assert ("/admin/groups/", "GET") in route_map
|
|
assert ("/admin/groups/{group_id}/members", "GET") in route_map
|
|
assert ("/admin/groups/{group_id}/members", "POST") in route_map
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_allows_manager_to_remove_member(monkeypatch):
|
|
db = object()
|
|
group_id = uuid.uuid4()
|
|
manager_id = uuid.uuid4()
|
|
member_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=uuid.uuid4())
|
|
removed = AsyncMock()
|
|
|
|
async def fake_get_group_member(db, requested_group_id, requested_user_id):
|
|
if requested_user_id == manager_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MANAGER)
|
|
if requested_user_id == member_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MEMBER)
|
|
return None
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(service, "get_group_member", fake_get_group_member)
|
|
monkeypatch.setattr(service, "delete_group_member", removed)
|
|
|
|
requester = SimpleNamespace(id=manager_id, is_admin=False)
|
|
await service.remove_member_from_group(db, group_id, member_id, requester)
|
|
|
|
removed.assert_awaited_once_with(db, group_id, member_id)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_rejects_non_manager(monkeypatch):
|
|
group_id = uuid.uuid4()
|
|
requester_id = uuid.uuid4()
|
|
member_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=uuid.uuid4())
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(
|
|
service,
|
|
"get_group_member",
|
|
AsyncMock(return_value=SimpleNamespace(role=GroupMemberRole.MEMBER)),
|
|
)
|
|
monkeypatch.setattr(service, "delete_group_member", AsyncMock())
|
|
|
|
requester = SimpleNamespace(id=requester_id, is_admin=False)
|
|
|
|
with pytest.raises(PermissionError, match="دسترسی لازم را ندارید"):
|
|
await service.remove_member_from_group(object(), group_id, member_id, requester)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_rejects_owner_removal(monkeypatch):
|
|
group_id = uuid.uuid4()
|
|
manager_id = uuid.uuid4()
|
|
owner_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=owner_id)
|
|
|
|
async def fake_get_group_member(db, requested_group_id, requested_user_id):
|
|
if requested_user_id == manager_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MANAGER)
|
|
return SimpleNamespace(role=GroupMemberRole.MEMBER)
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(service, "get_group_member", fake_get_group_member)
|
|
monkeypatch.setattr(service, "delete_group_member", AsyncMock())
|
|
|
|
requester = SimpleNamespace(id=manager_id, is_admin=False)
|
|
|
|
with pytest.raises(ValueError, match="حذف سازنده گروه مجاز نیست"):
|
|
await service.remove_member_from_group(object(), group_id, owner_id, requester)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_rejects_missing_target_membership(monkeypatch):
|
|
group_id = uuid.uuid4()
|
|
manager_id = uuid.uuid4()
|
|
member_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=uuid.uuid4())
|
|
|
|
async def fake_get_group_member(db, requested_group_id, requested_user_id):
|
|
if requested_user_id == manager_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MANAGER)
|
|
return None
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(service, "get_group_member", fake_get_group_member)
|
|
monkeypatch.setattr(service, "delete_group_member", AsyncMock())
|
|
|
|
requester = SimpleNamespace(id=manager_id, is_admin=False)
|
|
|
|
with pytest.raises(ValueError, match="کاربر عضو این گروه نیست"):
|
|
await service.remove_member_from_group(object(), group_id, member_id, requester)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_rejects_manager_target_for_non_admin(monkeypatch):
|
|
group_id = uuid.uuid4()
|
|
manager_id = uuid.uuid4()
|
|
other_manager_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=uuid.uuid4())
|
|
|
|
async def fake_get_group_member(db, requested_group_id, requested_user_id):
|
|
if requested_user_id == manager_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MANAGER)
|
|
if requested_user_id == other_manager_id:
|
|
return SimpleNamespace(role=GroupMemberRole.MANAGER)
|
|
return None
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(service, "get_group_member", fake_get_group_member)
|
|
monkeypatch.setattr(service, "delete_group_member", AsyncMock())
|
|
|
|
requester = SimpleNamespace(id=manager_id, is_admin=False)
|
|
|
|
with pytest.raises(ValueError, match="حذف مدیر گروه مجاز نیست"):
|
|
await service.remove_member_from_group(object(), group_id, other_manager_id, requester)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_remove_member_from_group_allows_admin_to_remove_manager(monkeypatch):
|
|
db = object()
|
|
group_id = uuid.uuid4()
|
|
target_manager_id = uuid.uuid4()
|
|
group = SimpleNamespace(owner_id=uuid.uuid4())
|
|
removed = AsyncMock()
|
|
|
|
monkeypatch.setattr(service, "get_group_by_id", AsyncMock(return_value=group))
|
|
monkeypatch.setattr(
|
|
service,
|
|
"get_group_member",
|
|
AsyncMock(return_value=SimpleNamespace(role=GroupMemberRole.MANAGER)),
|
|
)
|
|
monkeypatch.setattr(service, "delete_group_member", removed)
|
|
|
|
requester = SimpleNamespace(id=uuid.uuid4(), is_admin=True)
|
|
await service.remove_member_from_group(db, group_id, target_manager_id, requester)
|
|
|
|
removed.assert_awaited_once_with(db, group_id, target_manager_id)
|