from django.test import TestCase from django.core.files.uploadedfile import SimpleUploadedFile from django.contrib.auth import get_user_model from rest_framework.test import APIClient from rest_framework import status from apps.profiles.models.institution import ( Institution, InstitutionMember, InstitutionTimeline, InstitutionMedia, InstitutionFollower, ) User = get_user_model() class Phase2InstitutionProfileTests(TestCase): """ Automated test suite for Phase 2: - Institution Registration, Auto Admin Membership & Slug Generation - Institution List & Query Filtering (country, city, type, tag, search) - Institution Retrieval by ID and Unicode Slug - Institution Profile Updates & Role-Based Permissions - Historical Timeline Milestones CRUD - Photo/Video Media Gallery Upload & Deletion - Follower Subscriptions & Follower Count Tracking - Team Member Role Delegation & Management """ def setUp(self): self.client = APIClient() # Users self.admin_user = User.objects.create_user( email="imam.ali.center@berlin.de", password="SecurePassword123!", fullname="Sheikh Hassan Al-Berlini", user_type=User.UserType.INSTITUTION_ADMIN, country="Germany", city="Berlin" ) self.editor_user = User.objects.create_user( email="editor@berlin.de", password="SecurePassword123!", fullname="Zainab Media Specialist", user_type=User.UserType.EDITOR, country="Germany", city="Berlin" ) self.regular_user = User.objects.create_user( email="member@community.org", password="SecurePassword123!", fullname="Ali Community Member", user_type=User.UserType.CLIENT, country="Germany", city="Frankfurt" ) # Primary Institution self.institution = Institution.objects.create( name="Imam Ali Islamic Center Berlin", type=Institution.InstitutionType.CULTURAL_CENTER, country="Germany", city="Berlin", address="Flughafenstrasse 42, 12053 Berlin", latitude=52.4839, longitude=13.4325, description="Leading cultural and religious center serving the international community in Berlin.", website="https://imam-ali-berlin.de", email="contact@imam-ali-berlin.de", phone="+49 30 620000", established_year=1998, tags=["Youth Programs", "Interfaith Dialogue", "Quran Academy"], verification_status=Institution.VerificationStatus.APPROVED, is_featured=True, created_by=self.admin_user ) # Member roles InstitutionMember.objects.create( institution=self.institution, user=self.admin_user, role=InstitutionMember.MemberRole.ADMIN, title="Director General" ) InstitutionMember.objects.create( institution=self.institution, user=self.editor_user, role=InstitutionMember.MemberRole.EDITOR, title="Content Coordinator" ) def test_institution_registration_and_auto_membership(self): self.client.force_authenticate(user=self.admin_user) payload = { "name": "Al-Zahra Cultural Center Vienna", "type": "cultural_center", "country": "Austria", "city": "Vienna", "address": "Favoritenstrasse 88", "latitude": 48.1751, "longitude": 16.3776, "description": "Center dedicated to cultural and scientific exchange.", "established_year": 2005, "tags": ["Library", "Language Courses"] } response = self.client.post("/api/v1/profiles/", payload, format="json") self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.data["name"], "Al-Zahra Cultural Center Vienna") self.assertTrue(response.data["slug"]) self.assertEqual(response.data["verification_status"], "pending") # Verify Creator is registered as Admin member inst_id = response.data["id"] member = InstitutionMember.objects.get(institution_id=inst_id, user=self.admin_user) self.assertEqual(member.role, InstitutionMember.MemberRole.ADMIN) def test_institution_listing_and_filtering(self): # 1. Base list response = self.client.get("/api/v1/profiles/") self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertGreaterEqual(len(response.data["results"]), 1) # 2. Filter by country resp_de = self.client.get("/api/v1/profiles/?country=Germany") self.assertEqual(len(resp_de.data["results"]), 1) resp_fr = self.client.get("/api/v1/profiles/?country=France") self.assertEqual(len(resp_fr.data["results"]), 0) # 3. Filter by search resp_search = self.client.get("/api/v1/profiles/?search=Berlin") self.assertEqual(len(resp_search.data["results"]), 1) # 4. Filter by tag resp_tag = self.client.get("/api/v1/profiles/?tag=Youth Programs") self.assertEqual(len(resp_tag.data["results"]), 1) def test_institution_detail_by_id_and_slug(self): # 1. Fetch by ID resp_id = self.client.get(f"/api/v1/profiles/{self.institution.id}/") self.assertEqual(resp_id.status_code, status.HTTP_200_OK) self.assertEqual(resp_id.data["name"], "Imam Ali Islamic Center Berlin") self.assertEqual(resp_id.data["members_count"], 2) # 2. Fetch by Slug resp_slug = self.client.get(f"/api/v1/profiles/{self.institution.slug}/") self.assertEqual(resp_slug.status_code, status.HTTP_200_OK) self.assertEqual(resp_slug.data["id"], self.institution.id) def test_institution_patch_update_permissions(self): patch_data = { "description": "Updated international description with expanded multi-language community center.", "phone": "+49 30 777888" } # 1. Unauthorized user cannot update unauth_resp = self.client.patch(f"/api/v1/profiles/{self.institution.id}/", patch_data, format="json") self.assertEqual(unauth_resp.status_code, status.HTTP_401_UNAUTHORIZED) # 2. Regular non-member user receives 403 self.client.force_authenticate(user=self.regular_user) forbidden_resp = self.client.patch(f"/api/v1/profiles/{self.institution.id}/", patch_data, format="json") self.assertEqual(forbidden_resp.status_code, status.HTTP_403_FORBIDDEN) # 3. Institution Editor can update self.client.force_authenticate(user=self.editor_user) editor_resp = self.client.patch(f"/api/v1/profiles/{self.institution.id}/", patch_data, format="json") self.assertEqual(editor_resp.status_code, status.HTTP_200_OK) self.assertEqual(editor_resp.data["phone"], "+49 30 777888") # 4. Institution Admin can update self.client.force_authenticate(user=self.admin_user) admin_resp = self.client.patch(f"/api/v1/profiles/{self.institution.id}/", {"city": "Berlin Capital"}, format="json") self.assertEqual(admin_resp.status_code, status.HTTP_200_OK) self.assertEqual(admin_resp.data["city"], "Berlin Capital") def test_institution_timeline_crud(self): self.client.force_authenticate(user=self.editor_user) # 1. Add Timeline entry payload = { "year": "1998", "title": "Foundation and Inauguration", "description": "First official opening ceremony with community leaders.", "order": 1 } create_resp = self.client.post(f"/api/v1/profiles/{self.institution.id}/timeline/", payload, format="json") self.assertEqual(create_resp.status_code, status.HTTP_201_CREATED) entry_id = create_resp.data["id"] # 2. List Timeline list_resp = self.client.get(f"/api/v1/profiles/{self.institution.id}/timeline/") self.assertEqual(list_resp.status_code, status.HTTP_200_OK) self.assertEqual(len(list_resp.data), 1) self.assertEqual(list_resp.data[0]["title"], "Foundation and Inauguration") # 3. Delete Timeline entry del_resp = self.client.delete(f"/api/v1/profiles/{self.institution.id}/timeline/{entry_id}/") self.assertEqual(del_resp.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(InstitutionTimeline.objects.filter(id=entry_id).count(), 0) def test_institution_media_upload_and_delete(self): self.client.force_authenticate(user=self.editor_user) mock_image = SimpleUploadedFile("center_hall.jpg", b"mock_image_bytes", content_type="image/jpeg") upload_resp = self.client.post( f"/api/v1/profiles/{self.institution.id}/media/", { "file": mock_image, "media_type": "image", "title": "Main Prayer and Conference Hall", "caption": "Capacity for 800 attendees." }, format="multipart" ) self.assertEqual(upload_resp.status_code, status.HTTP_201_CREATED) media_id = upload_resp.data["id"] # List media list_resp = self.client.get(f"/api/v1/profiles/{self.institution.id}/media/") self.assertEqual(list_resp.status_code, status.HTTP_200_OK) self.assertEqual(len(list_resp.data), 1) # Delete media del_resp = self.client.delete(f"/api/v1/profiles/{self.institution.id}/media/{media_id}/") self.assertEqual(del_resp.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(InstitutionMedia.objects.filter(id=media_id).count(), 0) def test_institution_follow_and_unfollow(self): self.client.force_authenticate(user=self.regular_user) # 1. Initial State self.assertEqual(self.institution.follower_count, 0) # 2. Follow follow_resp = self.client.post(f"/api/v1/profiles/{self.institution.id}/follow/") self.assertEqual(follow_resp.status_code, status.HTTP_200_OK) self.assertTrue(follow_resp.data["is_following"]) self.assertEqual(follow_resp.data["follower_count"], 1) # 3. Check Institution Detail reflects following state detail_resp = self.client.get(f"/api/v1/profiles/{self.institution.id}/") self.assertTrue(detail_resp.data["is_following"]) # 4. Unfollow unfollow_resp = self.client.delete(f"/api/v1/profiles/{self.institution.id}/follow/") self.assertEqual(unfollow_resp.status_code, status.HTTP_200_OK) self.assertFalse(unfollow_resp.data["is_following"]) self.assertEqual(unfollow_resp.data["follower_count"], 0) def test_institution_member_delegation(self): # 1. Non-admin cannot delegate roles self.client.force_authenticate(user=self.regular_user) forbid_resp = self.client.post( f"/api/v1/profiles/{self.institution.id}/members/", {"email": "member@community.org", "role": "editor"}, format="json" ) self.assertEqual(forbid_resp.status_code, status.HTTP_403_FORBIDDEN) # 2. Admin adds regular_user as Viewer self.client.force_authenticate(user=self.admin_user) add_resp = self.client.post( f"/api/v1/profiles/{self.institution.id}/members/", { "email": "member@community.org", "role": "viewer", "title": "Community Representative" }, format="json" ) self.assertEqual(add_resp.status_code, status.HTTP_201_CREATED) member_id = add_resp.data["id"] self.assertEqual(add_resp.data["role"], "viewer") # 3. List Members members_resp = self.client.get(f"/api/v1/profiles/{self.institution.id}/members/") self.assertEqual(members_resp.status_code, status.HTTP_200_OK) self.assertEqual(len(members_resp.data), 3) # 4. Remove Member del_member_resp = self.client.delete(f"/api/v1/profiles/{self.institution.id}/members/{member_id}/") self.assertEqual(del_member_resp.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(InstitutionMember.objects.filter(id=member_id).count(), 0)