mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 13:11:08 -05:00
Refactor test utilities and enhance ASGI settings
- Cleaned up and standardized assertions in ApiTestMixin for API response validation. - Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE. - Removed unused imports and improved formatting in settings.py. - Refactored URL patterns in urls.py for better readability and organization. - Enhanced view functions in views.py for consistency and clarity. - Added .flake8 configuration for linting and style enforcement. - Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
@@ -3,355 +3,380 @@ Test cases for Parks API following Django styleguide patterns.
|
||||
Comprehensive API endpoint testing with proper naming conventions.
|
||||
"""
|
||||
|
||||
import json
|
||||
from decimal import Decimal
|
||||
from datetime import date
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from rest_framework import status
|
||||
|
||||
from parks.models import Park, Company
|
||||
from accounts.models import User
|
||||
from parks.models import Park
|
||||
from tests.factories import (
|
||||
UserFactory, StaffUserFactory, CompanyFactory, ParkFactory,
|
||||
TestScenarios
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
CompanyFactory,
|
||||
ParkFactory,
|
||||
)
|
||||
|
||||
|
||||
class TestParkListApi(APITestCase):
|
||||
"""Test cases for Park list API endpoint."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
self.user = UserFactory()
|
||||
self.company = CompanyFactory(roles=['OPERATOR'])
|
||||
self.company = CompanyFactory(roles=["OPERATOR"])
|
||||
self.parks = [
|
||||
ParkFactory(operator=self.company, name="Park A"),
|
||||
ParkFactory(operator=self.company, name="Park B", status='CLOSED_TEMP'),
|
||||
ParkFactory(operator=self.company, name="Park C")
|
||||
ParkFactory(operator=self.company, name="Park B", status="CLOSED_TEMP"),
|
||||
ParkFactory(operator=self.company, name="Park C"),
|
||||
]
|
||||
self.url = reverse('parks_api:park-list')
|
||||
|
||||
self.url = reverse("parks_api:park-list")
|
||||
|
||||
def test__park_list_api__unauthenticated_user__can_access(self):
|
||||
"""Test that unauthenticated users can access park list."""
|
||||
response = self.client.get(self.url)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], 'success')
|
||||
self.assertIsInstance(response.data['data'], list)
|
||||
|
||||
self.assertEqual(response.data["status"], "success")
|
||||
self.assertIsInstance(response.data["data"], list)
|
||||
|
||||
def test__park_list_api__returns_all_parks__in_correct_format(self):
|
||||
"""Test that park list returns all parks in correct format."""
|
||||
response = self.client.get(self.url)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['data']), 3)
|
||||
|
||||
self.assertEqual(len(response.data["data"]), 3)
|
||||
|
||||
# Check response structure
|
||||
park_data = response.data['data'][0]
|
||||
park_data = response.data["data"][0]
|
||||
expected_fields = [
|
||||
'id', 'name', 'slug', 'status', 'description',
|
||||
'average_rating', 'coaster_count', 'ride_count',
|
||||
'location', 'operator', 'created_at', 'updated_at'
|
||||
"id",
|
||||
"name",
|
||||
"slug",
|
||||
"status",
|
||||
"description",
|
||||
"average_rating",
|
||||
"coaster_count",
|
||||
"ride_count",
|
||||
"location",
|
||||
"operator",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
for field in expected_fields:
|
||||
self.assertIn(field, park_data)
|
||||
|
||||
def test__park_list_api__with_status_filter__returns_filtered_results(self):
|
||||
|
||||
def test__park_list_api__with_status_filter__returns_filtered_results(
|
||||
self,
|
||||
):
|
||||
"""Test that status filter works correctly."""
|
||||
response = self.client.get(self.url, {'status': 'OPERATING'})
|
||||
|
||||
response = self.client.get(self.url, {"status": "OPERATING"})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
# Should return only operating parks (2 out of 3)
|
||||
operating_parks = [p for p in response.data['data'] if p['status'] == 'OPERATING']
|
||||
operating_parks = [
|
||||
p for p in response.data["data"] if p["status"] == "OPERATING"
|
||||
]
|
||||
self.assertEqual(len(operating_parks), 2)
|
||||
|
||||
|
||||
def test__park_list_api__with_search_query__returns_matching_results(self):
|
||||
"""Test that search functionality works correctly."""
|
||||
response = self.client.get(self.url, {'search': 'Park A'})
|
||||
|
||||
response = self.client.get(self.url, {"search": "Park A"})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['data']), 1)
|
||||
self.assertEqual(response.data['data'][0]['name'], 'Park A')
|
||||
|
||||
self.assertEqual(len(response.data["data"]), 1)
|
||||
self.assertEqual(response.data["data"][0]["name"], "Park A")
|
||||
|
||||
def test__park_list_api__with_ordering__returns_ordered_results(self):
|
||||
"""Test that ordering functionality works correctly."""
|
||||
response = self.client.get(self.url, {'ordering': '-name'})
|
||||
|
||||
response = self.client.get(self.url, {"ordering": "-name"})
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
# Should be ordered by name descending (C, B, A)
|
||||
names = [park['name'] for park in response.data['data']]
|
||||
self.assertEqual(names, ['Park C', 'Park B', 'Park A'])
|
||||
names = [park["name"] for park in response.data["data"]]
|
||||
self.assertEqual(names, ["Park C", "Park B", "Park A"])
|
||||
|
||||
|
||||
class TestParkDetailApi(APITestCase):
|
||||
"""Test cases for Park detail API endpoint."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
self.company = CompanyFactory(roles=['OPERATOR'])
|
||||
self.company = CompanyFactory(roles=["OPERATOR"])
|
||||
self.park = ParkFactory(operator=self.company)
|
||||
self.url = reverse('parks_api:park-detail', kwargs={'slug': self.park.slug})
|
||||
|
||||
self.url = reverse("parks_api:park-detail", kwargs={"slug": self.park.slug})
|
||||
|
||||
def test__park_detail_api__with_valid_slug__returns_park_details(self):
|
||||
"""Test that park detail API returns correct park information."""
|
||||
response = self.client.get(self.url)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], 'success')
|
||||
|
||||
park_data = response.data['data']
|
||||
self.assertEqual(park_data['id'], self.park.id)
|
||||
self.assertEqual(park_data['name'], self.park.name)
|
||||
self.assertEqual(park_data['slug'], self.park.slug)
|
||||
|
||||
self.assertEqual(response.data["status"], "success")
|
||||
|
||||
park_data = response.data["data"]
|
||||
self.assertEqual(park_data["id"], self.park.id)
|
||||
self.assertEqual(park_data["name"], self.park.name)
|
||||
self.assertEqual(park_data["slug"], self.park.slug)
|
||||
|
||||
# Check that detailed fields are included
|
||||
detailed_fields = [
|
||||
'opening_date', 'closing_date', 'operating_season',
|
||||
'size_acres', 'website', 'areas', 'operator', 'property_owner'
|
||||
"opening_date",
|
||||
"closing_date",
|
||||
"operating_season",
|
||||
"size_acres",
|
||||
"website",
|
||||
"areas",
|
||||
"operator",
|
||||
"property_owner",
|
||||
]
|
||||
|
||||
|
||||
for field in detailed_fields:
|
||||
self.assertIn(field, park_data)
|
||||
|
||||
|
||||
def test__park_detail_api__with_invalid_slug__returns_404(self):
|
||||
"""Test that invalid slug returns 404 error."""
|
||||
invalid_url = reverse('parks_api:park-detail', kwargs={'slug': 'nonexistent'})
|
||||
invalid_url = reverse("parks_api:park-detail", kwargs={"slug": "nonexistent"})
|
||||
response = self.client.get(invalid_url)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertEqual(response.data['status'], 'error')
|
||||
self.assertEqual(response.data['error']['code'], 'NOT_FOUND')
|
||||
self.assertEqual(response.data["status"], "error")
|
||||
self.assertEqual(response.data["error"]["code"], "NOT_FOUND")
|
||||
|
||||
|
||||
class TestParkCreateApi(APITestCase):
|
||||
"""Test cases for Park creation API endpoint."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
self.user = UserFactory()
|
||||
self.staff_user = StaffUserFactory()
|
||||
self.company = CompanyFactory(roles=['OPERATOR'])
|
||||
self.url = reverse('parks_api:park-list') # POST to list endpoint
|
||||
|
||||
self.company = CompanyFactory(roles=["OPERATOR"])
|
||||
self.url = reverse("parks_api:park-list") # POST to list endpoint
|
||||
|
||||
self.valid_park_data = {
|
||||
'name': 'New Test Park',
|
||||
'description': 'A test park for API testing',
|
||||
'operator_id': self.company.id,
|
||||
'status': 'OPERATING'
|
||||
"name": "New Test Park",
|
||||
"description": "A test park for API testing",
|
||||
"operator_id": self.company.id,
|
||||
"status": "OPERATING",
|
||||
}
|
||||
|
||||
|
||||
def test__park_create_api__unauthenticated_user__returns_401(self):
|
||||
"""Test that unauthenticated users cannot create parks."""
|
||||
response = self.client.post(self.url, self.valid_park_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
|
||||
def test__park_create_api__authenticated_user__can_create_park(self):
|
||||
"""Test that authenticated users can create parks."""
|
||||
self.client.force_authenticate(user=self.user)
|
||||
response = self.client.post(self.url, self.valid_park_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(response.data['status'], 'success')
|
||||
|
||||
self.assertEqual(response.data["status"], "success")
|
||||
|
||||
# Verify park was created
|
||||
park_data = response.data['data']
|
||||
self.assertEqual(park_data['name'], 'New Test Park')
|
||||
self.assertTrue(Park.objects.filter(name='New Test Park').exists())
|
||||
|
||||
def test__park_create_api__with_invalid_data__returns_validation_errors(self):
|
||||
park_data = response.data["data"]
|
||||
self.assertEqual(park_data["name"], "New Test Park")
|
||||
self.assertTrue(Park.objects.filter(name="New Test Park").exists())
|
||||
|
||||
def test__park_create_api__with_invalid_data__returns_validation_errors(
|
||||
self,
|
||||
):
|
||||
"""Test that invalid data returns proper validation errors."""
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
|
||||
invalid_data = self.valid_park_data.copy()
|
||||
invalid_data['name'] = '' # Empty name should be invalid
|
||||
|
||||
invalid_data["name"] = "" # Empty name should be invalid
|
||||
|
||||
response = self.client.post(self.url, invalid_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data['status'], 'error')
|
||||
self.assertIn('name', response.data['error']['details'])
|
||||
|
||||
def test__park_create_api__with_invalid_date_range__returns_validation_error(self):
|
||||
self.assertEqual(response.data["status"], "error")
|
||||
self.assertIn("name", response.data["error"]["details"])
|
||||
|
||||
def test__park_create_api__with_invalid_date_range__returns_validation_error(
|
||||
self,
|
||||
):
|
||||
"""Test that invalid date ranges are caught by validation."""
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
|
||||
invalid_data = self.valid_park_data.copy()
|
||||
invalid_data.update({
|
||||
'opening_date': '2020-06-01',
|
||||
'closing_date': '2020-05-01' # Before opening date
|
||||
})
|
||||
|
||||
invalid_data.update(
|
||||
{
|
||||
"opening_date": "2020-06-01",
|
||||
"closing_date": "2020-05-01", # Before opening date
|
||||
}
|
||||
)
|
||||
|
||||
response = self.client.post(self.url, invalid_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn('Closing date cannot be before opening date', str(response.data))
|
||||
self.assertIn("Closing date cannot be before opening date", str(response.data))
|
||||
|
||||
|
||||
class TestParkUpdateApi(APITestCase):
|
||||
"""Test cases for Park update API endpoint."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
self.user = UserFactory()
|
||||
self.company = CompanyFactory(roles=['OPERATOR'])
|
||||
self.company = CompanyFactory(roles=["OPERATOR"])
|
||||
self.park = ParkFactory(operator=self.company)
|
||||
self.url = reverse('parks_api:park-detail', kwargs={'slug': self.park.slug})
|
||||
|
||||
self.url = reverse("parks_api:park-detail", kwargs={"slug": self.park.slug})
|
||||
|
||||
def test__park_update_api__authenticated_user__can_update_park(self):
|
||||
"""Test that authenticated users can update parks."""
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
|
||||
update_data = {
|
||||
'name': 'Updated Park Name',
|
||||
'description': 'Updated description'
|
||||
"name": "Updated Park Name",
|
||||
"description": "Updated description",
|
||||
}
|
||||
|
||||
|
||||
response = self.client.patch(self.url, update_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], 'success')
|
||||
|
||||
self.assertEqual(response.data["status"], "success")
|
||||
|
||||
# Verify park was updated
|
||||
self.park.refresh_from_db()
|
||||
self.assertEqual(self.park.name, 'Updated Park Name')
|
||||
self.assertEqual(self.park.description, 'Updated description')
|
||||
|
||||
def test__park_update_api__with_invalid_data__returns_validation_errors(self):
|
||||
self.assertEqual(self.park.name, "Updated Park Name")
|
||||
self.assertEqual(self.park.description, "Updated description")
|
||||
|
||||
def test__park_update_api__with_invalid_data__returns_validation_errors(
|
||||
self,
|
||||
):
|
||||
"""Test that invalid update data returns validation errors."""
|
||||
self.client.force_authenticate(user=self.user)
|
||||
|
||||
|
||||
invalid_data = {
|
||||
'opening_date': '2020-06-01',
|
||||
'closing_date': '2020-05-01' # Invalid date range
|
||||
"opening_date": "2020-06-01",
|
||||
"closing_date": "2020-05-01", # Invalid date range
|
||||
}
|
||||
|
||||
|
||||
response = self.client.patch(self.url, invalid_data)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class TestParkStatsApi(APITestCase):
|
||||
"""Test cases for Park statistics API endpoint."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
self.company = CompanyFactory(roles=['OPERATOR'])
|
||||
|
||||
self.company = CompanyFactory(roles=["OPERATOR"])
|
||||
|
||||
# Create parks with different statuses
|
||||
ParkFactory(operator=self.company, status='OPERATING')
|
||||
ParkFactory(operator=self.company, status='OPERATING')
|
||||
ParkFactory(operator=self.company, status='CLOSED_TEMP')
|
||||
|
||||
self.url = reverse('parks_api:park-stats')
|
||||
|
||||
ParkFactory(operator=self.company, status="OPERATING")
|
||||
ParkFactory(operator=self.company, status="OPERATING")
|
||||
ParkFactory(operator=self.company, status="CLOSED_TEMP")
|
||||
|
||||
self.url = reverse("parks_api:park-stats")
|
||||
|
||||
def test__park_stats_api__returns_correct_statistics(self):
|
||||
"""Test that park statistics API returns correct data."""
|
||||
response = self.client.get(self.url)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['status'], 'success')
|
||||
|
||||
stats = response.data['data']
|
||||
self.assertEqual(response.data["status"], "success")
|
||||
|
||||
stats = response.data["data"]
|
||||
expected_fields = [
|
||||
'total_parks', 'operating_parks', 'closed_parks',
|
||||
'under_construction', 'average_rating', 'recently_added_count'
|
||||
"total_parks",
|
||||
"operating_parks",
|
||||
"closed_parks",
|
||||
"under_construction",
|
||||
"average_rating",
|
||||
"recently_added_count",
|
||||
]
|
||||
|
||||
|
||||
for field in expected_fields:
|
||||
self.assertIn(field, stats)
|
||||
|
||||
|
||||
# Verify counts are correct
|
||||
self.assertEqual(stats['total_parks'], 3)
|
||||
self.assertEqual(stats['operating_parks'], 2)
|
||||
self.assertEqual(stats["total_parks"], 3)
|
||||
self.assertEqual(stats["operating_parks"], 2)
|
||||
|
||||
|
||||
class TestParkApiErrorHandling(APITestCase):
|
||||
"""Test cases for Park API error handling."""
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.client = APIClient()
|
||||
|
||||
|
||||
def test__park_api__with_malformed_json__returns_parse_error(self):
|
||||
"""Test that malformed JSON returns proper error."""
|
||||
url = reverse('parks_api:park-list')
|
||||
|
||||
url = reverse("parks_api:park-list")
|
||||
|
||||
response = self.client.post(
|
||||
url,
|
||||
data='{"invalid": json}',
|
||||
content_type='application/json'
|
||||
url, data='{"invalid": json}', content_type="application/json"
|
||||
)
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data['status'], 'error')
|
||||
|
||||
self.assertEqual(response.data["status"], "error")
|
||||
|
||||
def test__park_api__with_unsupported_method__returns_405(self):
|
||||
"""Test that unsupported HTTP methods return 405."""
|
||||
park = ParkFactory()
|
||||
url = reverse('parks_api:park-detail', kwargs={'slug': park.slug})
|
||||
|
||||
url = reverse("parks_api:park-detail", kwargs={"slug": park.slug})
|
||||
|
||||
response = self.client.head(url) # HEAD not supported
|
||||
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
|
||||
|
||||
class TestParkApiIntegration(APITestCase):
|
||||
"""Integration tests for Park API with complete scenarios."""
|
||||
|
||||
|
||||
def test__complete_park_workflow__create_update_retrieve_delete(self):
|
||||
"""Test complete CRUD workflow for parks."""
|
||||
user = UserFactory()
|
||||
company = CompanyFactory(roles=['OPERATOR'])
|
||||
company = CompanyFactory(roles=["OPERATOR"])
|
||||
self.client.force_authenticate(user=user)
|
||||
|
||||
|
||||
# 1. Create park
|
||||
create_data = {
|
||||
'name': 'Integration Test Park',
|
||||
'description': 'A park for integration testing',
|
||||
'operator_id': company.id
|
||||
"name": "Integration Test Park",
|
||||
"description": "A park for integration testing",
|
||||
"operator_id": company.id,
|
||||
}
|
||||
|
||||
create_response = self.client.post(
|
||||
reverse('parks_api:park-list'),
|
||||
create_data
|
||||
)
|
||||
|
||||
|
||||
create_response = self.client.post(reverse("parks_api:park-list"), create_data)
|
||||
|
||||
self.assertEqual(create_response.status_code, status.HTTP_201_CREATED)
|
||||
park_slug = create_response.data['data']['slug']
|
||||
|
||||
park_slug = create_response.data["data"]["slug"]
|
||||
|
||||
# 2. Retrieve park
|
||||
detail_url = reverse('parks_api:park-detail', kwargs={'slug': park_slug})
|
||||
detail_url = reverse("parks_api:park-detail", kwargs={"slug": park_slug})
|
||||
retrieve_response = self.client.get(detail_url)
|
||||
|
||||
|
||||
self.assertEqual(retrieve_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(retrieve_response.data['data']['name'], 'Integration Test Park')
|
||||
|
||||
self.assertEqual(
|
||||
retrieve_response.data["data"]["name"], "Integration Test Park"
|
||||
)
|
||||
|
||||
# 3. Update park
|
||||
update_data = {'description': 'Updated integration test description'}
|
||||
update_data = {"description": "Updated integration test description"}
|
||||
update_response = self.client.patch(detail_url, update_data)
|
||||
|
||||
|
||||
self.assertEqual(update_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
update_response.data['data']['description'],
|
||||
'Updated integration test description'
|
||||
update_response.data["data"]["description"],
|
||||
"Updated integration test description",
|
||||
)
|
||||
|
||||
|
||||
# 4. Delete park
|
||||
delete_response = self.client.delete(detail_url)
|
||||
|
||||
|
||||
self.assertEqual(delete_response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
# 5. Verify park is deleted
|
||||
verify_response = self.client.get(detail_url)
|
||||
self.assertEqual(verify_response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
Reference in New Issue
Block a user