Browse Source

feat(tests): add comprehensive test script for video and podcast API endpoints

- Introduced a new test script `test_apis.py` to verify all video and podcast API endpoints.
- Implemented a function to test individual endpoints with support for authenticated and anonymous users.
- Included detailed logging of test results, including status codes and errors, to facilitate debugging and ensure API reliability.
- Enhanced test coverage for both video and podcast APIs, ensuring all critical endpoints are validated.
master
mortezaei 6 months ago
parent
commit
67f91ee47e
  1. 23
      apps/bookmark/migrations/0005_auto_20251202_1245.py
  2. 4
      apps/bookmark/models/bookmark.py
  3. 4
      apps/bookmark/models/rate.py
  4. 52
      apps/podcast/views.py
  5. 206
      test_apis.py

23
apps/bookmark/migrations/0005_auto_20251202_1245.py

@ -0,0 +1,23 @@
# Generated by Django 3.2.4 on 2025-12-02 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bookmark', '0004_auto_20251130_1758'),
]
operations = [
migrations.AlterField(
model_name='bookmark',
name='service',
field=models.CharField(choices=[('library', 'Library'), ('podcast', 'Podcast'), ('podcast_playlist', 'Podcast Playlist'), ('hadith', 'Hadith'), ('video', 'Video'), ('video_playlist', 'Video Playlist'), ('article', 'Article')], max_length=20, verbose_name='Service'),
),
migrations.AlterField(
model_name='rate',
name='service',
field=models.CharField(choices=[('library', 'Library'), ('podcast', 'Podcast'), ('podcast_playlist', 'Podcast Playlist'), ('hadith', 'Hadith'), ('video', 'Video'), ('video_playlist', 'Video Playlist')], max_length=20, verbose_name='Service'),
),
]

4
apps/bookmark/models/bookmark.py

@ -13,6 +13,7 @@ class Bookmark(models.Model):
class ServiceChoices(models.TextChoices): class ServiceChoices(models.TextChoices):
LIBRARY = 'library', 'Library' LIBRARY = 'library', 'Library'
PODCAST = 'podcast', 'Podcast' PODCAST = 'podcast', 'Podcast'
PODCAST_PLAYLIST = 'podcast_playlist', 'Podcast Playlist'
HADITH = 'hadith', 'Hadith' HADITH = 'hadith', 'Hadith'
VIDEO = 'video', 'Video' VIDEO = 'video', 'Video'
VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist' VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist'
@ -71,6 +72,9 @@ class Bookmark(models.Model):
elif service == cls.ServiceChoices.PODCAST: elif service == cls.ServiceChoices.PODCAST:
from apps.podcast.models import Podcast from apps.podcast.models import Podcast
return Podcast.objects.filter(id=content_id).exists() return Podcast.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.PODCAST_PLAYLIST:
from apps.podcast.models import PodcastPlaylist
return PodcastPlaylist.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.HADITH: elif service == cls.ServiceChoices.HADITH:
from apps.hadith.models import Hadith from apps.hadith.models import Hadith
return Hadith.objects.filter(id=content_id).exists() return Hadith.objects.filter(id=content_id).exists()

4
apps/bookmark/models/rate.py

@ -15,6 +15,7 @@ class Rate(models.Model):
class ServiceChoices(models.TextChoices): class ServiceChoices(models.TextChoices):
LIBRARY = 'library', 'Library' LIBRARY = 'library', 'Library'
PODCAST = 'podcast', 'Podcast' PODCAST = 'podcast', 'Podcast'
PODCAST_PLAYLIST = 'podcast_playlist', 'Podcast Playlist'
HADITH = 'hadith', 'Hadith' HADITH = 'hadith', 'Hadith'
VIDEO = 'video', 'Video' VIDEO = 'video', 'Video'
VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist' VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist'
@ -88,6 +89,9 @@ class Rate(models.Model):
elif service == cls.ServiceChoices.PODCAST: elif service == cls.ServiceChoices.PODCAST:
from apps.podcast.models import Podcast from apps.podcast.models import Podcast
return Podcast.objects.filter(id=content_id).exists() return Podcast.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.PODCAST_PLAYLIST:
from apps.podcast.models import PodcastPlaylist
return PodcastPlaylist.objects.filter(id=content_id).exists()
elif service == cls.ServiceChoices.HADITH: elif service == cls.ServiceChoices.HADITH:
from apps.hadith.models import Hadith from apps.hadith.models import Hadith
return Hadith.objects.filter(id=content_id).exists() return Hadith.objects.filter(id=content_id).exists()

52
apps/podcast/views.py

@ -84,54 +84,47 @@ class MiddlePodcastCollectionListView(generics.ListAPIView):
class PodcastListAPIView(generics.ListAPIView): class PodcastListAPIView(generics.ListAPIView):
""" """
API view to list all podcasts, with optional filtering by category, collection, or user playlist
API view to list all podcast playlists, with optional filtering by category, collection
""" """
serializer_class = PodcastListSerializer
serializer_class = PodcastPlaylistListSerializer
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
@swagger_auto_schema( @swagger_auto_schema(
operation_description="Get a list of podcasts with optional filtering",
operation_description="Get a list of podcast playlists with optional filtering",
manual_parameters=[ manual_parameters=[
openapi.Parameter( openapi.Parameter(
name='category', name='category',
in_=openapi.IN_QUERY, in_=openapi.IN_QUERY,
description='Filter podcasts by category slug',
description='Filter playlists by category slug',
type=openapi.TYPE_STRING, type=openapi.TYPE_STRING,
required=False required=False
), ),
openapi.Parameter( openapi.Parameter(
name='collection', name='collection',
in_=openapi.IN_QUERY, in_=openapi.IN_QUERY,
description='Filter podcasts by collection slug',
description='Filter playlists by collection slug',
type=openapi.TYPE_STRING, type=openapi.TYPE_STRING,
required=False required=False
), ),
openapi.Parameter(
name='in_playlist',
in_=openapi.IN_QUERY,
description='Filter podcasts that are in the user\'s playlist (true/false)',
type=openapi.TYPE_BOOLEAN,
required=False
),
openapi.Parameter( openapi.Parameter(
name='is_bookmark', name='is_bookmark',
in_=openapi.IN_QUERY, in_=openapi.IN_QUERY,
description='Filter podcasts that are bookmarked by the user (true/false)',
description='Filter playlists that are bookmarked by the user (true/false)',
type=openapi.TYPE_BOOLEAN, type=openapi.TYPE_BOOLEAN,
required=False required=False
), ),
openapi.Parameter( openapi.Parameter(
name='search', name='search',
in_=openapi.IN_QUERY, in_=openapi.IN_QUERY,
description='Search podcasts by title',
description='Search playlists by title',
type=openapi.TYPE_STRING, type=openapi.TYPE_STRING,
required=False required=False
) )
], ],
responses={ responses={
200: openapi.Response( 200: openapi.Response(
description="List of podcasts",
schema=PodcastListSerializer(many=True)
description="List of podcast playlists",
schema=PodcastPlaylistListSerializer(many=True)
) )
} }
) )
@ -139,7 +132,7 @@ class PodcastListAPIView(generics.ListAPIView):
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
queryset = Podcast.objects.filter(status=True).order_by('-created_at')
queryset = PodcastPlaylist.objects.filter(status=True).order_by('-created_at')
# Search by title if search parameter is provided # Search by title if search parameter is provided
search_query = self.request.query_params.get('search', None) search_query = self.request.query_params.get('search', None)
@ -154,47 +147,36 @@ class PodcastListAPIView(generics.ListAPIView):
# Filter by collection if provided # Filter by collection if provided
collection_slug = self.request.query_params.get('collection', None) collection_slug = self.request.query_params.get('collection', None)
if collection_slug: if collection_slug:
# Get all podcasts that are in the collection with the given slug
queryset = queryset.filter( queryset = queryset.filter(
collections__slug=collection_slug collections__slug=collection_slug
) )
# Filter by user playlist if provided
in_playlist = self.request.query_params.get('in_playlist', None)
if in_playlist and in_playlist.lower() == 'true':
# Get podcasts that are in the user's playlist and active
user_playlist_podcasts = UserPlaylist.objects.filter(
user=self.request.user,
status=True
).values_list('podcast_id', flat=True)
queryset = queryset.filter(id__in=user_playlist_podcasts)
# Filter by bookmarks if provided # Filter by bookmarks if provided
is_bookmark = self.request.query_params.get('is_bookmark', '').lower() is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true': if is_bookmark == 'true':
# Import Bookmark model here to avoid circular imports
from apps.bookmark.models import Bookmark from apps.bookmark.models import Bookmark
# Get all bookmarked podcast IDs for the current user
bookmarked_ids = Bookmark.objects.filter( bookmarked_ids = Bookmark.objects.filter(
user=self.request.user, user=self.request.user,
service=Bookmark.ServiceChoices.PODCAST,
service=Bookmark.ServiceChoices.PODCAST_PLAYLIST,
status=True status=True
).values_list('content_id', flat=True) ).values_list('content_id', flat=True)
# Filter podcasts by these IDs
queryset = queryset.filter(id__in=bookmarked_ids) queryset = queryset.filter(id__in=bookmarked_ids)
return queryset return queryset
class PodcastDetailAPIView(generics.RetrieveAPIView): class PodcastDetailAPIView(generics.RetrieveAPIView):
serializer_class = PodcastDetailSerializer
"""
API view to retrieve details of a specific podcast playlist
"""
serializer_class = PodcastPlaylistDetailSerializer
lookup_field = 'slug' lookup_field = 'slug'
permission_classes = (IsAuthenticated,)
def get_queryset(self): def get_queryset(self):
return Podcast.objects.filter(status=True)
return PodcastPlaylist.objects.filter(status=True)
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()

206
test_apis.py

@ -0,0 +1,206 @@
#!/usr/bin/env python3
"""
Test script to verify all video and podcast API endpoints
"""
import os
import django
import sys
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
django.setup()
from django.test import RequestFactory
from django.contrib.auth import get_user_model
from apps.video.views import (
VideoCategoryListAPIView,
PinnedVideoCollectionListView,
MiddleVideoCollectionListView,
VideoPlaylistListAPIView,
VideoPlaylistDetailAPIView,
)
from apps.podcast.views import (
PodcastCategoryListAPIView,
PinnedPodcastCollectionListView,
MiddlePodcastCollectionListView,
PodcastListAPIView,
PodcastDetailAPIView,
)
from apps.video.models import VideoPlaylist
from apps.podcast.models import Podcast
User = get_user_model()
# Create a request factory
factory = RequestFactory()
def test_endpoint(view_class, url, method='GET', user=None, slug=None):
"""Test a single endpoint"""
try:
request = factory.get(url)
if user:
request.user = user
else:
# Create an anonymous user
from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
view = view_class.as_view()
if slug:
response = view(request, slug=slug)
else:
response = view(request)
status_code = response.status_code
return {
'status': 'SUCCESS' if status_code == 200 else 'FAILED',
'status_code': status_code,
'error': None
}
except Exception as e:
return {
'status': 'ERROR',
'status_code': None,
'error': str(e)
}
def main():
print("=" * 80)
print("TESTING VIDEO AND PODCAST APIs")
print("=" * 80)
# Get or create a test user
try:
user = User.objects.first()
if not user:
print("\n⚠️ No users found in database. Testing with anonymous user only.\n")
except Exception as e:
print(f"\n⚠️ Error getting user: {e}. Testing with anonymous user only.\n")
user = None
# VIDEO API TESTS
print("\n" + "=" * 80)
print("VIDEO APIs")
print("=" * 80)
video_tests = [
{
'name': 'Video Categories List',
'view': VideoCategoryListAPIView,
'url': '/api/videos/categories/',
'user': None
},
{
'name': 'Pinned Video Collections List',
'view': PinnedVideoCollectionListView,
'url': '/api/videos/pinned-collections/',
'user': user
},
{
'name': 'Middle Video Collections List',
'view': MiddleVideoCollectionListView,
'url': '/api/videos/collections/',
'user': user
},
{
'name': 'Video Playlists List',
'view': VideoPlaylistListAPIView,
'url': '/api/videos/playlists/',
'user': None
},
]
# Test detail endpoint if we have a playlist
try:
first_playlist = VideoPlaylist.objects.filter(status=True).first()
if first_playlist:
video_tests.append({
'name': f'Video Playlist Detail (slug: {first_playlist.slug})',
'view': VideoPlaylistDetailAPIView,
'url': f'/api/videos/playlists/{first_playlist.slug}/',
'user': None,
'slug': first_playlist.slug
})
except Exception as e:
print(f"\n⚠️ Could not fetch video playlist for detail test: {e}")
for test in video_tests:
result = test_endpoint(
test['view'],
test['url'],
user=test.get('user'),
slug=test.get('slug')
)
status_symbol = "" if result['status'] == 'SUCCESS' else ""
print(f"\n{status_symbol} {test['name']}")
print(f" URL: {test['url']}")
print(f" Status: {result['status']} (HTTP {result['status_code']})")
if result['error']:
print(f" Error: {result['error']}")
# PODCAST API TESTS
print("\n" + "=" * 80)
print("PODCAST APIs")
print("=" * 80)
podcast_tests = [
{
'name': 'Podcast Categories List',
'view': PodcastCategoryListAPIView,
'url': '/api/podcast/categories/',
'user': None
},
{
'name': 'Pinned Podcast Collections List',
'view': PinnedPodcastCollectionListView,
'url': '/api/podcast/pinned-collections/',
'user': user
},
{
'name': 'Middle Podcast Collections List',
'view': MiddlePodcastCollectionListView,
'url': '/api/podcast/collections/',
'user': user
},
{
'name': 'Podcasts List',
'view': PodcastListAPIView,
'url': '/api/podcast/list/',
'user': None
},
]
# Test detail endpoint if we have a podcast
try:
first_podcast = Podcast.objects.filter(status=True).first()
if first_podcast:
podcast_tests.append({
'name': f'Podcast Detail (slug: {first_podcast.slug})',
'view': PodcastDetailAPIView,
'url': f'/api/podcast/detail/{first_podcast.slug}/',
'user': None,
'slug': first_podcast.slug
})
except Exception as e:
print(f"\n⚠️ Could not fetch podcast for detail test: {e}")
for test in podcast_tests:
result = test_endpoint(
test['view'],
test['url'],
user=test.get('user'),
slug=test.get('slug')
)
status_symbol = "" if result['status'] == 'SUCCESS' else ""
print(f"\n{status_symbol} {test['name']}")
print(f" URL: {test['url']}")
print(f" Status: {result['status']} (HTTP {result['status_code']})")
if result['error']:
print(f" Error: {result['error']}")
print("\n" + "=" * 80)
print("TEST COMPLETE")
print("=" * 80 + "\n")
if __name__ == '__main__':
main()
Loading…
Cancel
Save