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.
 
 

286 lines
12 KiB

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)