mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 16:51:07 -05:00
- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks. - Added tests for filtering, searching, and ordering parks in the API. - Created tests for error handling in the API, including malformed JSON and unsupported methods. - Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced. - Introduced utility mixins for API and model testing to streamline assertions and enhance test readability. - Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
358 lines
14 KiB
Python
358 lines
14 KiB
Python
"""
|
|
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 tests.factories import (
|
|
UserFactory, StaffUserFactory, CompanyFactory, ParkFactory,
|
|
TestScenarios
|
|
)
|
|
|
|
|
|
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.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")
|
|
]
|
|
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)
|
|
|
|
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)
|
|
|
|
# Check response structure
|
|
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'
|
|
]
|
|
|
|
for field in expected_fields:
|
|
self.assertIn(field, park_data)
|
|
|
|
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'})
|
|
|
|
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']
|
|
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'})
|
|
|
|
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')
|
|
|
|
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'})
|
|
|
|
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'])
|
|
|
|
|
|
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.park = ParkFactory(operator=self.company)
|
|
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)
|
|
|
|
# Check that detailed fields are included
|
|
detailed_fields = [
|
|
'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'})
|
|
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')
|
|
|
|
|
|
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.valid_park_data = {
|
|
'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')
|
|
|
|
# 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):
|
|
"""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
|
|
|
|
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):
|
|
"""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
|
|
})
|
|
|
|
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))
|
|
|
|
|
|
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.park = ParkFactory(operator=self.company)
|
|
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'
|
|
}
|
|
|
|
response = self.client.patch(self.url, update_data)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
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):
|
|
"""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
|
|
}
|
|
|
|
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'])
|
|
|
|
# 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')
|
|
|
|
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']
|
|
expected_fields = [
|
|
'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)
|
|
|
|
|
|
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')
|
|
|
|
response = self.client.post(
|
|
url,
|
|
data='{"invalid": json}',
|
|
content_type='application/json'
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
|
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})
|
|
|
|
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'])
|
|
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
|
|
}
|
|
|
|
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']
|
|
|
|
# 2. Retrieve park
|
|
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')
|
|
|
|
# 3. Update park
|
|
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'
|
|
)
|
|
|
|
# 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)
|