You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
8.5 KiB
199 lines
8.5 KiB
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="[email protected]",
|
|
password="Password123!",
|
|
fullname="Super Admin"
|
|
)
|
|
self.admin_a = User.objects.create_user(
|
|
email="[email protected]",
|
|
password="Password123!",
|
|
fullname="Director Alpha",
|
|
user_type="institution_admin"
|
|
)
|
|
self.admin_b = User.objects.create_user(
|
|
email="[email protected]",
|
|
password="Password123!",
|
|
fullname="Director Beta",
|
|
user_type="institution_admin"
|
|
)
|
|
self.unrelated_user = User.objects.create_user(
|
|
email="[email protected]",
|
|
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())
|