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):
LIBRARY = 'library', 'Library'
PODCAST = 'podcast', 'Podcast'
PODCAST_PLAYLIST = 'podcast_playlist', 'Podcast Playlist'
HADITH = 'hadith', 'Hadith'
VIDEO = 'video', 'Video'
VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist'
@ -71,6 +72,9 @@ class Bookmark(models.Model):
elif service == cls.ServiceChoices.PODCAST:
from apps.podcast.models import Podcast
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:
from apps.hadith.models import Hadith
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):
LIBRARY = 'library', 'Library'
PODCAST = 'podcast', 'Podcast'
PODCAST_PLAYLIST = 'podcast_playlist', 'Podcast Playlist'
HADITH = 'hadith', 'Hadith'
VIDEO = 'video', 'Video'
VIDEO_PLAYLIST = 'video_playlist', 'Video Playlist'
@ -88,6 +89,9 @@ class Rate(models.Model):
elif service == cls.ServiceChoices.PODCAST:
from apps.podcast.models import Podcast
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:
from apps.hadith.models import Hadith
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):
"""
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,)
@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=[
openapi.Parameter(
name='category',
in_=openapi.IN_QUERY,
description='Filter podcasts by category slug',
description='Filter playlists by category slug',
type=openapi.TYPE_STRING,
required=False
),
openapi.Parameter(
name='collection',
in_=openapi.IN_QUERY,
description='Filter podcasts by collection slug',
description='Filter playlists by collection slug',
type=openapi.TYPE_STRING,
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(
name='is_bookmark',
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,
required=False
),
openapi.Parameter(
name='search',
in_=openapi.IN_QUERY,
description='Search podcasts by title',
description='Search playlists by title',
type=openapi.TYPE_STRING,
required=False
)
],
responses={
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)
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_query = self.request.query_params.get('search', None)
@ -154,47 +147,36 @@ class PodcastListAPIView(generics.ListAPIView):
# Filter by collection if provided
collection_slug = self.request.query_params.get('collection', None)
if collection_slug:
# Get all podcasts that are in the collection with the given slug
queryset = queryset.filter(
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
is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true':
# Import Bookmark model here to avoid circular imports
from apps.bookmark.models import Bookmark
# Get all bookmarked podcast IDs for the current user
bookmarked_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.PODCAST,
service=Bookmark.ServiceChoices.PODCAST_PLAYLIST,
status=True
).values_list('content_id', flat=True)
# Filter podcasts by these IDs
queryset = queryset.filter(id__in=bookmarked_ids)
return queryset
class PodcastDetailAPIView(generics.RetrieveAPIView):
serializer_class = PodcastDetailSerializer
"""
API view to retrieve details of a specific podcast playlist
"""
serializer_class = PodcastPlaylistDetailSerializer
lookup_field = 'slug'
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return Podcast.objects.filter(status=True)
return PodcastPlaylist.objects.filter(status=True)
def retrieve(self, request, *args, **kwargs):
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