import datetime from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase from apps.account.models import User from apps.profiles.models import Institution, InstitutionMember from apps.meetings.models import MeetingRequest, MeetingStatus class Phase7MeetingsAPITests(APITestCase): def setUp(self): # Users self.superadmin = User.objects.create_superuser( email="superadmin@razavi.global", password="Password123!", fullname="Super Admin" ) self.admin_a = User.objects.create_user( email="admin_a@center1.org", password="Password123!", fullname="Director Alpha", user_type="institution_admin" ) self.admin_b = User.objects.create_user( email="admin_b@center2.org", password="Password123!", fullname="Director Beta", user_type="institution_admin" ) self.unrelated_user = User.objects.create_user( email="unrelated@external.org", password="Password123!", fullname="External User", user_type="client" ) # Institutions self.inst_a = Institution.objects.create( name="Islamic Center of Hamburg", slug="islamic-center-hamburg", type="cultural_center", country="Germany", city="Hamburg", address="Schöne Aussicht 36", latitude=53.5600, longitude=10.0100, verification_status="approved", is_active=True ) self.inst_b = Institution.objects.create( name="Al-Khoei Foundation London", slug="al-khoei-london", type="institute", country="United Kingdom", city="London", address="Chevening Road", latitude=51.5400, longitude=-0.2100, verification_status="approved", is_active=True ) self.inst_c = Institution.objects.create( name="Imam Reza Cultural Center Paris", slug="imam-reza-paris", type="cultural_center", country="France", city="Paris", address="Rue de Rivoli", latitude=48.8566, longitude=2.3522, verification_status="approved", is_active=True ) # Memberships InstitutionMember.objects.create(institution=self.inst_a, user=self.admin_a, role="admin") InstitutionMember.objects.create(institution=self.inst_b, user=self.admin_b, role="admin") # Initial meeting self.meeting = MeetingRequest.objects.create( title="Joint Inter-Faith Youth Dialogue 2026", requester_institution=self.inst_a, recipient_institution=self.inst_b, created_by=self.admin_a, status=MeetingStatus.PENDING, meeting_date=datetime.date.today() + datetime.timedelta(days=3), meeting_time=datetime.time(14, 0), duration_minutes=60, agenda="Discussing curriculum collaboration and joint webinar dates." ) def test_list_meetings_scoped_to_user_institutions(self): url = reverse('meeting-list-create') # Admin A should see the meeting self.client.force_authenticate(user=self.admin_a) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) results = response.data.get('results', response.data) self.assertEqual(len(results), 1) self.assertEqual(results[0]['title'], self.meeting.title) # Admin B (recipient) should also see the meeting self.client.force_authenticate(user=self.admin_b) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) results = response.data.get('results', response.data) self.assertEqual(len(results), 1) # Unrelated user should see no meetings self.client.force_authenticate(user=self.unrelated_user) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) results = response.data.get('results', response.data) self.assertEqual(len(results), 0) def test_create_meeting_request_successful(self): self.client.force_authenticate(user=self.admin_a) url = reverse('meeting-list-create') payload = { "title": "Bilateral Cultural Exchange Planning", "requester_institution_id": self.inst_a.id, "recipient_institution_id": self.inst_c.id, "meeting_date": str(datetime.date.today() + datetime.timedelta(days=5)), "meeting_time": "15:30:00", "duration_minutes": 45, "agenda": "Reviewing translated publications distribution." } response = self.client.post(url, payload, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.data['title'], "Bilateral Cultural Exchange Planning") self.assertEqual(response.data['status'], MeetingStatus.PENDING) # Verify auto-generated meeting url self.assertTrue("meet.jit.si" in response.data['meeting_url']) def test_create_meeting_same_institution_rejected(self): self.client.force_authenticate(user=self.admin_a) url = reverse('meeting-list-create') payload = { "title": "Invalid Self Meeting", "requester_institution_id": self.inst_a.id, "recipient_institution_id": self.inst_a.id, "meeting_date": str(datetime.date.today() + datetime.timedelta(days=2)), "meeting_time": "11:00:00" } response = self.client.post(url, payload, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) error_fields = [e.get('field') for e in response.data.get('errors', [])] if 'errors' in response.data else response.data self.assertTrue('recipient_institution_id' in error_fields) def test_create_meeting_unauthorized_requester(self): self.client.force_authenticate(user=self.unrelated_user) url = reverse('meeting-list-create') payload = { "title": "Unauthorized Meeting", "requester_institution_id": self.inst_a.id, "recipient_institution_id": self.inst_b.id, "meeting_date": str(datetime.date.today() + datetime.timedelta(days=2)), "meeting_time": "11:00:00" } response = self.client.post(url, payload, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) error_fields = [e.get('field') for e in response.data.get('errors', [])] if 'errors' in response.data else response.data self.assertTrue('requester_institution_id' in error_fields) def test_retrieve_meeting_detail(self): url = reverse('meeting-detail', kwargs={'pk': self.meeting.id}) # Unrelated user forbidden self.client.force_authenticate(user=self.unrelated_user) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) # Recipient admin permitted self.client.force_authenticate(user=self.admin_b) response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['id'], self.meeting.id) self.assertEqual(response.data['requester_institution']['id'], self.inst_a.id) def test_update_meeting_status_and_schedule(self): url = reverse('meeting-detail', kwargs={'pk': self.meeting.id}) # Recipient admin accepts meeting self.client.force_authenticate(user=self.admin_b) response = self.client.patch(url, { "status": MeetingStatus.SCHEDULED, "admin_notes": "Accepted. Looking forward to meeting." }, format='json') self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['status'], MeetingStatus.SCHEDULED) self.assertEqual(response.data['admin_notes'], "Accepted. Looking forward to meeting.") def test_cancel_meeting(self): url = reverse('meeting-detail', kwargs={'pk': self.meeting.id}) self.client.force_authenticate(user=self.admin_a) response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertFalse(MeetingRequest.objects.filter(id=self.meeting.id).exists())