Skip to content

Commit 6f01590

Browse files
authored
tests: add tests for remove from team API (#271)
* feat(team): restrict member removal to admins and reassign tasks - enforce auth so only admins can remove another member - automatically remove user roles on removal - reassign all non-DONE tasks of the removed user to the team - add audit log entry for task reassignment * feat(remove-member): resolve comments by bot * feat(remove-member): instantiate the exception raised - fix cannot remove error being shown for correct exception * feat(remove-from-team): sync changes for task assignment to postgres - update postgres model for audit log to include task_count * feat(remove-member): update cannot remove POC exception message - fix formatting * feat(remove-member-from-team): handle mixed ObjectId and string types in task reassignment - Moves service imports to the top level in `team_service.py` for better code organization. - Modifies the `update_many` call in `task_assignment_repository.py` to use the `$in` operator. This ensures that `assignee_id` and `team_id` can be matched regardless of whether they are stored as ObjectIds or strings, preventing potential issues when deactivating task assignments during member removal. * feat(remove-member-from-team): move UserTeamDetailsRepository to the function * feat(remove-from-team): set updated_by to None in the ops for task assignment creation in reassign_tasks_from_user_to_team - fix formatting issues * test(remove-member): add tests for remove_member_from_team * test(remove-member): move import to function * feat(remove-member): move task_reassignment function in transaction - add team exception in views - add assigned_from and assigned_to to audit log * feat(remove-member): fix formatting issues * feat(remove-from-team): include is_active in unique constraint to prevent sync failures * feat(remove-from-team): remove unecessary comments - fix bug where done tasks were also being reassigned - fix bug where in postgres_user_role where data sync failed if a user was removed and added more than once - updated reassigned tasks status and add sync to postgres db * feat(remove-from-member): add serializer for path params validation * feat(remove-from-team): removed aggregation pipeline and use normal db queries * feat(remove-from-todo): remove unecessary migration files * feat(remove-from-team): remove task_count field from audit log model * feat(remove-from-team): fix bug where reassigned todos not showing on poc dashboard * feat(remove-from-team): move remove role and validation to individual function * feat(remove-from-team): fix formatting issues * feat(remove-from-team): use bool return value instead of count for task reassignment function * feat(remove-from-team): update test cases for changed implementation * test(remove-from-team): remove unnecssary setup values
1 parent e58e9ec commit 6f01590

File tree

1 file changed

+117
-2
lines changed

1 file changed

+117
-2
lines changed

todo/tests/unit/services/test_team_service.py

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,58 @@
22
from unittest.mock import patch
33
from datetime import datetime, timezone
44

5+
from todo.exceptions.team_exceptions import (
6+
CannotRemoveOwnerException,
7+
CannotRemoveTeamPOCException,
8+
NotTeamAdminException,
9+
)
510
from todo.services.team_service import TeamService
611
from todo.dto.responses.get_user_teams_response import GetUserTeamsResponse
712
from todo.models.team import TeamModel, UserTeamDetailsModel
813
from todo.models.common.pyobjectid import PyObjectId
14+
from todo.dto.user_dto import UserDTO
15+
from todo.dto.team_dto import TeamDTO
916

1017

1118
class TeamServiceTests(TestCase):
1219
def setUp(self):
1320
self.user_id = "507f1f77bcf86cd799439011"
1421
self.team_id = "507f1f77bcf86cd799439012"
15-
22+
self.poc_id = "507f1f77bcf86cd799439014"
23+
self.admin_id = "507f1f77bcf86cd799439015"
24+
self.member_id = "507f1f77bcf86cd799439016"
1625
# Mock team model
1726
self.team_model = TeamModel(
1827
id=PyObjectId(self.team_id),
1928
name="Test Team",
2029
description="Test Description",
21-
poc_id=PyObjectId(self.user_id),
30+
poc_id=PyObjectId(self.poc_id),
2231
invite_code="TEST123",
2332
created_by=PyObjectId(self.user_id),
2433
updated_by=PyObjectId(self.user_id),
2534
created_at=datetime.now(timezone.utc),
2635
updated_at=datetime.now(timezone.utc),
2736
)
2837

38+
self.user_details = [
39+
UserDTO(id=self.user_id, name="Test User 1", addedOn=datetime.now(timezone.utc), tasksAssignedCount=2),
40+
UserDTO(id=self.poc_id, name="Test User 2", addedOn=datetime.now(timezone.utc), tasksAssignedCount=1),
41+
UserDTO(id=self.admin_id, name="Test User 3", addedOn=datetime.now(timezone.utc), tasksAssignedCount=2),
42+
UserDTO(id=self.member_id, name="Test User 4", addedOn=datetime.now(timezone.utc), tasksAssignedCount=1),
43+
]
44+
45+
self.team_details = TeamDTO(
46+
id=self.team_id,
47+
name="Test Team",
48+
description="Test Description",
49+
poc_id=self.poc_id,
50+
invite_code="TEST123",
51+
created_by=self.user_id,
52+
updated_by=self.user_id,
53+
created_at=datetime.now(timezone.utc),
54+
updated_at=datetime.now(timezone.utc),
55+
)
56+
2957
# Mock user team details model
3058
self.user_team_details = UserTeamDetailsModel(
3159
id=PyObjectId("507f1f77bcf86cd799439013"),
@@ -185,3 +213,90 @@ def test_join_team_by_invite_code_already_member(self, mock_get_by_user_id, mock
185213
with self.assertRaises(ValueError) as context:
186214
TeamService.join_team_by_invite_code("TEST123", self.user_id)
187215
self.assertIn("already a member", str(context.exception))
216+
217+
@patch("todo.services.user_service.UserService.get_users_by_team_id")
218+
@patch("todo.services.team_service.TeamService.get_team_by_id")
219+
def test_cannot_remove_owner(self, mock_get_by_team_id, mock_get_users_by_team_id):
220+
"""Test cannot remove team owner"""
221+
mock_get_users_by_team_id.return_value = self.user_details
222+
mock_get_by_team_id.return_value = self.team_details
223+
with self.assertRaises(CannotRemoveOwnerException):
224+
TeamService._validate_remove_member_permissions(self.user_id, self.team_id, self.user_id)
225+
226+
@patch("todo.services.user_service.UserService.get_users_by_team_id")
227+
@patch("todo.services.team_service.TeamService.get_team_by_id")
228+
def test_cannot_remove_poc(self, mock_get_by_team_id, mock_get_users_by_team_id):
229+
"""Test cannot remove team POC"""
230+
mock_get_users_by_team_id.return_value = self.user_details
231+
mock_get_by_team_id.return_value = self.team_details
232+
233+
with self.assertRaises(CannotRemoveTeamPOCException):
234+
TeamService._validate_remove_member_permissions(self.poc_id, self.team_id, self.user_id)
235+
236+
@patch("todo.services.user_service.UserService.get_users_by_team_id")
237+
@patch("todo.services.team_service.TeamService.get_team_by_id")
238+
def test_normal_member_cannot_remove_member(self, mock_get_by_team_id, mock_get_users_by_team_id):
239+
mock_get_users_by_team_id.return_value = self.user_details
240+
mock_get_by_team_id.return_value = self.team_details
241+
242+
with self.assertRaises(NotTeamAdminException):
243+
TeamService._validate_remove_member_permissions(self.admin_id, self.team_id, self.member_id)
244+
245+
@patch("todo.services.user_service.UserService.get_users_by_team_id")
246+
@patch("todo.services.team_service.TeamService.get_team_by_id")
247+
def test_team_or_user_not_found(self, mock_get_by_team_id, mock_get_users_by_team_id):
248+
mock_get_users_by_team_id.return_value = self.user_details
249+
mock_get_by_team_id.return_value = self.team_details
250+
251+
with self.assertRaises(TeamService.TeamOrUserNotFound):
252+
TeamService._validate_remove_member_permissions("not-existing-member", self.team_id, self.member_id)
253+
254+
@patch("todo.services.task_assignment_service.TaskAssignmentService.reassign_tasks_from_user_to_team")
255+
@patch("todo.services.user_role_service.UserRoleService.remove_all_user_roles_for_team")
256+
@patch("todo.repositories.audit_log_repository.AuditLogRepository.create")
257+
@patch("todo.repositories.user_team_details_repository.UserTeamDetailsRepository.remove_member_from_team")
258+
@patch("todo.services.team_service.TeamService._validate_remove_member_permissions")
259+
def test_admin_can_remove_member_successfully(
260+
self, mock_validate, mock_remove_member, mock_audit_log_create, mock_remove_roles, mock_reassign_tasks
261+
):
262+
mock_validate.return_value = None
263+
mock_remove_member.return_value = True
264+
mock_remove_roles.return_value = True
265+
mock_reassign_tasks.return_value = True
266+
267+
result = TeamService.remove_member_from_team(self.member_id, self.team_id, self.admin_id)
268+
269+
self.assertTrue(result)
270+
mock_remove_member.assert_called_once_with(user_id=self.member_id, team_id=self.team_id)
271+
mock_remove_roles.assert_called_once()
272+
mock_reassign_tasks.assert_called_once()
273+
mock_audit_log_create.assert_called_once()
274+
log_entry = mock_audit_log_create.call_args[0][0]
275+
self.assertEqual(log_entry.action, "member_removed_from_team")
276+
self.assertEqual(str(log_entry.team_id), self.team_id)
277+
self.assertEqual(str(log_entry.performed_by), self.admin_id)
278+
279+
@patch("todo.services.task_assignment_service.TaskAssignmentService.reassign_tasks_from_user_to_team")
280+
@patch("todo.services.user_role_service.UserRoleService.remove_all_user_roles_for_team")
281+
@patch("todo.repositories.audit_log_repository.AuditLogRepository.create")
282+
@patch("todo.repositories.user_team_details_repository.UserTeamDetailsRepository.remove_member_from_team")
283+
@patch("todo.services.team_service.TeamService._validate_remove_member_permissions")
284+
def test_user_can_remove_themselves(
285+
self, mock_validate, mock_remove_member, mock_audit_log_create, mock_remove_roles, mock_reassign_tasks
286+
):
287+
mock_validate.return_value = None
288+
mock_remove_member.return_value = True
289+
mock_remove_roles.return_value = True
290+
mock_reassign_tasks.return_value = True
291+
292+
result = TeamService.remove_member_from_team(self.member_id, self.team_id, self.member_id)
293+
294+
self.assertTrue(result)
295+
mock_remove_member.assert_called_once_with(user_id=self.member_id, team_id=self.team_id)
296+
mock_remove_roles.assert_called_once_with(self.member_id, self.team_id)
297+
mock_reassign_tasks.assert_called_once_with(self.member_id, self.team_id, self.member_id)
298+
mock_audit_log_create.assert_called_once()
299+
log_entry = mock_audit_log_create.call_args[0][0]
300+
self.assertEqual(log_entry.action, "member_left_team")
301+
self.assertEqual(str(log_entry.team_id), self.team_id)
302+
self.assertEqual(str(log_entry.performed_by), self.member_id)

0 commit comments

Comments
 (0)