""" 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)