Files
xiaoxia-saas/tests/unit/test_invite_member_use_case.py
T
Xiaoxia AI b79d6718d6
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 21s
Deploy / Deploy Production (push) Has been cancelled
Deploy / Deploy Staging (push) Has been cancelled
style: normalize python formatting gates
2026-06-21 06:52:19 +08:00

297 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
邀请成员 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