from django.test import TestCase from rest_framework.test import APIClient from apps.notifications.services import store as services from apps.notifications.services import RedisNotificationStore from apps.notifications.tests.fakes import FakeRedis from apps.users.models import User from apps.workspaces.models import Workspace, WorkspaceMembership class WorkspaceMembershipNotificationTests(TestCase): @classmethod def setUpTestData(cls): cls.owner = cls._create_user(1) cls.member = cls._create_user(2) @staticmethod def _create_user(index): return User.objects.create_user( mobile=f"091200000{index:02d}", password="secret123", first_name=f"User{index}", ) def setUp(self): self.client = APIClient() self.fake_redis = FakeRedis() self.original_redis_client = services.redis_client services.redis_client = self.fake_redis def tearDown(self): services.redis_client = self.original_redis_client @staticmethod def _notifications_for(user): notifications, _ = RedisNotificationStore.list(str(user.id), paginate=False) return notifications def test_workspace_create_notifies_initial_members_not_owner(self): self.client.force_authenticate(user=self.owner) response = self.client.post( "/api/workspaces/", { "name": "Ops", "description": "Workspace", "members": [ { "user_id": str(self.member.id), "role": WorkspaceMembership.Role.ADMIN, } ], }, format="json", ) self.assertEqual(response.status_code, 201) self.assertEqual(self._notifications_for(self.owner), []) member_notifications = self._notifications_for(self.member) self.assertEqual(len(member_notifications), 1) self.assertEqual(member_notifications[0]["type"], "workspace_membership_added") self.assertEqual(member_notifications[0]["meta"]["workspace_name"], "Ops") self.assertEqual( member_notifications[0]["meta"]["new_role"], WorkspaceMembership.Role.ADMIN, ) def test_workspace_membership_crud_emits_all_expected_events(self): workspace = Workspace.objects.create(name="Design", description="", owner=self.owner) self.client.force_authenticate(user=self.owner) create_response = self.client.post( "/api/workspace-memberships/", { "workspace": str(workspace.id), "user": str(self.member.id), "role": WorkspaceMembership.Role.MEMBER, "is_active": True, }, format="json", ) self.assertEqual(create_response.status_code, 201) membership_id = create_response.data["id"] notifications = self._notifications_for(self.member) self.assertEqual( [item["type"] for item in notifications], ["workspace_membership_added"], ) role_response = self.client.patch( f"/api/workspace-memberships/{membership_id}/", {"role": WorkspaceMembership.Role.ADMIN}, format="json", ) self.assertEqual(role_response.status_code, 200) deactivate_response = self.client.patch( f"/api/workspace-memberships/{membership_id}/", {"is_active": False}, format="json", ) self.assertEqual(deactivate_response.status_code, 200) remove_response = self.client.delete( f"/api/workspace-memberships/{membership_id}/" ) self.assertEqual(remove_response.status_code, 204) notifications = self._notifications_for(self.member) self.assertEqual( [item["type"] for item in notifications], [ "workspace_membership_removed", "workspace_membership_deactivated", "workspace_membership_role_changed", "workspace_membership_added", ], ) def test_workspace_membership_update_skips_self_notifications(self): workspace = Workspace.objects.create(name="Product", description="", owner=self.owner) owner_membership = WorkspaceMembership.objects.get( workspace=workspace, user=self.owner, is_deleted=False, ) self.client.force_authenticate(user=self.owner) response = self.client.patch( f"/api/workspace-memberships/{owner_membership.id}/", {"role": WorkspaceMembership.Role.OWNER}, format="json", ) self.assertEqual(response.status_code, 403) self.assertEqual(self._notifications_for(self.owner), [])