mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-02 01:47:04 -05:00
feat: Implement initial schema and add various API, service, and management command enhancements across the application.
This commit is contained in:
@@ -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])
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user