""" Test cases for Parks API following Django styleguide patterns. Comprehensive API endpoint testing with proper naming conventions. """ from django.urls import reverse from rest_framework.test import APITestCase, APIClient from rest_framework import status from parks.models import Park from tests.factories import ( 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.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)