diff --git a/apps/hadis/tests.py b/apps/hadis/tests.py index 7ce503c..2cfff0c 100644 --- a/apps/hadis/tests.py +++ b/apps/hadis/tests.py @@ -1,3 +1,335 @@ -from django.test import TestCase +from django.test import TestCase, override_settings +from django.urls import reverse, resolve +from rest_framework.test import APITestCase +from rest_framework import status +from django.test.utils import override_settings -# Create your tests here. + +class HadisAPIConnectivityTests(APITestCase): + """ + Test suite for hadis app API endpoints connectivity. + Tests that all endpoints return proper HTTP responses. + """ + + # Comment out fixtures for now to avoid migration issues + # fixtures = [ + # 'backend/apps/hadis/fixtures/new_categories.json', + # 'backend/apps/hadis/fixtures/hadises1_reformatted.json', + # 'backend/apps/hadis/fixtures/transmitters_reformatted.json', + # ] + + def test_collections_endpoint(self): + """Test collections endpoint returns 200""" + url = reverse('hadis-collection-list') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_sync_sects_endpoint(self): + """Test sync sects endpoint returns 200""" + url = reverse('hadis-sect-list') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_sync_categories_tree_endpoint(self): + """Test sync categories tree endpoint returns 200""" + url = reverse('hadis-category-tree') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_sync_hadis_endpoint(self): + """Test sync hadis endpoint returns 200""" + url = reverse('hadis-sync') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_sync_narrators_endpoint(self): + """Test sync narrators endpoint returns 200""" + url = reverse('transmitter-sync') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_sync_references_endpoint(self): + """Test sync references endpoint returns 200""" + url = reverse('reference-sync') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_info_endpoint(self): + """Test info endpoint returns 200""" + url = reverse('hadis-info') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_categories_tree_normal_endpoint(self): + """Test categories tree normal endpoint returns 200""" + url = reverse('hadis-category-tree-normal') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_categories_endpoint(self): + """Test categories endpoint returns 200""" + url = reverse('categories') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_categories_by_sect_endpoint(self): + """Test categories by sect endpoint is accessible""" + # Using a common sect type + url = reverse('categories-by-sect', kwargs={'sect_type': '1'}) + response = self.client.get(url) + # May return 200 if data exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_categories_by_sect_with_slug_endpoint(self): + """Test categories by sect with slug endpoint is accessible""" + # Using common parameters + url = reverse('categories-tree-by-sect', kwargs={ + 'sect_type': '1', + 'slug': 'test-category' + }) + response = self.client.get(url) + # May return 200 if data exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_categories_by_sect_source_endpoint(self): + """Test categories by sect source endpoint is accessible""" + # Using common parameters + url = reverse('categories-tree-by-sect-source', kwargs={ + 'sect_type': '1', + 'slug': 'test-category', + 'source_type': 'quran' + }) + response = self.client.get(url) + # May return 200 if data exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_hadis_main_list_endpoint(self): + """Test hadis main list (arguments) endpoint returns 200""" + url = reverse('hadis-main-list') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_hadis_filters_endpoint(self): + """Test hadis filters endpoint returns 200""" + url = reverse('hadis-filters') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_narrators_endpoint(self): + """Test narrators endpoint returns 200""" + url = reverse('narrators') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_narrator_filters_endpoint(self): + """Test narrator filters endpoint returns 200""" + url = reverse('narrator-filters') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_narrator_detail_endpoint(self): + """Test narrator detail endpoint is accessible""" + # Using a test narrator slug + url = reverse('narrator-detail', kwargs={'narrator_slug': 'test-narrator'}) + response = self.client.get(url) + # May return 200 if narrator exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_narrator_opinions_endpoint(self): + """Test narrator opinions endpoint is accessible""" + # Using a test narrator slug + url = reverse('narrator-opinions', kwargs={'narrator_slug': 'test-narrator'}) + response = self.client.get(url) + # May return 200 if narrator exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_narrator_original_texts_endpoint(self): + """Test narrator original texts endpoint is accessible""" + # Using a test narrator slug + url = reverse('narrator-original-texts', kwargs={'narrator_slug': 'test-narrator'}) + response = self.client.get(url) + # May return 200 if narrator exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_references_endpoint(self): + """Test references endpoint returns 200""" + url = reverse('references') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_category_hadis_list_endpoint(self): + """Test category hadis list endpoint is accessible""" + # Using a test category slug + url = reverse('hadis-list', kwargs={'category_slug': 'test-category'}) + response = self.client.get(url) + # May return 200 if category exists, or 404 if not - but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + # Parameterized endpoints that may not have data - these might return 404 but should still be accessible + def test_reference_detail_endpoint(self): + """Test reference detail endpoint is accessible (may return 404 if no data)""" + url = reverse('reference-detail', kwargs={'reference_slug': 'test-reference'}) + response = self.client.get(url) + # This might return 404 if reference doesn't exist, but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_hadis_basic_endpoint(self): + """Test hadis basic endpoint is accessible (may return 404 if no data)""" + url = reverse('hadis-basic', kwargs={'hadis_slug': 'test-hadis'}) + response = self.client.get(url) + # This might return 404 if hadis doesn't exist, but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_hadis_detail_endpoint(self): + """Test hadis detail endpoint is accessible (may return 404 if no data)""" + url = reverse('hadis-detail', kwargs={'hadis_slug': 'test-hadis'}) + response = self.client.get(url) + # This might return 404 if hadis doesn't exist, but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_hadis_transmitters_endpoint(self): + """Test hadis transmitters endpoint is accessible (may return 404 if no data)""" + url = reverse('hadis-transmitters', kwargs={'hadis_slug': 'test-hadis'}) + response = self.client.get(url) + # This might return 404 if hadis doesn't exist, but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + def test_hadis_corrections_endpoint(self): + """Test hadis corrections endpoint is accessible (may return 404 if no data)""" + url = reverse('hadis-corrections', kwargs={'hadis_slug': 'test-hadis'}) + response = self.client.get(url) + # This might return 404 if hadis doesn't exist, but endpoint should be accessible + self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) + + +class HadisAPIResponseStructureTests(APITestCase): + """ + Additional tests to ensure API responses have proper structure + """ + + # Comment out fixtures for now to avoid migration issues + # fixtures = [ + # 'backend/apps/hadis/fixtures/new_categories.json', + # 'backend/apps/hadis/fixtures/hadises1_reformatted.json', + # 'backend/apps/hadis/fixtures/transmitters_reformatted.json', + # ] + + def test_api_returns_json_content_type(self): + """Test that API endpoints return JSON content type""" + endpoints = [ + reverse('hadis-collection-list'), + reverse('hadis-info'), + reverse('categories'), + reverse('hadis-main-list'), + reverse('narrators'), + ] + + for url in endpoints: + with self.subTest(url=url): + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response['Content-Type'], 'application/json') + + def test_api_endpoints_are_cached(self): + """Test that API endpoints have cache headers (from cached_view decorator)""" + url = reverse('hadis-info') + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Check if cache headers are present (this indicates caching is working) + cache_headers = ['Cache-Control', 'Expires', 'ETag'] + has_cache_header = any(header in response for header in cache_headers) + self.assertTrue(has_cache_header, "API endpoint should have cache headers") + + +class HadisURLResolutionTests(TestCase): + """ + Test that all hadis URLs are properly configured and resolvable. + These tests don't require database access. + """ + + def test_all_url_names_are_resolvable(self): + """Test that all URL names can be reversed without parameters""" + url_names = [ + 'hadis-collection-list', + 'hadis-sect-list', + 'hadis-category-tree', + 'hadis-sync', + 'transmitter-sync', + 'reference-sync', + 'hadis-info', + 'hadis-category-tree-normal', + 'categories', + 'hadis-main-list', + 'hadis-filters', + 'narrator-filters', + 'narrators', + 'references', + ] + + for url_name in url_names: + with self.subTest(url_name=url_name): + try: + url = reverse(url_name) + # If we get here, the URL is resolvable + self.assertIsInstance(url, str) + self.assertTrue(url.startswith('/')) + except Exception as e: + self.fail(f"URL name '{url_name}' could not be reversed: {e}") + + def test_parameterized_url_names_are_resolvable(self): + """Test that parameterized URL names can be reversed with test parameters""" + parameterized_urls = [ + ('categories-by-sect', {'sect_type': '1'}), + ('categories-tree-by-sect', {'sect_type': '1', 'slug': 'test'}), + ('categories-tree-by-sect-source', {'sect_type': '1', 'slug': 'test', 'source_type': 'quran'}), + ('hadis-list', {'category_slug': 'test'}), + ('narrator-detail', {'narrator_slug': 'test'}), + ('narrator-opinions', {'narrator_slug': 'test'}), + ('narrator-original-texts', {'narrator_slug': 'test'}), + ('reference-detail', {'reference_slug': 'test'}), + ('hadis-basic', {'hadis_slug': 'test'}), + ('hadis-detail', {'hadis_slug': 'test'}), + ('hadis-transmitters', {'hadis_slug': 'test'}), + ('hadis-corrections', {'hadis_slug': 'test'}), + ] + + for url_name, kwargs in parameterized_urls: + with self.subTest(url_name=url_name): + try: + url = reverse(url_name, kwargs=kwargs) + # If we get here, the URL is resolvable + self.assertIsInstance(url, str) + self.assertTrue(url.startswith('/')) + except Exception as e: + self.fail(f"URL name '{url_name}' could not be reversed with kwargs {kwargs}: {e}") + + def test_urls_resolve_to_correct_views(self): + """Test that URLs resolve to the correct view functions/classes""" + test_urls = [ + ('/hadis/collections/', 'hadis-collection-list'), + ('/hadis/sync/sects/', 'hadis-sect-list'), + ('/hadis/sync/categories/tree/', 'hadis-category-tree'), + ('/hadis/sync/hadis/', 'hadis-sync'), + ('/hadis/sync/narrators/', 'transmitter-sync'), + ('/hadis/sync/references/', 'reference-sync'), + ('/hadis/info/', 'hadis-info'), + ('/hadis/categories/tree/', 'hadis-category-tree-normal'), + ('/hadis/categories/', 'categories'), + ('/hadis/arguments/', 'hadis-main-list'), + ('/hadis/arguments/filters/', 'hadis-filters'), + ('/hadis/narrators/filters/', 'narrator-filters'), + ('/hadis/narrators/', 'narrators'), + ('/hadis/references/', 'references'), + ] + + for url_path, expected_url_name in test_urls: + with self.subTest(url_path=url_path): + try: + resolved = resolve(url_path) + # Check that the URL resolves + self.assertIsNotNone(resolved) + # Check that we can reverse back to the same URL + reversed_url = reverse(expected_url_name) + self.assertEqual(url_path, reversed_url) + except Exception as e: + self.fail(f"URL '{url_path}' could not be resolved: {e}") diff --git a/test_hadis_urls.py b/test_hadis_urls.py new file mode 100644 index 0000000..5cf0e5b --- /dev/null +++ b/test_hadis_urls.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +""" +Simple script to test hadis API endpoints connectivity. +This script manually tests all hadis URLs to verify they return proper HTTP status codes. +""" + +import os +import sys +import django +from django.conf import settings +from django.test import override_settings + +# Setup Django +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.base') +django.setup() + +from django.test import Client +from django.urls import reverse +from rest_framework import status + + +def test_endpoint(client, url_name, kwargs=None, description=""): + """Test a single endpoint and return the result""" + try: + if kwargs: + url = reverse(url_name, kwargs=kwargs) + else: + url = reverse(url_name) + + response = client.get(url) + status_code = response.status_code + + if status_code in [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]: + result = "โœ… PASS" + else: + result = f"โŒ FAIL ({status_code})" + + print(f"{result} {description}: {url} -> {status_code}") + return status_code in [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND] + + except Exception as e: + print(f"โŒ ERROR {description}: {url_name} -> {str(e)}") + return False + + +def main(): + """Main test function""" + print("๐Ÿงช Testing Hadis API Endpoints Connectivity") + print("=" * 60) + + # Override ALLOWED_HOSTS for testing + with override_settings(ALLOWED_HOSTS=['testserver']): + client = Client() + total_tests = 0 + passed_tests = 0 + + # Test non-parameterized endpoints + print("\n๐Ÿ“‹ Testing non-parameterized endpoints:") + non_param_endpoints = [ + ('hadis-collection-list', None, 'Collections'), + ('hadis-sect-list', None, 'Sync Sects'), + ('hadis-category-tree', None, 'Sync Categories Tree'), + ('hadis-sync', None, 'Sync Hadis'), + ('transmitter-sync', None, 'Sync Narrators'), + ('reference-sync', None, 'Sync References'), + ('hadis-info', None, 'Info'), + ('hadis-category-tree-normal', None, 'Categories Tree Normal'), + ('categories', None, 'Categories'), + ('hadis-main-list', None, 'Hadis Main List (Arguments)'), + ('hadis-filters', None, 'Hadis Filters'), + ('narrator-filters', None, 'Narrator Filters'), + ('narrators', None, 'Narrators'), + ('references', None, 'References'), + ] + + for url_name, kwargs, description in non_param_endpoints: + total_tests += 1 + if test_endpoint(client, url_name, kwargs, description): + passed_tests += 1 + + # Test parameterized endpoints + print("\n๐Ÿ“‹ Testing parameterized endpoints:") + param_endpoints = [ + ('categories-by-sect', {'sect_type': '1'}, 'Categories by Sect (type=1)'), + ('categories-tree-by-sect', {'sect_type': '1', 'slug': 'test-category'}, 'Categories Tree by Sect'), + ('categories-tree-by-sect-source', {'sect_type': '1', 'slug': 'test-category', 'source_type': 'quran'}, 'Categories Tree by Sect Source'), + ('hadis-list', {'category_slug': 'test-category'}, 'Hadis List by Category'), + ('narrator-detail', {'narrator_slug': 'test-narrator'}, 'Narrator Detail'), + ('narrator-opinions', {'narrator_slug': 'test-narrator'}, 'Narrator Opinions'), + ('narrator-original-texts', {'narrator_slug': 'test-narrator'}, 'Narrator Original Texts'), + ('reference-detail', {'reference_slug': 'test-reference'}, 'Reference Detail'), + ('hadis-basic', {'hadis_slug': 'test-hadis'}, 'Hadis Basic'), + ('hadis-detail', {'hadis_slug': 'test-hadis'}, 'Hadis Detail'), + ('hadis-transmitters', {'hadis_slug': 'test-hadis'}, 'Hadis Transmitters'), + ('hadis-corrections', {'hadis_slug': 'test-hadis'}, 'Hadis Corrections'), + ] + + for url_name, kwargs, description in param_endpoints: + total_tests += 1 + if test_endpoint(client, url_name, kwargs, description): + passed_tests += 1 + + # Summary + print("\n" + "=" * 60) + print(f"๐Ÿ“Š Test Results: {passed_tests}/{total_tests} endpoints accessible") + success_rate = (passed_tests / total_tests) * 100 if total_tests > 0 else 0 + print(f"๐Ÿ“ˆ Success Rate: {success_rate:.1f}%") + if passed_tests == total_tests: + print("โœ… All endpoints are properly configured and accessible!") + else: + print("โš ๏ธ Some endpoints may have issues") + + return passed_tests == total_tests + + +if __name__ == '__main__': + success = main() + sys.exit(0 if success else 1)