297 lines
8.7 KiB
Python
297 lines
8.7 KiB
Python
"""
|
||
邀请成员 Use Case 测试
|
||
"""
|
||
|
||
from datetime import datetime, timezone
|
||
from unittest.mock import Mock
|
||
|
||
import pytest
|
||
|
||
from packages.application.workspace.invite_member_use_case import (
|
||
InviteMemberRequest,
|
||
InviteMemberUseCase,
|
||
)
|
||
from packages.domain.entities import (
|
||
User,
|
||
Workspace,
|
||
WorkspaceMember,
|
||
WorkspaceMemberRole,
|
||
)
|
||
|
||
|
||
class TestInviteMemberUseCase:
|
||
"""邀请成员测试"""
|
||
|
||
@pytest.fixture
|
||
def mock_workspace_repo(self):
|
||
repo = Mock()
|
||
repo.find_by_id = Mock(return_value=None)
|
||
return repo
|
||
|
||
@pytest.fixture
|
||
def mock_member_repo(self):
|
||
repo = Mock()
|
||
repo.find_by_workspace_and_user = Mock(return_value=None)
|
||
return repo
|
||
|
||
@pytest.fixture
|
||
def mock_invitation_repo(self):
|
||
repo = Mock()
|
||
repo.find_pending_by_workspace_and_email = Mock(return_value=None)
|
||
repo.save = Mock()
|
||
return repo
|
||
|
||
@pytest.fixture
|
||
def mock_user_repo(self):
|
||
repo = Mock()
|
||
repo.find_by_email = Mock(return_value=None)
|
||
repo.find_by_id = Mock(return_value=None)
|
||
return repo
|
||
|
||
@pytest.fixture
|
||
def use_case(
|
||
self,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
mock_invitation_repo,
|
||
mock_user_repo,
|
||
):
|
||
email_service = Mock()
|
||
email_service.send_workspace_invitation_email.return_value = (True, None)
|
||
return InviteMemberUseCase(
|
||
workspace_repository=mock_workspace_repo,
|
||
workspace_member_repository=mock_member_repo,
|
||
workspace_invitation_repository=mock_invitation_repo,
|
||
user_repository=mock_user_repo,
|
||
base_url="https://test.com",
|
||
invitation_expire_days=7,
|
||
email_service=email_service,
|
||
)
|
||
|
||
@pytest.fixture
|
||
def test_workspace(self):
|
||
return Workspace(
|
||
id="workspace-123",
|
||
name="Test Workspace",
|
||
owner_user_id="owner-id",
|
||
)
|
||
|
||
@pytest.fixture
|
||
def test_inviter(self):
|
||
return User(
|
||
id="inviter-id",
|
||
email="inviter@test.com",
|
||
username="inviter",
|
||
display_name="Inviter User",
|
||
)
|
||
|
||
@pytest.fixture
|
||
def owner_member(self):
|
||
return WorkspaceMember(
|
||
id="member-1",
|
||
workspace_id="workspace-123",
|
||
user_id="inviter-id",
|
||
role=WorkspaceMemberRole.OWNER,
|
||
)
|
||
|
||
@pytest.fixture
|
||
def admin_member(self):
|
||
return WorkspaceMember(
|
||
id="member-2",
|
||
workspace_id="workspace-123",
|
||
user_id="inviter-id",
|
||
role=WorkspaceMemberRole.ADMIN,
|
||
)
|
||
|
||
def test_invite_member_success_by_owner(
|
||
self,
|
||
use_case,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
mock_invitation_repo,
|
||
mock_user_repo,
|
||
test_workspace,
|
||
test_inviter,
|
||
owner_member,
|
||
):
|
||
"""测试 Owner 邀请成员成功"""
|
||
mock_workspace_repo.find_by_id.return_value = test_workspace
|
||
mock_member_repo.find_by_workspace_and_user.return_value = owner_member
|
||
mock_user_repo.find_by_id.return_value = test_inviter
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="newuser@test.com",
|
||
role="member",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert error is None
|
||
assert response is not None
|
||
assert response.invitee_email == "newuser@test.com"
|
||
assert response.role == "member"
|
||
assert response.expires_at is not None
|
||
|
||
# 验证保存了邀请
|
||
mock_invitation_repo.save.assert_called_once()
|
||
invitation = mock_invitation_repo.save.call_args[0][0]
|
||
assert invitation.invitee_email == "newuser@test.com"
|
||
assert invitation.status == "pending"
|
||
|
||
# 验证发送了邮件
|
||
use_case.email_service.send_workspace_invitation_email.assert_called_once()
|
||
|
||
def test_invite_member_success_by_admin(
|
||
self,
|
||
use_case,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
mock_invitation_repo,
|
||
mock_user_repo,
|
||
test_workspace,
|
||
test_inviter,
|
||
admin_member,
|
||
):
|
||
"""测试 Admin 邀请成员成功"""
|
||
mock_workspace_repo.find_by_id.return_value = test_workspace
|
||
mock_member_repo.find_by_workspace_and_user.return_value = admin_member
|
||
mock_user_repo.find_by_id.return_value = test_inviter
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="newuser@test.com",
|
||
role="viewer",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert error is None
|
||
assert response is not None
|
||
|
||
def test_invite_member_workspace_not_found(self, use_case, mock_workspace_repo):
|
||
"""测试 Workspace 不存在"""
|
||
mock_workspace_repo.find_by_id.return_value = None
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="nonexistent",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="newuser@test.com",
|
||
role="member",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert response is None
|
||
assert error == "Workspace not found"
|
||
|
||
def test_invite_member_inviter_not_member(
|
||
self,
|
||
use_case,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
test_workspace,
|
||
):
|
||
"""测试邀请人不是成员"""
|
||
mock_workspace_repo.find_by_id.return_value = test_workspace
|
||
mock_member_repo.find_by_workspace_and_user.return_value = None
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="nonmember-id",
|
||
invitee_email="newuser@test.com",
|
||
role="member",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert response is None
|
||
assert error == "You are not a member of this workspace"
|
||
|
||
def test_invite_member_inviter_no_permission(
|
||
self,
|
||
use_case,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
test_workspace,
|
||
):
|
||
"""测试邀请人没有权限(只是普通成员)"""
|
||
mock_workspace_repo.find_by_id.return_value = test_workspace
|
||
|
||
regular_member = WorkspaceMember(
|
||
id="member-3",
|
||
workspace_id="workspace-123",
|
||
user_id="inviter-id",
|
||
role=WorkspaceMemberRole.MEMBER,
|
||
)
|
||
mock_member_repo.find_by_workspace_and_user.return_value = regular_member
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="newuser@test.com",
|
||
role="member",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert response is None
|
||
assert error == "Only owners and admins can invite members"
|
||
|
||
def test_invite_member_already_member(
|
||
self,
|
||
use_case,
|
||
mock_workspace_repo,
|
||
mock_member_repo,
|
||
mock_user_repo,
|
||
test_workspace,
|
||
owner_member,
|
||
):
|
||
"""测试被邀请人已经是成员"""
|
||
mock_workspace_repo.find_by_id.return_value = test_workspace
|
||
mock_member_repo.find_by_workspace_and_user.side_effect = [
|
||
owner_member, # 第一次调用:inviter 是 owner
|
||
WorkspaceMember( # 第二次调用:invitee 已经是成员
|
||
id="member-4",
|
||
workspace_id="workspace-123",
|
||
user_id="existing-user-id",
|
||
role=WorkspaceMemberRole.MEMBER,
|
||
),
|
||
]
|
||
|
||
existing_user = User(
|
||
id="existing-user-id",
|
||
email="existing@test.com",
|
||
username="existing",
|
||
display_name="Existing User",
|
||
)
|
||
mock_user_repo.find_by_email.return_value = existing_user
|
||
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="existing@test.com",
|
||
role="member",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert response is None
|
||
assert error == "User is already a member of this workspace"
|
||
|
||
def test_invite_member_invalid_role(self, use_case):
|
||
"""测试无效角色(不能邀请为 owner)"""
|
||
request = InviteMemberRequest(
|
||
workspace_id="workspace-123",
|
||
inviter_user_id="inviter-id",
|
||
invitee_email="newuser@test.com",
|
||
role="owner",
|
||
)
|
||
|
||
response, error = use_case.execute(request)
|
||
|
||
assert response is None
|
||
assert "Invalid role" in error
|