""" 邀请成员 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