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.
 
 

265 lines
11 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.cms.models.post import (
PostCategory,
PostTag,
Post,
PostComment,
PostLike,
MediaAsset,
)
from apps.profiles.models.institution import Institution
User = get_user_model()
class Phase4CMSTests(TestCase):
"""
Automated test suite for Phase 4:
- Multilingual Post Creation, Auto-Slug & Reading Time Calculation
- Multilingual Post Listing & Filtering (language, post_type, category, tag, search)
- Post Detail View & Automated Views Counter Increment
- Post Update & Role-Based Permissions (author vs forbidden user)
- Post Like / Unlike Toggle with Counter Sync
- Post Reader Comments Submission & Listing
- Category & Tag Taxonomy Endpoints
- Media Asset Library Upload & Metadata Tracking
"""
def setUp(self):
self.client = APIClient()
# Users
self.author_user = User.objects.create_user(
email="[email protected]",
password="SecurePassword123!",
fullname="Dr. Mahmoud Razavi",
user_type=User.UserType.EDITOR,
country="Iran",
city="Mashhad"
)
self.other_user = User.objects.create_user(
email="[email protected]",
password="SecurePassword123!",
fullname="Hassan Reader",
user_type=User.UserType.CLIENT,
country="Lebanon",
city="Beirut"
)
# Institution
self.institution = Institution.objects.create(
name="Astan Quds Razavi International Center",
type=Institution.InstitutionType.INSTITUTE,
country="Iran",
city="Mashhad"
)
# Category & Tag
self.category_en = PostCategory.objects.create(
name="Cultural Diplomacy",
language="en",
description="Inter-faith and cultural cooperation articles."
)
self.category_fa = PostCategory.objects.create(
name="دیپلماسی فرهنگی",
language="fa",
description="مقالات همکاری های بین المللی و گفتگوی ادیان."
)
self.tag_diplomacy = PostTag.objects.create(name="Diplomacy")
self.tag_dialogue = PostTag.objects.create(name="Interfaith")
# Sample Published Post
self.post = Post.objects.create(
title="The Role of Razavi Teachings in Global Cultural Diplomacy",
post_type=Post.PostType.ARTICLE,
category=self.category_en,
language=Post.LanguageChoices.EN,
status=Post.Status.PUBLISHED,
author=self.author_user,
institution=self.institution,
author_role_label="Senior Fellow in Islamic Diplomacy",
excerpt="Analyzing contemporary methods for promoting intercultural understanding through Razavi traditions.",
content=" ".join(["Razavi teachings emphasize compassion, rational dialogue, and mutual respect."] * 100),
tags=["Diplomacy", "Interfaith", "Razavi Studies"],
is_featured=True
)
def test_post_creation_and_auto_attributes(self):
self.client.force_authenticate(user=self.author_user)
payload = {
"title": "Interfaith Centers in Modern Europe: A Comparative Analysis",
"post_type": "report",
"category": self.category_en.id,
"language": "en",
"status": "published",
"excerpt": "Field report examining 50 Islamic cultural centers across Western Europe.",
"content": " ".join(["Comprehensive analysis of Islamic centers in Germany, France, and Austria."] * 250),
"tags": ["Europe", "Cultural Centers", "Field Report"],
"is_featured": False
}
response = self.client.post("/api/v1/cms/posts/", payload, format="json")
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data["title"], payload["title"])
self.assertTrue(response.data["slug"])
self.assertEqual(response.data["author"]["email"], self.author_user.email)
# Word count: 250 * 9 = 2250 words -> approx 12 mins
self.assertGreaterEqual(response.data["reading_time_minutes"], 2)
def test_post_listing_and_multilingual_filtering(self):
# Create an Arabic post
Post.objects.create(
title="دور المراكز الإسلامية في تعزيز الحوار الحضاري",
post_type=Post.PostType.NEWS,
language=Post.LanguageChoices.AR,
status=Post.Status.PUBLISHED,
author=self.author_user,
content="تقرير إخباري حول مؤتمر الحوار في مشهد المقدسة."
)
# 1. Base list
response = self.client.get("/api/v1/cms/posts/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertGreaterEqual(len(response.data["results"]), 2)
# 2. Filter by Language
resp_en = self.client.get("/api/v1/cms/posts/?language=en")
self.assertEqual(len(resp_en.data["results"]), 1)
self.assertEqual(resp_en.data["results"][0]["language"], "en")
resp_ar = self.client.get("/api/v1/cms/posts/?language=ar")
self.assertEqual(len(resp_ar.data["results"]), 1)
self.assertEqual(resp_ar.data["results"][0]["language"], "ar")
# 3. Filter by Post Type
resp_type = self.client.get("/api/v1/cms/posts/?post_type=article")
self.assertEqual(len(resp_type.data["results"]), 1)
self.assertEqual(resp_type.data["results"][0]["post_type"], "article")
# 4. Search Filter
resp_search = self.client.get("/api/v1/cms/posts/?search=Razavi")
self.assertEqual(len(resp_search.data["results"]), 1)
def test_post_detail_retrieval_and_views_increment(self):
initial_views = self.post.views_count
# 1. Fetch by Slug
response = self.client.get(f"/api/v1/cms/posts/{self.post.slug}/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["id"], self.post.id)
self.assertEqual(response.data["views_count"], initial_views + 1)
# 2. Fetch by ID
resp_id = self.client.get(f"/api/v1/cms/posts/{self.post.id}/")
self.assertEqual(resp_id.status_code, status.HTTP_200_OK)
self.assertEqual(resp_id.data["views_count"], initial_views + 2)
def test_post_patch_update_and_permissions(self):
patch_payload = {
"title": "The Role of Razavi Teachings in Global Cultural Diplomacy (Updated Edition)"
}
# 1. Unauthenticated user receives 401
unauth_resp = self.client.patch(f"/api/v1/cms/posts/{self.post.id}/", patch_payload, format="json")
self.assertEqual(unauth_resp.status_code, status.HTTP_401_UNAUTHORIZED)
# 2. Non-author user receives 403
self.client.force_authenticate(user=self.other_user)
forbidden_resp = self.client.patch(f"/api/v1/cms/posts/{self.post.id}/", patch_payload, format="json")
self.assertEqual(forbidden_resp.status_code, status.HTTP_403_FORBIDDEN)
# 3. Author can successfully update
self.client.force_authenticate(user=self.author_user)
author_resp = self.client.patch(f"/api/v1/cms/posts/{self.post.id}/", patch_payload, format="json")
self.assertEqual(author_resp.status_code, status.HTTP_200_OK)
self.assertEqual(author_resp.data["title"], patch_payload["title"])
def test_post_like_toggle(self):
self.client.force_authenticate(user=self.other_user)
# 1. Like
like_resp = self.client.post(f"/api/v1/cms/posts/{self.post.id}/like/")
self.assertEqual(like_resp.status_code, status.HTTP_200_OK)
self.assertTrue(like_resp.data["is_liked"])
self.assertEqual(like_resp.data["likes_count"], 1)
# Check detail includes is_liked = True
detail_resp = self.client.get(f"/api/v1/cms/posts/{self.post.id}/")
self.assertTrue(detail_resp.data["is_liked"])
# 2. Unlike
unlike_resp = self.client.post(f"/api/v1/cms/posts/{self.post.id}/like/")
self.assertEqual(unlike_resp.status_code, status.HTTP_200_OK)
self.assertFalse(unlike_resp.data["is_liked"])
self.assertEqual(unlike_resp.data["likes_count"], 0)
def test_post_comments_submission_and_listing(self):
# 1. Anonymous / Visitor Comment
comment_payload = {
"author_name": "Dr. Ali Sadr",
"author_email": "[email protected]",
"content": "Outstanding analysis on the foundational pillars of Islamic cultural diplomacy."
}
comment_resp = self.client.post(f"/api/v1/cms/posts/{self.post.id}/comments/", comment_payload, format="json")
self.assertEqual(comment_resp.status_code, status.HTTP_201_CREATED)
self.assertEqual(comment_resp.data["author_name"], "Dr. Ali Sadr")
# 2. Authenticated User Comment
self.client.force_authenticate(user=self.other_user)
auth_comment_payload = {
"content": "Very insightful article, thank you for sharing."
}
auth_comment_resp = self.client.post(f"/api/v1/cms/posts/{self.post.id}/comments/", auth_comment_payload, format="json")
self.assertEqual(auth_comment_resp.status_code, status.HTTP_201_CREATED)
# 3. List Comments
list_resp = self.client.get(f"/api/v1/cms/posts/{self.post.id}/comments/")
self.assertEqual(list_resp.status_code, status.HTTP_200_OK)
self.assertEqual(len(list_resp.data), 2)
def test_categories_and_tags_endpoints(self):
# 1. Categories
cat_resp = self.client.get("/api/v1/cms/categories/")
self.assertEqual(cat_resp.status_code, status.HTTP_200_OK)
self.assertGreaterEqual(len(cat_resp.data), 2)
# Filter by language
cat_lang_resp = self.client.get("/api/v1/cms/categories/?language=fa")
self.assertEqual(len(cat_lang_resp.data), 1)
self.assertEqual(cat_lang_resp.data[0]["name"], "دیپلماسی فرهنگی")
# 2. Tags
tag_resp = self.client.get("/api/v1/cms/tags/")
self.assertEqual(tag_resp.status_code, status.HTTP_200_OK)
self.assertGreaterEqual(len(tag_resp.data), 2)
def test_media_asset_upload_and_listing(self):
self.client.force_authenticate(user=self.author_user)
mock_file = SimpleUploadedFile("diplomacy_infographic.png", b"mock_png_image_data", content_type="image/png")
upload_resp = self.client.post(
"/api/v1/cms/media/",
{
"file": mock_file,
"title": "Razavi Cultural Map 2026",
"media_type": "image",
"dimensions": "1920x1080"
},
format="multipart"
)
self.assertEqual(upload_resp.status_code, status.HTTP_201_CREATED)
self.assertEqual(upload_resp.data["title"], "Razavi Cultural Map 2026")
self.assertEqual(upload_resp.data["uploaded_by_email"], self.author_user.email)
self.assertGreater(upload_resp.data["file_size"], 0)
# List Media Assets
list_resp = self.client.get("/api/v1/cms/media/")
self.assertEqual(list_resp.status_code, status.HTTP_200_OK)
self.assertEqual(len(list_resp.data), 1)