feat: Implement initial schema and add various API, service, and management command enhancements across the application.

This commit is contained in:
pacnpal
2026-01-01 15:13:01 -05:00
parent c95f99ca10
commit b243b17af7
413 changed files with 11164 additions and 17433 deletions

View File

@@ -32,63 +32,47 @@ class TestLoginAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.user.set_password('testpass123')
self.user.set_password("testpass123")
self.user.save()
self.url = '/api/v1/auth/login/'
self.url = "/api/v1/auth/login/"
def test__login__with_valid_credentials__returns_tokens(self):
"""Test successful login returns JWT tokens."""
response = self.client.post(self.url, {
'username': self.user.username,
'password': 'testpass123'
})
response = self.client.post(self.url, {"username": self.user.username, "password": "testpass123"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('access', response.data)
self.assertIn('refresh', response.data)
self.assertIn('user', response.data)
self.assertIn("access", response.data)
self.assertIn("refresh", response.data)
self.assertIn("user", response.data)
def test__login__with_email__returns_tokens(self):
"""Test login with email instead of username."""
response = self.client.post(self.url, {
'username': self.user.email,
'password': 'testpass123'
})
response = self.client.post(self.url, {"username": self.user.email, "password": "testpass123"})
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test__login__with_invalid_password__returns_400(self):
"""Test login with wrong password returns error."""
response = self.client.post(self.url, {
'username': self.user.username,
'password': 'wrongpassword'
})
response = self.client.post(self.url, {"username": self.user.username, "password": "wrongpassword"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn('error', response.data)
self.assertIn("detail", response.data)
def test__login__with_nonexistent_user__returns_400(self):
"""Test login with nonexistent username returns error."""
response = self.client.post(self.url, {
'username': 'nonexistentuser',
'password': 'testpass123'
})
response = self.client.post(self.url, {"username": "nonexistentuser", "password": "testpass123"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__login__with_missing_username__returns_400(self):
"""Test login without username returns error."""
response = self.client.post(self.url, {
'password': 'testpass123'
})
response = self.client.post(self.url, {"password": "testpass123"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__login__with_missing_password__returns_400(self):
"""Test login without password returns error."""
response = self.client.post(self.url, {
'username': self.user.username
})
response = self.client.post(self.url, {"username": self.user.username})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -103,10 +87,7 @@ class TestLoginAPIView(EnhancedAPITestCase):
self.user.is_active = False
self.user.save()
response = self.client.post(self.url, {
'username': self.user.username,
'password': 'testpass123'
})
response = self.client.post(self.url, {"username": self.user.username, "password": "testpass123"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -117,12 +98,12 @@ class TestSignupAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.url = '/api/v1/auth/signup/'
self.url = "/api/v1/auth/signup/"
self.valid_data = {
'username': 'newuser',
'email': 'newuser@example.com',
'password1': 'ComplexPass123!',
'password2': 'ComplexPass123!'
"username": "newuser",
"email": "newuser@example.com",
"password1": "ComplexPass123!",
"password2": "ComplexPass123!",
}
def test__signup__with_valid_data__creates_user(self):
@@ -133,10 +114,10 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_existing_username__returns_400(self):
"""Test signup with existing username returns error."""
UserFactory(username='existinguser')
UserFactory(username="existinguser")
data = self.valid_data.copy()
data['username'] = 'existinguser'
data["username"] = "existinguser"
response = self.client.post(self.url, data)
@@ -144,10 +125,10 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_existing_email__returns_400(self):
"""Test signup with existing email returns error."""
UserFactory(email='existing@example.com')
UserFactory(email="existing@example.com")
data = self.valid_data.copy()
data['email'] = 'existing@example.com'
data["email"] = "existing@example.com"
response = self.client.post(self.url, data)
@@ -156,7 +137,7 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_password_mismatch__returns_400(self):
"""Test signup with mismatched passwords returns error."""
data = self.valid_data.copy()
data['password2'] = 'DifferentPass123!'
data["password2"] = "DifferentPass123!"
response = self.client.post(self.url, data)
@@ -165,8 +146,8 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_weak_password__returns_400(self):
"""Test signup with weak password returns error."""
data = self.valid_data.copy()
data['password1'] = '123'
data['password2'] = '123'
data["password1"] = "123"
data["password2"] = "123"
response = self.client.post(self.url, data)
@@ -175,7 +156,7 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_invalid_email__returns_400(self):
"""Test signup with invalid email returns error."""
data = self.valid_data.copy()
data['email'] = 'notanemail'
data["email"] = "notanemail"
response = self.client.post(self.url, data)
@@ -183,7 +164,7 @@ class TestSignupAPIView(EnhancedAPITestCase):
def test__signup__with_missing_fields__returns_400(self):
"""Test signup with missing required fields returns error."""
response = self.client.post(self.url, {'username': 'onlyusername'})
response = self.client.post(self.url, {"username": "onlyusername"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -195,7 +176,7 @@ class TestLogoutAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/logout/'
self.url = "/api/v1/auth/logout/"
def test__logout__authenticated_user__returns_success(self):
"""Test successful logout for authenticated user."""
@@ -204,7 +185,7 @@ class TestLogoutAPIView(EnhancedAPITestCase):
response = self.client.post(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('message', response.data)
self.assertIn("detail", response.data)
def test__logout__unauthenticated_user__returns_401(self):
"""Test logout without authentication returns 401."""
@@ -217,7 +198,7 @@ class TestLogoutAPIView(EnhancedAPITestCase):
self.client.force_authenticate(user=self.user)
# Simulate providing a refresh token
response = self.client.post(self.url, {'refresh': 'dummy-token'})
response = self.client.post(self.url, {"refresh": "dummy-token"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -229,7 +210,7 @@ class TestCurrentUserAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/user/'
self.url = "/api/v1/auth/user/"
def test__current_user__authenticated__returns_user_data(self):
"""Test getting current user data when authenticated."""
@@ -238,7 +219,7 @@ class TestCurrentUserAPIView(EnhancedAPITestCase):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['username'], self.user.username)
self.assertEqual(response.data["username"], self.user.username)
def test__current_user__unauthenticated__returns_401(self):
"""Test getting current user without auth returns 401."""
@@ -254,18 +235,18 @@ class TestPasswordResetAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/password/reset/'
self.url = "/api/v1/auth/password/reset/"
def test__password_reset__with_valid_email__returns_success(self):
"""Test password reset request with valid email."""
response = self.client.post(self.url, {'email': self.user.email})
response = self.client.post(self.url, {"email": self.user.email})
# Should return success (don't reveal if email exists)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test__password_reset__with_nonexistent_email__returns_success(self):
"""Test password reset with nonexistent email returns success (security)."""
response = self.client.post(self.url, {'email': 'nonexistent@example.com'})
response = self.client.post(self.url, {"email": "nonexistent@example.com"})
# Should return success to not reveal email existence
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
@@ -278,7 +259,7 @@ class TestPasswordResetAPIView(EnhancedAPITestCase):
def test__password_reset__with_invalid_email_format__returns_400(self):
"""Test password reset with invalid email format returns error."""
response = self.client.post(self.url, {'email': 'notanemail'})
response = self.client.post(self.url, {"email": "notanemail"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -290,19 +271,22 @@ class TestPasswordChangeAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.user.set_password('oldpassword123')
self.user.set_password("oldpassword123")
self.user.save()
self.url = '/api/v1/auth/password/change/'
self.url = "/api/v1/auth/password/change/"
def test__password_change__with_valid_data__changes_password(self):
"""Test password change with valid data."""
self.client.force_authenticate(user=self.user)
response = self.client.post(self.url, {
'old_password': 'oldpassword123',
'new_password1': 'NewComplexPass123!',
'new_password2': 'NewComplexPass123!'
})
response = self.client.post(
self.url,
{
"old_password": "oldpassword123",
"new_password1": "NewComplexPass123!",
"new_password2": "NewComplexPass123!",
},
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
@@ -310,21 +294,27 @@ class TestPasswordChangeAPIView(EnhancedAPITestCase):
"""Test password change with wrong old password."""
self.client.force_authenticate(user=self.user)
response = self.client.post(self.url, {
'old_password': 'wrongpassword',
'new_password1': 'NewComplexPass123!',
'new_password2': 'NewComplexPass123!'
})
response = self.client.post(
self.url,
{
"old_password": "wrongpassword",
"new_password1": "NewComplexPass123!",
"new_password2": "NewComplexPass123!",
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__password_change__unauthenticated__returns_401(self):
"""Test password change without authentication."""
response = self.client.post(self.url, {
'old_password': 'oldpassword123',
'new_password1': 'NewComplexPass123!',
'new_password2': 'NewComplexPass123!'
})
response = self.client.post(
self.url,
{
"old_password": "oldpassword123",
"new_password1": "NewComplexPass123!",
"new_password2": "NewComplexPass123!",
},
)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -335,7 +325,7 @@ class TestSocialProvidersAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.url = '/api/v1/auth/social/providers/'
self.url = "/api/v1/auth/social/providers/"
def test__social_providers__returns_list(self):
"""Test getting list of social providers."""
@@ -352,7 +342,7 @@ class TestAuthStatusAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/status/'
self.url = "/api/v1/auth/status/"
def test__auth_status__authenticated__returns_authenticated_true(self):
"""Test auth status for authenticated user."""
@@ -361,15 +351,15 @@ class TestAuthStatusAPIView(EnhancedAPITestCase):
response = self.client.post(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data.get('authenticated'))
self.assertIsNotNone(response.data.get('user'))
self.assertTrue(response.data.get("authenticated"))
self.assertIsNotNone(response.data.get("user"))
def test__auth_status__unauthenticated__returns_authenticated_false(self):
"""Test auth status for unauthenticated user."""
response = self.client.post(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertFalse(response.data.get('authenticated'))
self.assertFalse(response.data.get("authenticated"))
class TestAvailableProvidersAPIView(EnhancedAPITestCase):
@@ -378,7 +368,7 @@ class TestAvailableProvidersAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.url = '/api/v1/auth/social/available/'
self.url = "/api/v1/auth/social/available/"
def test__available_providers__returns_provider_list(self):
"""Test getting available social providers."""
@@ -395,7 +385,7 @@ class TestConnectedProvidersAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/social/connected/'
self.url = "/api/v1/auth/social/connected/"
def test__connected_providers__authenticated__returns_list(self):
"""Test getting connected providers for authenticated user."""
@@ -423,9 +413,7 @@ class TestConnectProviderAPIView(EnhancedAPITestCase):
def test__connect_provider__unauthenticated__returns_401(self):
"""Test connecting provider without auth."""
response = self.client.post('/api/v1/auth/social/connect/google/', {
'access_token': 'dummy-token'
})
response = self.client.post("/api/v1/auth/social/connect/google/", {"access_token": "dummy-token"})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -433,9 +421,7 @@ class TestConnectProviderAPIView(EnhancedAPITestCase):
"""Test connecting invalid provider."""
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/v1/auth/social/connect/invalid/', {
'access_token': 'dummy-token'
})
response = self.client.post("/api/v1/auth/social/connect/invalid/", {"access_token": "dummy-token"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -443,7 +429,7 @@ class TestConnectProviderAPIView(EnhancedAPITestCase):
"""Test connecting provider without token."""
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/v1/auth/social/connect/google/', {})
response = self.client.post("/api/v1/auth/social/connect/google/", {})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -458,7 +444,7 @@ class TestDisconnectProviderAPIView(EnhancedAPITestCase):
def test__disconnect_provider__unauthenticated__returns_401(self):
"""Test disconnecting provider without auth."""
response = self.client.post('/api/v1/auth/social/disconnect/google/')
response = self.client.post("/api/v1/auth/social/disconnect/google/")
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -466,7 +452,7 @@ class TestDisconnectProviderAPIView(EnhancedAPITestCase):
"""Test disconnecting invalid provider."""
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/v1/auth/social/disconnect/invalid/')
response = self.client.post("/api/v1/auth/social/disconnect/invalid/")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -478,7 +464,7 @@ class TestSocialAuthStatusAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory()
self.url = '/api/v1/auth/social/status/'
self.url = "/api/v1/auth/social/status/"
def test__social_auth_status__authenticated__returns_status(self):
"""Test getting social auth status."""
@@ -504,7 +490,7 @@ class TestEmailVerificationAPIView(EnhancedAPITestCase):
def test__email_verification__invalid_token__returns_404(self):
"""Test email verification with invalid token."""
response = self.client.get('/api/v1/auth/verify-email/invalid-token/')
response = self.client.get("/api/v1/auth/verify-email/invalid-token/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -516,7 +502,7 @@ class TestResendVerificationAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.user = UserFactory(is_active=False)
self.url = '/api/v1/auth/resend-verification/'
self.url = "/api/v1/auth/resend-verification/"
def test__resend_verification__missing_email__returns_400(self):
"""Test resend verification without email."""
@@ -528,13 +514,13 @@ class TestResendVerificationAPIView(EnhancedAPITestCase):
"""Test resend verification for already verified user."""
active_user = UserFactory(is_active=True)
response = self.client.post(self.url, {'email': active_user.email})
response = self.client.post(self.url, {"email": active_user.email})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__resend_verification__nonexistent_email__returns_success(self):
"""Test resend verification for nonexistent email (security)."""
response = self.client.post(self.url, {'email': 'nonexistent@example.com'})
response = self.client.post(self.url, {"email": "nonexistent@example.com"})
# Should return success to not reveal email existence
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -556,35 +542,26 @@ class TestAuthAPIEdgeCases(EnhancedAPITestCase):
]
for username in special_usernames:
response = self.client.post('/api/v1/auth/login/', {
'username': username,
'password': 'testpass123'
})
response = self.client.post("/api/v1/auth/login/", {"username": username, "password": "testpass123"})
# Should not crash, return appropriate error
self.assertIn(response.status_code, [
status.HTTP_400_BAD_REQUEST,
status.HTTP_401_UNAUTHORIZED
])
self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED])
def test__signup__with_very_long_username__handled_safely(self):
"""Test signup with very long username."""
response = self.client.post('/api/v1/auth/signup/', {
'username': 'a' * 1000,
'email': 'test@example.com',
'password1': 'ComplexPass123!',
'password2': 'ComplexPass123!'
})
response = self.client.post(
"/api/v1/auth/signup/",
{
"username": "a" * 1000,
"email": "test@example.com",
"password1": "ComplexPass123!",
"password2": "ComplexPass123!",
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__login__with_unicode_characters__handled_safely(self):
"""Test login with unicode characters."""
response = self.client.post('/api/v1/auth/login/', {
'username': 'user\u202e',
'password': 'pass\u202e'
})
response = self.client.post("/api/v1/auth/login/", {"username": "user\u202e", "password": "pass\u202e"})
self.assertIn(response.status_code, [
status.HTTP_400_BAD_REQUEST,
status.HTTP_401_UNAUTHORIZED
])
self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED])

View File

@@ -25,23 +25,18 @@ class ErrorResponseFormatTestCase(TestCase):
data = response.json()
# Should have error information
self.assertTrue(
"error" in data or "detail" in data or "status" in data,
"404 response should contain error information"
"error" in data or "detail" in data or "status" in data, "404 response should contain error information"
)
def test_400_error_format(self):
"""Test that 400 validation errors follow standardized format."""
response = self.client.get(
"/api/v1/rides/hybrid/",
{"offset": "invalid"}
)
response = self.client.get("/api/v1/rides/hybrid/", {"offset": "invalid"})
if response.status_code == status.HTTP_400_BAD_REQUEST:
data = response.json()
# Should have error information
self.assertTrue(
"error" in data or "status" in data or "detail" in data,
"400 response should contain error information"
"error" in data or "status" in data or "detail" in data, "400 response should contain error information"
)
def test_500_error_format(self):
@@ -59,10 +54,7 @@ class ErrorCodeConsistencyTestCase(TestCase):
def test_validation_error_code(self):
"""Test that validation errors use consistent error codes."""
response = self.client.get(
"/api/v1/rides/hybrid/",
{"offset": "invalid"}
)
response = self.client.get("/api/v1/rides/hybrid/", {"offset": "invalid"})
if response.status_code == status.HTTP_400_BAD_REQUEST:
data = response.json()
@@ -86,10 +78,7 @@ class AuthenticationErrorTestCase(TestCase):
if response.status_code == status.HTTP_401_UNAUTHORIZED:
data = response.json()
# Should have error information
self.assertTrue(
"error" in data or "detail" in data,
"401 response should contain error information"
)
self.assertTrue("error" in data or "detail" in data, "401 response should contain error information")
def test_forbidden_error_format(self):
"""Test that forbidden errors are properly formatted."""
@@ -109,10 +98,7 @@ class ExceptionHandlerTestCase(TestCase):
from django.conf import settings
exception_handler = settings.REST_FRAMEWORK.get("EXCEPTION_HANDLER")
self.assertEqual(
exception_handler,
"apps.core.api.exceptions.custom_exception_handler"
)
self.assertEqual(exception_handler, "apps.core.api.exceptions.custom_exception_handler")
def test_throttled_error_format(self):
"""Test that throttled errors are properly formatted."""

View File

@@ -20,39 +20,24 @@ class FilterParameterNamingTestCase(TestCase):
def test_range_filter_naming_convention(self):
"""Test that range filters use {field}_min/{field}_max naming."""
# Test parks rating range filter
response = self.client.get(
"/api/v1/parks/hybrid/",
{"rating_min": 3.0, "rating_max": 5.0}
)
response = self.client.get("/api/v1/parks/hybrid/", {"rating_min": 3.0, "rating_max": 5.0})
# Should not return error for valid filter names
self.assertIn(
response.status_code,
[status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test_search_parameter_naming(self):
"""Test that search parameter is named consistently."""
response = self.client.get("/api/v1/parks/hybrid/", {"search": "cedar"})
self.assertIn(
response.status_code,
[status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test_ordering_parameter_naming(self):
"""Test that ordering parameter is named consistently."""
response = self.client.get("/api/v1/parks/hybrid/", {"ordering": "name"})
self.assertIn(
response.status_code,
[status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test_ordering_descending_prefix(self):
"""Test that descending ordering uses - prefix."""
response = self.client.get("/api/v1/parks/hybrid/", {"ordering": "-name"})
self.assertIn(
response.status_code,
[status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
class FilterBehaviorTestCase(TestCase):
@@ -64,10 +49,7 @@ class FilterBehaviorTestCase(TestCase):
def test_filter_combination_and_logic(self):
"""Test that multiple different filters use AND logic."""
response = self.client.get(
"/api/v1/parks/hybrid/",
{"rating_min": 4.0, "country": "us"}
)
response = self.client.get("/api/v1/parks/hybrid/", {"rating_min": 4.0, "country": "us"})
if response.status_code == status.HTTP_200_OK:
data = response.json()
# Results should match both criteria
@@ -75,25 +57,16 @@ class FilterBehaviorTestCase(TestCase):
def test_multi_select_filter_or_logic(self):
"""Test that multi-select filters within same field use OR logic."""
response = self.client.get(
"/api/v1/rides/hybrid/",
{"ride_type": "Coaster,Dark Ride"}
)
response = self.client.get("/api/v1/rides/hybrid/", {"ride_type": "Coaster,Dark Ride"})
if response.status_code == status.HTTP_200_OK:
data = response.json()
self.assertIn("success", data)
def test_invalid_filter_value_returns_error(self):
"""Test that invalid filter values return appropriate error."""
response = self.client.get(
"/api/v1/parks/hybrid/",
{"rating_min": "not_a_number"}
)
response = self.client.get("/api/v1/parks/hybrid/", {"rating_min": "not_a_number"})
# Could be 200 (ignored) or 400 (validation error)
self.assertIn(
response.status_code,
[status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
)
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
class FilterMetadataTestCase(TestCase):
@@ -116,9 +89,11 @@ class FilterMetadataTestCase(TestCase):
metadata = data["data"]
# Should have categorical and/or ranges
self.assertTrue(
"categorical" in metadata or "ranges" in metadata or
"total_count" in metadata or "ordering_options" in metadata,
"Filter metadata should contain filter options"
"categorical" in metadata
or "ranges" in metadata
or "total_count" in metadata
or "ordering_options" in metadata,
"Filter metadata should contain filter options",
)
def test_rides_filter_metadata_structure(self):

View File

@@ -112,7 +112,4 @@ class HybridPaginationTestCase(TestCase):
if response.status_code == status.HTTP_400_BAD_REQUEST:
data = response.json()
# Should have error information
self.assertTrue(
"error" in data or "status" in data,
"Error response should contain error information"
)
self.assertTrue("error" in data or "status" in data, "Error response should contain error information")

View File

@@ -38,13 +38,13 @@ class TestParkPhotoViewSetList(EnhancedAPITestCase):
self.user = UserFactory()
self.park = ParkFactory()
@patch('apps.parks.models.ParkPhoto.objects')
@patch("apps.parks.models.ParkPhoto.objects")
def test__list_park_photos__unauthenticated__can_access(self, mock_queryset):
"""Test that unauthenticated users can access park photo list."""
# Mock the queryset
mock_queryset.select_related.return_value.filter.return_value.order_by.return_value = []
url = f'/api/v1/parks/{self.park.id}/photos/'
url = f"/api/v1/parks/{self.park.id}/photos/"
response = self.client.get(url)
# Should allow access (AllowAny permission for list)
@@ -52,7 +52,7 @@ class TestParkPhotoViewSetList(EnhancedAPITestCase):
def test__list_park_photos__with_invalid_park__returns_empty_or_404(self):
"""Test listing photos for non-existent park."""
url = '/api/v1/parks/99999/photos/'
url = "/api/v1/parks/99999/photos/"
response = self.client.get(url)
# Should handle gracefully
@@ -71,7 +71,7 @@ class TestParkPhotoViewSetCreate(EnhancedAPITestCase):
def test__create_park_photo__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot create photos."""
url = f'/api/v1/parks/{self.park.id}/photos/'
url = f"/api/v1/parks/{self.park.id}/photos/"
response = self.client.post(url, {})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -79,7 +79,7 @@ class TestParkPhotoViewSetCreate(EnhancedAPITestCase):
def test__create_park_photo__authenticated_without_data__returns_400(self):
"""Test that creating photo without required data returns 400."""
self.client.force_authenticate(user=self.user)
url = f'/api/v1/parks/{self.park.id}/photos/'
url = f"/api/v1/parks/{self.park.id}/photos/"
response = self.client.post(url, {})
@@ -88,9 +88,9 @@ class TestParkPhotoViewSetCreate(EnhancedAPITestCase):
def test__create_park_photo__invalid_park__returns_error(self):
"""Test creating photo for non-existent park."""
self.client.force_authenticate(user=self.user)
url = '/api/v1/parks/99999/photos/'
url = "/api/v1/parks/99999/photos/"
response = self.client.post(url, {'caption': 'Test'})
response = self.client.post(url, {"caption": "Test"})
# Should return 400 or 404 for invalid park
self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_404_NOT_FOUND])
@@ -106,7 +106,7 @@ class TestParkPhotoViewSetRetrieve(EnhancedAPITestCase):
def test__retrieve_park_photo__not_found__returns_404(self):
"""Test retrieving non-existent photo returns 404."""
url = f'/api/v1/parks/{self.park.id}/photos/99999/'
url = f"/api/v1/parks/{self.park.id}/photos/99999/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -125,8 +125,8 @@ class TestParkPhotoViewSetUpdate(EnhancedAPITestCase):
def test__update_park_photo__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot update photos."""
url = f'/api/v1/parks/{self.park.id}/photos/1/'
response = self.client.patch(url, {'caption': 'Updated'})
url = f"/api/v1/parks/{self.park.id}/photos/1/"
response = self.client.patch(url, {"caption": "Updated"})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -143,7 +143,7 @@ class TestParkPhotoViewSetDelete(EnhancedAPITestCase):
def test__delete_park_photo__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot delete photos."""
url = f'/api/v1/parks/{self.park.id}/photos/1/'
url = f"/api/v1/parks/{self.park.id}/photos/1/"
response = self.client.delete(url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -161,7 +161,7 @@ class TestParkPhotoViewSetSetPrimary(EnhancedAPITestCase):
def test__set_primary__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot set primary photo."""
url = f'/api/v1/parks/{self.park.id}/photos/1/set_primary/'
url = f"/api/v1/parks/{self.park.id}/photos/1/set_primary/"
response = self.client.post(url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@@ -169,7 +169,7 @@ class TestParkPhotoViewSetSetPrimary(EnhancedAPITestCase):
def test__set_primary__photo_not_found__returns_404(self):
"""Test setting primary for non-existent photo."""
self.client.force_authenticate(user=self.user)
url = f'/api/v1/parks/{self.park.id}/photos/99999/set_primary/'
url = f"/api/v1/parks/{self.park.id}/photos/99999/set_primary/"
response = self.client.post(url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -187,23 +187,23 @@ class TestParkPhotoViewSetBulkApprove(EnhancedAPITestCase):
def test__bulk_approve__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot bulk approve."""
url = f'/api/v1/parks/{self.park.id}/photos/bulk_approve/'
response = self.client.post(url, {'photo_ids': [1, 2], 'approve': True})
url = f"/api/v1/parks/{self.park.id}/photos/bulk_approve/"
response = self.client.post(url, {"photo_ids": [1, 2], "approve": True})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test__bulk_approve__non_staff__returns_403(self):
"""Test that non-staff users cannot bulk approve."""
self.client.force_authenticate(user=self.user)
url = f'/api/v1/parks/{self.park.id}/photos/bulk_approve/'
response = self.client.post(url, {'photo_ids': [1, 2], 'approve': True})
url = f"/api/v1/parks/{self.park.id}/photos/bulk_approve/"
response = self.client.post(url, {"photo_ids": [1, 2], "approve": True})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test__bulk_approve__missing_data__returns_400(self):
"""Test bulk approve with missing required data."""
self.client.force_authenticate(user=self.staff_user)
url = f'/api/v1/parks/{self.park.id}/photos/bulk_approve/'
url = f"/api/v1/parks/{self.park.id}/photos/bulk_approve/"
response = self.client.post(url, {})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -219,7 +219,7 @@ class TestParkPhotoViewSetStats(EnhancedAPITestCase):
def test__stats__unauthenticated__can_access(self):
"""Test that unauthenticated users can access stats."""
url = f'/api/v1/parks/{self.park.id}/photos/stats/'
url = f"/api/v1/parks/{self.park.id}/photos/stats/"
response = self.client.get(url)
# Stats should be accessible to all
@@ -227,7 +227,7 @@ class TestParkPhotoViewSetStats(EnhancedAPITestCase):
def test__stats__invalid_park__returns_404(self):
"""Test stats for non-existent park returns 404."""
url = '/api/v1/parks/99999/photos/stats/'
url = "/api/v1/parks/99999/photos/stats/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -244,15 +244,15 @@ class TestParkPhotoViewSetSaveImage(EnhancedAPITestCase):
def test__save_image__unauthenticated__returns_401(self):
"""Test that unauthenticated users cannot save images."""
url = f'/api/v1/parks/{self.park.id}/photos/save_image/'
response = self.client.post(url, {'cloudflare_image_id': 'test-id'})
url = f"/api/v1/parks/{self.park.id}/photos/save_image/"
response = self.client.post(url, {"cloudflare_image_id": "test-id"})
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test__save_image__missing_cloudflare_id__returns_400(self):
"""Test saving image without cloudflare_image_id."""
self.client.force_authenticate(user=self.user)
url = f'/api/v1/parks/{self.park.id}/photos/save_image/'
url = f"/api/v1/parks/{self.park.id}/photos/save_image/"
response = self.client.post(url, {})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -260,8 +260,8 @@ class TestParkPhotoViewSetSaveImage(EnhancedAPITestCase):
def test__save_image__invalid_park__returns_404(self):
"""Test saving image for non-existent park."""
self.client.force_authenticate(user=self.user)
url = '/api/v1/parks/99999/photos/save_image/'
response = self.client.post(url, {'cloudflare_image_id': 'test-id'})
url = "/api/v1/parks/99999/photos/save_image/"
response = self.client.post(url, {"cloudflare_image_id": "test-id"})
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -273,112 +273,112 @@ class TestHybridParkAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
# Create several parks for testing
self.operator = CompanyFactory(roles=['OPERATOR'])
self.operator = CompanyFactory(roles=["OPERATOR"])
self.parks = [
ParkFactory(operator=self.operator, status='OPERATING', name='Alpha Park'),
ParkFactory(operator=self.operator, status='OPERATING', name='Beta Park'),
ParkFactory(operator=self.operator, status='CLOSED_PERM', name='Gamma Park'),
ParkFactory(operator=self.operator, status="OPERATING", name="Alpha Park"),
ParkFactory(operator=self.operator, status="OPERATING", name="Beta Park"),
ParkFactory(operator=self.operator, status="CLOSED_PERM", name="Gamma Park"),
]
def test__hybrid_park_api__initial_load__returns_parks(self):
"""Test initial load returns parks with metadata."""
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data.get('success', False))
self.assertIn('data', response.data)
self.assertIn('parks', response.data['data'])
self.assertIn('total_count', response.data['data'])
self.assertIn('strategy', response.data['data'])
self.assertTrue(response.data.get("success", False))
self.assertIn("data", response.data)
self.assertIn("parks", response.data["data"])
self.assertIn("total_count", response.data["data"])
self.assertIn("strategy", response.data["data"])
def test__hybrid_park_api__with_status_filter__returns_filtered_parks(self):
"""Test filtering by status."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'status': 'OPERATING'})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"status": "OPERATING"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All returned parks should be OPERATING
for park in response.data['data']['parks']:
self.assertEqual(park['status'], 'OPERATING')
for park in response.data["data"]["parks"]:
self.assertEqual(park["status"], "OPERATING")
def test__hybrid_park_api__with_multiple_status_filter__returns_filtered_parks(self):
"""Test filtering by multiple statuses."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'status': 'OPERATING,CLOSED_PERM'})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"status": "OPERATING,CLOSED_PERM"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__with_search__returns_matching_parks(self):
"""Test search functionality."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'search': 'Alpha'})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"search": "Alpha"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Should find Alpha Park
parks = response.data['data']['parks']
park_names = [p['name'] for p in parks]
self.assertIn('Alpha Park', park_names)
parks = response.data["data"]["parks"]
park_names = [p["name"] for p in parks]
self.assertIn("Alpha Park", park_names)
def test__hybrid_park_api__with_offset__returns_progressive_data(self):
"""Test progressive loading with offset."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'offset': 0})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"offset": 0})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('has_more', response.data['data'])
self.assertIn("has_more", response.data["data"])
def test__hybrid_park_api__with_invalid_offset__returns_400(self):
"""Test invalid offset parameter."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'offset': 'invalid'})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"offset": "invalid"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__hybrid_park_api__with_year_filters__returns_filtered_parks(self):
"""Test filtering by opening year range."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'opening_year_min': 2000, 'opening_year_max': 2024})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"opening_year_min": 2000, "opening_year_max": 2024})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__with_rating_filters__returns_filtered_parks(self):
"""Test filtering by rating range."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'rating_min': 5.0, 'rating_max': 10.0})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"rating_min": 5.0, "rating_max": 10.0})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__with_size_filters__returns_filtered_parks(self):
"""Test filtering by size range."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'size_min': 10, 'size_max': 1000})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"size_min": 10, "size_max": 1000})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__with_ride_count_filters__returns_filtered_parks(self):
"""Test filtering by ride count range."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'ride_count_min': 5, 'ride_count_max': 100})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"ride_count_min": 5, "ride_count_max": 100})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__with_coaster_count_filters__returns_filtered_parks(self):
"""Test filtering by coaster count range."""
url = '/api/v1/parks/hybrid/'
response = self.client.get(url, {'coaster_count_min': 1, 'coaster_count_max': 20})
url = "/api/v1/parks/hybrid/"
response = self.client.get(url, {"coaster_count_min": 1, "coaster_count_max": 20})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_park_api__includes_filter_metadata__on_initial_load(self):
"""Test that initial load includes filter metadata."""
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Filter metadata should be included for client-side filtering
if 'filter_metadata' in response.data.get('data', {}):
self.assertIn('filter_metadata', response.data['data'])
if "filter_metadata" in response.data.get("data", {}):
self.assertIn("filter_metadata", response.data["data"])
class TestParkFilterMetadataAPIView(EnhancedAPITestCase):
@@ -387,7 +387,7 @@ class TestParkFilterMetadataAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.operator = CompanyFactory(roles=['OPERATOR'])
self.operator = CompanyFactory(roles=["OPERATOR"])
self.parks = [
ParkFactory(operator=self.operator),
ParkFactory(operator=self.operator),
@@ -395,32 +395,32 @@ class TestParkFilterMetadataAPIView(EnhancedAPITestCase):
def test__filter_metadata__unscoped__returns_all_metadata(self):
"""Test getting unscoped filter metadata."""
url = '/api/v1/parks/filter-metadata/'
url = "/api/v1/parks/filter-metadata/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data.get('success', False))
self.assertIn('data', response.data)
self.assertTrue(response.data.get("success", False))
self.assertIn("data", response.data)
def test__filter_metadata__scoped__returns_filtered_metadata(self):
"""Test getting scoped filter metadata."""
url = '/api/v1/parks/filter-metadata/'
response = self.client.get(url, {'scoped': 'true', 'status': 'OPERATING'})
url = "/api/v1/parks/filter-metadata/"
response = self.client.get(url, {"scoped": "true", "status": "OPERATING"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__filter_metadata__structure__contains_expected_fields(self):
"""Test that metadata contains expected structure."""
url = '/api/v1/parks/filter-metadata/'
url = "/api/v1/parks/filter-metadata/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.data.get('data', {})
data = response.data.get("data", {})
# Should contain categorical and range metadata
if data:
# These are the expected top-level keys based on the view
possible_keys = ['categorical', 'ranges', 'total_count']
possible_keys = ["categorical", "ranges", "total_count"]
for key in possible_keys:
if key in data:
self.assertIsNotNone(data[key])
@@ -464,7 +464,7 @@ class TestParkAPIQueryOptimization(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.operator = CompanyFactory(roles=['OPERATOR'])
self.operator = CompanyFactory(roles=["OPERATOR"])
def test__park_list__uses_select_related(self):
"""Test that park list uses select_related for optimization."""
@@ -472,7 +472,7 @@ class TestParkAPIQueryOptimization(EnhancedAPITestCase):
for _i in range(5):
ParkFactory(operator=self.operator)
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
# This test verifies the query is executed without N+1
response = self.client.get(url)
@@ -483,13 +483,13 @@ class TestParkAPIQueryOptimization(EnhancedAPITestCase):
"""Test that park list handles larger datasets efficiently."""
# Create a batch of parks
for i in range(10):
ParkFactory(operator=self.operator, name=f'Park {i}')
ParkFactory(operator=self.operator, name=f"Park {i}")
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertGreaterEqual(response.data['data']['total_count'], 10)
self.assertGreaterEqual(response.data["data"]["total_count"], 10)
class TestParkAPIEdgeCases(EnhancedAPITestCase):
@@ -504,16 +504,16 @@ class TestParkAPIEdgeCases(EnhancedAPITestCase):
# Delete all parks for this test
Park.objects.all().delete()
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['data']['parks'], [])
self.assertEqual(response.data['data']['total_count'], 0)
self.assertEqual(response.data["data"]["parks"], [])
self.assertEqual(response.data["data"]["total_count"], 0)
def test__hybrid_park__special_characters_in_search__handled_safely(self):
"""Test that special characters in search are handled safely."""
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
# Test with special characters
special_searches = [
@@ -525,21 +525,24 @@ class TestParkAPIEdgeCases(EnhancedAPITestCase):
]
for search_term in special_searches:
response = self.client.get(url, {'search': search_term})
response = self.client.get(url, {"search": search_term})
# Should not crash, either 200 or error with proper message
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test__hybrid_park__extreme_filter_values__handled_safely(self):
"""Test that extreme filter values are handled safely."""
url = '/api/v1/parks/hybrid/'
url = "/api/v1/parks/hybrid/"
# Test with extreme values
response = self.client.get(url, {
'rating_min': -100,
'rating_max': 10000,
'opening_year_min': 1,
'opening_year_max': 9999,
})
response = self.client.get(
url,
{
"rating_min": -100,
"rating_max": 10000,
"opening_year_min": 1,
"opening_year_max": 9999,
},
)
# Should handle gracefully
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])

View File

@@ -44,7 +44,7 @@ class ResponseFormatTestCase(TestCase):
# Should have either 'error' or 'status' key for error responses
self.assertTrue(
"error" in data or "status" in data or "detail" in data,
"Error response should contain error information"
"Error response should contain error information",
)
def test_validation_error_format(self):

View File

@@ -45,25 +45,16 @@ class TestRideListAPIView(EnhancedAPITestCase):
park=self.park,
manufacturer=self.manufacturer,
designer=self.designer,
name='Alpha Coaster',
status='OPERATING',
category='RC'
name="Alpha Coaster",
status="OPERATING",
category="RC",
),
RideFactory(
park=self.park,
manufacturer=self.manufacturer,
name='Beta Ride',
status='OPERATING',
category='DR'
),
RideFactory(
park=self.park,
name='Gamma Coaster',
status='CLOSED_TEMP',
category='RC'
park=self.park, manufacturer=self.manufacturer, name="Beta Ride", status="OPERATING", category="DR"
),
RideFactory(park=self.park, name="Gamma Coaster", status="CLOSED_TEMP", category="RC"),
]
self.url = '/api/v1/rides/'
self.url = "/api/v1/rides/"
def test__ride_list__unauthenticated__can_access(self):
"""Test that unauthenticated users can access ride list."""
@@ -77,119 +68,109 @@ class TestRideListAPIView(EnhancedAPITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Should have pagination info
self.assertIn('results', response.data)
self.assertIn('count', response.data)
self.assertIn("results", response.data)
self.assertIn("count", response.data)
def test__ride_list__with_search__returns_matching_rides(self):
"""Test search functionality."""
response = self.client.get(self.url, {'search': 'Alpha'})
response = self.client.get(self.url, {"search": "Alpha"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Should find Alpha Coaster
results = response.data.get('results', [])
results = response.data.get("results", [])
if results:
names = [r.get('name', '') for r in results]
self.assertTrue(any('Alpha' in name for name in names))
names = [r.get("name", "") for r in results]
self.assertTrue(any("Alpha" in name for name in names))
def test__ride_list__with_park_slug__returns_filtered_rides(self):
"""Test filtering by park slug."""
response = self.client.get(self.url, {'park_slug': self.park.slug})
response = self.client.get(self.url, {"park_slug": self.park.slug})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_park_id__returns_filtered_rides(self):
"""Test filtering by park ID."""
response = self.client.get(self.url, {'park_id': self.park.id})
response = self.client.get(self.url, {"park_id": self.park.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_category_filter__returns_filtered_rides(self):
"""Test filtering by category."""
response = self.client.get(self.url, {'category': 'RC'})
response = self.client.get(self.url, {"category": "RC"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All returned rides should be roller coasters
for ride in response.data.get('results', []):
self.assertEqual(ride.get('category'), 'RC')
for ride in response.data.get("results", []):
self.assertEqual(ride.get("category"), "RC")
def test__ride_list__with_status_filter__returns_filtered_rides(self):
"""Test filtering by status."""
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)
for ride in response.data.get('results', []):
self.assertEqual(ride.get('status'), 'OPERATING')
for ride in response.data.get("results", []):
self.assertEqual(ride.get("status"), "OPERATING")
def test__ride_list__with_manufacturer_filter__returns_filtered_rides(self):
"""Test filtering by manufacturer ID."""
response = self.client.get(self.url, {'manufacturer_id': self.manufacturer.id})
response = self.client.get(self.url, {"manufacturer_id": self.manufacturer.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_manufacturer_slug__returns_filtered_rides(self):
"""Test filtering by manufacturer slug."""
response = self.client.get(self.url, {'manufacturer_slug': self.manufacturer.slug})
response = self.client.get(self.url, {"manufacturer_slug": self.manufacturer.slug})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_designer_filter__returns_filtered_rides(self):
"""Test filtering by designer ID."""
response = self.client.get(self.url, {'designer_id': self.designer.id})
response = self.client.get(self.url, {"designer_id": self.designer.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_rating_filters__returns_filtered_rides(self):
"""Test filtering by rating range."""
response = self.client.get(self.url, {'min_rating': 5, 'max_rating': 10})
response = self.client.get(self.url, {"min_rating": 5, "max_rating": 10})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_height_requirement_filters__returns_filtered_rides(self):
"""Test filtering by height requirement."""
response = self.client.get(self.url, {
'min_height_requirement': 36,
'max_height_requirement': 54
})
response = self.client.get(self.url, {"min_height_requirement": 36, "max_height_requirement": 54})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_capacity_filters__returns_filtered_rides(self):
"""Test filtering by capacity."""
response = self.client.get(self.url, {'min_capacity': 500, 'max_capacity': 3000})
response = self.client.get(self.url, {"min_capacity": 500, "max_capacity": 3000})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_opening_year_filters__returns_filtered_rides(self):
"""Test filtering by opening year."""
response = self.client.get(self.url, {
'min_opening_year': 2000,
'max_opening_year': 2024
})
response = self.client.get(self.url, {"min_opening_year": 2000, "max_opening_year": 2024})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_ordering__returns_ordered_results(self):
"""Test ordering functionality."""
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)
def test__ride_list__with_multiple_filters__returns_combined_results(self):
"""Test combining multiple filters."""
response = self.client.get(self.url, {
'category': 'RC',
'status': 'OPERATING',
'ordering': 'name'
})
response = self.client.get(self.url, {"category": "RC", "status": "OPERATING", "ordering": "name"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__pagination__page_size_respected(self):
"""Test that page_size parameter is respected."""
response = self.client.get(self.url, {'page_size': 1})
response = self.client.get(self.url, {"page_size": 1})
self.assertEqual(response.status_code, status.HTTP_200_OK)
results = response.data.get('results', [])
results = response.data.get("results", [])
self.assertLessEqual(len(results), 1)
@@ -203,14 +184,14 @@ class TestRideCreateAPIView(EnhancedAPITestCase):
self.staff_user = StaffUserFactory()
self.park = ParkFactory()
self.manufacturer = ManufacturerCompanyFactory()
self.url = '/api/v1/rides/'
self.url = "/api/v1/rides/"
self.valid_ride_data = {
'name': 'New Test Ride',
'description': 'A test ride for API testing',
'park_id': self.park.id,
'category': 'RC',
'status': 'OPERATING',
"name": "New Test Ride",
"description": "A test ride for API testing",
"park_id": self.park.id,
"category": "RC",
"status": "OPERATING",
}
def test__ride_create__unauthenticated__returns_401(self):
@@ -219,11 +200,9 @@ class TestRideCreateAPIView(EnhancedAPITestCase):
# Based on the view, AllowAny is used, so it might allow creation
# If not, it should be 401
self.assertIn(response.status_code, [
status.HTTP_201_CREATED,
status.HTTP_401_UNAUTHORIZED,
status.HTTP_400_BAD_REQUEST
])
self.assertIn(
response.status_code, [status.HTTP_201_CREATED, status.HTTP_401_UNAUTHORIZED, status.HTTP_400_BAD_REQUEST]
)
def test__ride_create__with_valid_data__creates_ride(self):
"""Test creating ride with valid data."""
@@ -231,39 +210,36 @@ class TestRideCreateAPIView(EnhancedAPITestCase):
response = self.client.post(self.url, self.valid_ride_data)
# Should create or return validation error if models not available
self.assertIn(response.status_code, [
status.HTTP_201_CREATED,
status.HTTP_400_BAD_REQUEST,
status.HTTP_501_NOT_IMPLEMENTED
])
self.assertIn(
response.status_code,
[status.HTTP_201_CREATED, status.HTTP_400_BAD_REQUEST, status.HTTP_501_NOT_IMPLEMENTED],
)
def test__ride_create__with_invalid_park__returns_error(self):
"""Test creating ride with invalid park ID."""
self.client.force_authenticate(user=self.user)
invalid_data = self.valid_ride_data.copy()
invalid_data['park_id'] = 99999
invalid_data["park_id"] = 99999
response = self.client.post(self.url, invalid_data)
self.assertIn(response.status_code, [
status.HTTP_400_BAD_REQUEST,
status.HTTP_404_NOT_FOUND,
status.HTTP_501_NOT_IMPLEMENTED
])
self.assertIn(
response.status_code,
[status.HTTP_400_BAD_REQUEST, status.HTTP_404_NOT_FOUND, status.HTTP_501_NOT_IMPLEMENTED],
)
def test__ride_create__with_manufacturer__creates_ride_with_relationship(self):
"""Test creating ride with manufacturer relationship."""
self.client.force_authenticate(user=self.user)
data_with_manufacturer = self.valid_ride_data.copy()
data_with_manufacturer['manufacturer_id'] = self.manufacturer.id
data_with_manufacturer["manufacturer_id"] = self.manufacturer.id
response = self.client.post(self.url, data_with_manufacturer)
self.assertIn(response.status_code, [
status.HTTP_201_CREATED,
status.HTTP_400_BAD_REQUEST,
status.HTTP_501_NOT_IMPLEMENTED
])
self.assertIn(
response.status_code,
[status.HTTP_201_CREATED, status.HTTP_400_BAD_REQUEST, status.HTTP_501_NOT_IMPLEMENTED],
)
class TestRideDetailAPIView(EnhancedAPITestCase):
@@ -275,7 +251,7 @@ class TestRideDetailAPIView(EnhancedAPITestCase):
self.user = UserFactory()
self.park = ParkFactory()
self.ride = RideFactory(park=self.park)
self.url = f'/api/v1/rides/{self.ride.id}/'
self.url = f"/api/v1/rides/{self.ride.id}/"
def test__ride_detail__unauthenticated__can_access(self):
"""Test that unauthenticated users can access ride detail."""
@@ -289,13 +265,13 @@ class TestRideDetailAPIView(EnhancedAPITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
expected_fields = ['id', 'name', 'description', 'category', 'status', 'park']
expected_fields = ["id", "name", "description", "category", "status", "park"]
for field in expected_fields:
self.assertIn(field, response.data)
def test__ride_detail__invalid_id__returns_404(self):
"""Test that invalid ride ID returns 404."""
response = self.client.get('/api/v1/rides/99999/')
response = self.client.get("/api/v1/rides/99999/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -309,34 +285,30 @@ class TestRideUpdateAPIView(EnhancedAPITestCase):
self.user = UserFactory()
self.park = ParkFactory()
self.ride = RideFactory(park=self.park)
self.url = f'/api/v1/rides/{self.ride.id}/'
self.url = f"/api/v1/rides/{self.ride.id}/"
def test__ride_update__partial_update__updates_field(self):
"""Test partial update (PATCH)."""
self.client.force_authenticate(user=self.user)
update_data = {'description': 'Updated description'}
update_data = {"description": "Updated description"}
response = self.client.patch(self.url, update_data)
self.assertIn(response.status_code, [
status.HTTP_200_OK,
status.HTTP_401_UNAUTHORIZED,
status.HTTP_403_FORBIDDEN
])
self.assertIn(
response.status_code, [status.HTTP_200_OK, status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
)
def test__ride_update__move_to_new_park__updates_relationship(self):
"""Test moving ride to a different park."""
self.client.force_authenticate(user=self.user)
new_park = ParkFactory()
update_data = {'park_id': new_park.id}
update_data = {"park_id": new_park.id}
response = self.client.patch(self.url, update_data)
self.assertIn(response.status_code, [
status.HTTP_200_OK,
status.HTTP_401_UNAUTHORIZED,
status.HTTP_403_FORBIDDEN
])
self.assertIn(
response.status_code, [status.HTTP_200_OK, status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
)
class TestRideDeleteAPIView(EnhancedAPITestCase):
@@ -348,18 +320,16 @@ class TestRideDeleteAPIView(EnhancedAPITestCase):
self.user = UserFactory()
self.park = ParkFactory()
self.ride = RideFactory(park=self.park)
self.url = f'/api/v1/rides/{self.ride.id}/'
self.url = f"/api/v1/rides/{self.ride.id}/"
def test__ride_delete__authenticated__deletes_ride(self):
"""Test deleting a ride."""
self.client.force_authenticate(user=self.user)
response = self.client.delete(self.url)
self.assertIn(response.status_code, [
status.HTTP_204_NO_CONTENT,
status.HTTP_401_UNAUTHORIZED,
status.HTTP_403_FORBIDDEN
])
self.assertIn(
response.status_code, [status.HTTP_204_NO_CONTENT, status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
)
class TestFilterOptionsAPIView(EnhancedAPITestCase):
@@ -368,7 +338,7 @@ class TestFilterOptionsAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.url = '/api/v1/rides/filter-options/'
self.url = "/api/v1/rides/filter-options/"
def test__filter_options__returns_all_options(self):
"""Test that filter options endpoint returns all filter options."""
@@ -377,7 +347,7 @@ class TestFilterOptionsAPIView(EnhancedAPITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Check for expected filter categories
expected_keys = ['categories', 'statuses']
expected_keys = ["categories", "statuses"]
for key in expected_keys:
self.assertIn(key, response.data)
@@ -386,14 +356,14 @@ class TestFilterOptionsAPIView(EnhancedAPITestCase):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('ranges', response.data)
self.assertIn("ranges", response.data)
def test__filter_options__includes_ordering_options(self):
"""Test that filter options include ordering options."""
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('ordering_options', response.data)
self.assertIn("ordering_options", response.data)
class TestHybridRideAPIView(EnhancedAPITestCase):
@@ -405,92 +375,86 @@ class TestHybridRideAPIView(EnhancedAPITestCase):
self.park = ParkFactory()
self.manufacturer = ManufacturerCompanyFactory()
self.rides = [
RideFactory(park=self.park, manufacturer=self.manufacturer, status='OPERATING', category='RC'),
RideFactory(park=self.park, status='OPERATING', category='DR'),
RideFactory(park=self.park, status='CLOSED_TEMP', category='RC'),
RideFactory(park=self.park, manufacturer=self.manufacturer, status="OPERATING", category="RC"),
RideFactory(park=self.park, status="OPERATING", category="DR"),
RideFactory(park=self.park, status="CLOSED_TEMP", category="RC"),
]
self.url = '/api/v1/rides/hybrid/'
self.url = "/api/v1/rides/hybrid/"
def test__hybrid_ride__initial_load__returns_rides(self):
"""Test initial load returns rides with metadata."""
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data.get('success', False))
self.assertIn('data', response.data)
self.assertIn('rides', response.data['data'])
self.assertIn('total_count', response.data['data'])
self.assertTrue(response.data.get("success", False))
self.assertIn("data", response.data)
self.assertIn("rides", response.data["data"])
self.assertIn("total_count", response.data["data"])
def test__hybrid_ride__with_category_filter__returns_filtered_rides(self):
"""Test filtering by category."""
response = self.client.get(self.url, {'category': 'RC'})
response = self.client.get(self.url, {"category": "RC"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_status_filter__returns_filtered_rides(self):
"""Test filtering by status."""
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)
def test__hybrid_ride__with_park_slug__returns_filtered_rides(self):
"""Test filtering by park slug."""
response = self.client.get(self.url, {'park_slug': self.park.slug})
response = self.client.get(self.url, {"park_slug": self.park.slug})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_manufacturer_filter__returns_filtered_rides(self):
"""Test filtering by manufacturer."""
response = self.client.get(self.url, {'manufacturer': self.manufacturer.slug})
response = self.client.get(self.url, {"manufacturer": self.manufacturer.slug})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_offset__returns_progressive_data(self):
"""Test progressive loading with offset."""
response = self.client.get(self.url, {'offset': 0})
response = self.client.get(self.url, {"offset": 0})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('has_more', response.data['data'])
self.assertIn("has_more", response.data["data"])
def test__hybrid_ride__with_invalid_offset__returns_400(self):
"""Test invalid offset parameter."""
response = self.client.get(self.url, {'offset': 'invalid'})
response = self.client.get(self.url, {"offset": "invalid"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test__hybrid_ride__with_search__returns_matching_rides(self):
"""Test search functionality."""
response = self.client.get(self.url, {'search': 'test'})
response = self.client.get(self.url, {"search": "test"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_rating_filters__returns_filtered_rides(self):
"""Test filtering by rating range."""
response = self.client.get(self.url, {'rating_min': 5.0, 'rating_max': 10.0})
response = self.client.get(self.url, {"rating_min": 5.0, "rating_max": 10.0})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_height_filters__returns_filtered_rides(self):
"""Test filtering by height requirement range."""
response = self.client.get(self.url, {
'height_requirement_min': 36,
'height_requirement_max': 54
})
response = self.client.get(self.url, {"height_requirement_min": 36, "height_requirement_max": 54})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_roller_coaster_filters__returns_filtered_rides(self):
"""Test filtering by roller coaster specific fields."""
response = self.client.get(self.url, {
'roller_coaster_type': 'SITDOWN',
'track_material': 'STEEL'
})
response = self.client.get(self.url, {"roller_coaster_type": "SITDOWN", "track_material": "STEEL"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__hybrid_ride__with_inversions_filter__returns_filtered_rides(self):
"""Test filtering by inversions."""
response = self.client.get(self.url, {'has_inversions': 'true'})
response = self.client.get(self.url, {"has_inversions": "true"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -501,19 +465,19 @@ class TestRideFilterMetadataAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.url = '/api/v1/rides/filter-metadata/'
self.url = "/api/v1/rides/filter-metadata/"
def test__filter_metadata__unscoped__returns_all_metadata(self):
"""Test getting unscoped filter metadata."""
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data.get('success', False))
self.assertIn('data', response.data)
self.assertTrue(response.data.get("success", False))
self.assertIn("data", response.data)
def test__filter_metadata__scoped__returns_filtered_metadata(self):
"""Test getting scoped filter metadata."""
response = self.client.get(self.url, {'scoped': 'true', 'category': 'RC'})
response = self.client.get(self.url, {"scoped": "true", "category": "RC"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -524,19 +488,19 @@ class TestCompanySearchAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.manufacturer = ManufacturerCompanyFactory(name='Bolliger & Mabillard')
self.url = '/api/v1/rides/search/companies/'
self.manufacturer = ManufacturerCompanyFactory(name="Bolliger & Mabillard")
self.url = "/api/v1/rides/search/companies/"
def test__company_search__with_query__returns_matching_companies(self):
"""Test searching for companies."""
response = self.client.get(self.url, {'q': 'Bolliger'})
response = self.client.get(self.url, {"q": "Bolliger"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsInstance(response.data, list)
def test__company_search__empty_query__returns_empty_list(self):
"""Test empty query returns empty list."""
response = self.client.get(self.url, {'q': ''})
response = self.client.get(self.url, {"q": ""})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [])
@@ -555,19 +519,19 @@ class TestRideModelSearchAPIView(EnhancedAPITestCase):
def setUp(self):
"""Set up test data."""
self.client = APIClient()
self.ride_model = RideModelFactory(name='Hyper Coaster')
self.url = '/api/v1/rides/search-ride-models/'
self.ride_model = RideModelFactory(name="Hyper Coaster")
self.url = "/api/v1/rides/search-ride-models/"
def test__ride_model_search__with_query__returns_matching_models(self):
"""Test searching for ride models."""
response = self.client.get(self.url, {'q': 'Hyper'})
response = self.client.get(self.url, {"q": "Hyper"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsInstance(response.data, list)
def test__ride_model_search__empty_query__returns_empty_list(self):
"""Test empty query returns empty list."""
response = self.client.get(self.url, {'q': ''})
response = self.client.get(self.url, {"q": ""})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [])
@@ -580,19 +544,19 @@ class TestRideSearchSuggestionsAPIView(EnhancedAPITestCase):
"""Set up test data."""
self.client = APIClient()
self.park = ParkFactory()
self.ride = RideFactory(park=self.park, name='Superman: Escape from Krypton')
self.url = '/api/v1/rides/search-suggestions/'
self.ride = RideFactory(park=self.park, name="Superman: Escape from Krypton")
self.url = "/api/v1/rides/search-suggestions/"
def test__search_suggestions__with_query__returns_suggestions(self):
"""Test getting search suggestions."""
response = self.client.get(self.url, {'q': 'Superman'})
response = self.client.get(self.url, {"q": "Superman"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsInstance(response.data, list)
def test__search_suggestions__empty_query__returns_empty_list(self):
"""Test empty query returns empty list."""
response = self.client.get(self.url, {'q': ''})
response = self.client.get(self.url, {"q": ""})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [])
@@ -607,7 +571,7 @@ class TestRideImageSettingsAPIView(EnhancedAPITestCase):
self.user = UserFactory()
self.park = ParkFactory()
self.ride = RideFactory(park=self.park)
self.url = f'/api/v1/rides/{self.ride.id}/image-settings/'
self.url = f"/api/v1/rides/{self.ride.id}/image-settings/"
def test__image_settings__patch__updates_settings(self):
"""Test updating ride image settings."""
@@ -616,17 +580,15 @@ class TestRideImageSettingsAPIView(EnhancedAPITestCase):
response = self.client.patch(self.url, {})
# Should handle the request
self.assertIn(response.status_code, [
status.HTTP_200_OK,
status.HTTP_400_BAD_REQUEST,
status.HTTP_401_UNAUTHORIZED
])
self.assertIn(
response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST, status.HTTP_401_UNAUTHORIZED]
)
def test__image_settings__invalid_ride__returns_404(self):
"""Test updating image settings for non-existent ride."""
self.client.force_authenticate(user=self.user)
response = self.client.patch('/api/v1/rides/99999/image-settings/', {})
response = self.client.patch("/api/v1/rides/99999/image-settings/", {})
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@@ -639,55 +601,55 @@ class TestRideAPIRollerCoasterFilters(EnhancedAPITestCase):
self.client = APIClient()
self.park = ParkFactory()
# Create coasters with different stats
self.coaster1 = CoasterFactory(park=self.park, name='Steel Vengeance')
self.coaster2 = CoasterFactory(park=self.park, name='Millennium Force')
self.url = '/api/v1/rides/'
self.coaster1 = CoasterFactory(park=self.park, name="Steel Vengeance")
self.coaster2 = CoasterFactory(park=self.park, name="Millennium Force")
self.url = "/api/v1/rides/"
def test__ride_list__with_roller_coaster_type__filters_correctly(self):
"""Test filtering by roller coaster type."""
response = self.client.get(self.url, {'roller_coaster_type': 'SITDOWN'})
response = self.client.get(self.url, {"roller_coaster_type": "SITDOWN"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_track_material__filters_correctly(self):
"""Test filtering by track material."""
response = self.client.get(self.url, {'track_material': 'STEEL'})
response = self.client.get(self.url, {"track_material": "STEEL"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_propulsion_system__filters_correctly(self):
"""Test filtering by propulsion system."""
response = self.client.get(self.url, {'propulsion_system': 'CHAIN'})
response = self.client.get(self.url, {"propulsion_system": "CHAIN"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_height_ft_range__filters_correctly(self):
"""Test filtering by height in feet."""
response = self.client.get(self.url, {'min_height_ft': 100, 'max_height_ft': 500})
response = self.client.get(self.url, {"min_height_ft": 100, "max_height_ft": 500})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_speed_mph_range__filters_correctly(self):
"""Test filtering by speed in mph."""
response = self.client.get(self.url, {'min_speed_mph': 50, 'max_speed_mph': 150})
response = self.client.get(self.url, {"min_speed_mph": 50, "max_speed_mph": 150})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__with_inversions_range__filters_correctly(self):
"""Test filtering by number of inversions."""
response = self.client.get(self.url, {'min_inversions': 0, 'max_inversions': 14})
response = self.client.get(self.url, {"min_inversions": 0, "max_inversions": 14})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__ordering_by_height__orders_correctly(self):
"""Test ordering by height."""
response = self.client.get(self.url, {'ordering': '-height_ft'})
response = self.client.get(self.url, {"ordering": "-height_ft"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test__ride_list__ordering_by_speed__orders_correctly(self):
"""Test ordering by speed."""
response = self.client.get(self.url, {'ordering': '-speed_mph'})
response = self.client.get(self.url, {"ordering": "-speed_mph"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -702,7 +664,7 @@ class TestRideAPIEdgeCases(EnhancedAPITestCase):
def test__ride_list__empty_database__returns_empty_list(self):
"""Test API behavior with no rides in database."""
# This depends on existing data, just verify no error
response = self.client.get('/api/v1/rides/')
response = self.client.get("/api/v1/rides/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -716,20 +678,20 @@ class TestRideAPIEdgeCases(EnhancedAPITestCase):
]
for search_term in special_searches:
response = self.client.get('/api/v1/rides/', {'search': search_term})
response = self.client.get("/api/v1/rides/", {"search": search_term})
# Should not crash
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])
def test__ride_list__extreme_pagination__handled_safely(self):
"""Test extreme pagination values."""
response = self.client.get('/api/v1/rides/', {'page': 99999, 'page_size': 1000})
response = self.client.get("/api/v1/rides/", {"page": 99999, "page_size": 1000})
# Should handle gracefully
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND])
def test__ride_list__invalid_ordering__handled_safely(self):
"""Test invalid ordering parameter."""
response = self.client.get('/api/v1/rides/', {'ordering': 'invalid_field'})
response = self.client.get("/api/v1/rides/", {"ordering": "invalid_field"})
# Should use default ordering
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -749,7 +711,7 @@ class TestRideAPIQueryOptimization(EnhancedAPITestCase):
for _i in range(5):
RideFactory(park=self.park)
response = self.client.get('/api/v1/rides/')
response = self.client.get("/api/v1/rides/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -757,8 +719,8 @@ class TestRideAPIQueryOptimization(EnhancedAPITestCase):
"""Test that ride list handles larger datasets efficiently."""
# Create batch of rides
for i in range(10):
RideFactory(park=self.park, name=f'Ride {i}')
RideFactory(park=self.park, name=f"Ride {i}")
response = self.client.get('/api/v1/rides/')
response = self.client.get("/api/v1/rides/")
self.assertEqual(response.status_code, status.HTTP_200_OK)