mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 23:31:08 -05:00
Refactor test utilities and enhance ASGI settings
- Cleaned up and standardized assertions in ApiTestMixin for API response validation. - Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE. - Removed unused imports and improved formatting in settings.py. - Refactored URL patterns in urls.py for better readability and organization. - Enhanced view functions in views.py for consistency and clarity. - Added .flake8 configuration for linting and style enforcement. - Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
@@ -1 +1 @@
|
||||
# Parks app test suite
|
||||
# Parks app test suite
|
||||
|
||||
@@ -2,31 +2,29 @@
|
||||
Tests for park filtering functionality including search, status filtering,
|
||||
date ranges, and numeric validations.
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import timezone
|
||||
from datetime import date, timedelta
|
||||
|
||||
from parks.models import Park, ParkLocation
|
||||
from django.test import TestCase
|
||||
from datetime import date
|
||||
|
||||
from parks.models import Park, ParkLocation, Company
|
||||
from parks.filters import ParkFilter
|
||||
from parks.models.companies import Company
|
||||
|
||||
# NOTE: These tests need to be updated to work with the new ParkLocation model
|
||||
# instead of the generic Location model
|
||||
|
||||
|
||||
class ParkFilterTests(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set up test data for all filter tests"""
|
||||
# Create operators
|
||||
cls.operator1 = Company.objects.create(
|
||||
name="Thrilling Adventures Inc",
|
||||
slug="thrilling-adventures"
|
||||
name="Thrilling Adventures Inc", slug="thrilling-adventures"
|
||||
)
|
||||
cls.operator2 = Company.objects.create(
|
||||
name="Family Fun Corp",
|
||||
slug="family-fun"
|
||||
name="Family Fun Corp", slug="family-fun"
|
||||
)
|
||||
|
||||
|
||||
# Create parks with various attributes for testing all filters
|
||||
cls.park1 = Park.objects.create(
|
||||
name="Thrilling Adventures Park",
|
||||
@@ -37,7 +35,7 @@ class ParkFilterTests(TestCase):
|
||||
size_acres=100,
|
||||
ride_count=20,
|
||||
coaster_count=5,
|
||||
average_rating=4.5
|
||||
average_rating=4.5,
|
||||
)
|
||||
ParkLocation.objects.create(
|
||||
park=cls.park1,
|
||||
@@ -45,9 +43,9 @@ class ParkFilterTests(TestCase):
|
||||
city="Thrill City",
|
||||
state="Thrill State",
|
||||
country="USA",
|
||||
postal_code="12345"
|
||||
postal_code="12345",
|
||||
)
|
||||
|
||||
|
||||
cls.park2 = Park.objects.create(
|
||||
name="Family Fun Park",
|
||||
description="Family-friendly entertainment and attractions",
|
||||
@@ -57,7 +55,7 @@ class ParkFilterTests(TestCase):
|
||||
size_acres=50,
|
||||
ride_count=15,
|
||||
coaster_count=2,
|
||||
average_rating=4.0
|
||||
average_rating=4.0,
|
||||
)
|
||||
ParkLocation.objects.create(
|
||||
park=cls.park2,
|
||||
@@ -65,159 +63,161 @@ class ParkFilterTests(TestCase):
|
||||
city="Fun City",
|
||||
state="Fun State",
|
||||
country="Canada",
|
||||
postal_code="54321"
|
||||
postal_code="54321",
|
||||
)
|
||||
|
||||
|
||||
# Park with minimal data for edge case testing
|
||||
cls.park3 = Park.objects.create(
|
||||
name="Incomplete Park",
|
||||
status="UNDER_CONSTRUCTION",
|
||||
operator=cls.operator1
|
||||
operator=cls.operator1,
|
||||
)
|
||||
|
||||
|
||||
def test_text_search(self):
|
||||
"""Test search functionality across different fields"""
|
||||
# Test name search
|
||||
queryset = ParkFilter(data={"search": "Thrilling"}).qs
|
||||
self.assertEqual(queryset.count(), 1)
|
||||
self.assertIn(self.park1, queryset)
|
||||
|
||||
|
||||
# Test description search
|
||||
queryset = ParkFilter(data={"search": "family-friendly"}).qs
|
||||
self.assertEqual(queryset.count(), 1)
|
||||
self.assertIn(self.park2, queryset)
|
||||
|
||||
|
||||
# Test location search
|
||||
queryset = ParkFilter(data={"search": "Thrill City"}).qs
|
||||
self.assertEqual(queryset.count(), 1)
|
||||
self.assertIn(self.park1, queryset)
|
||||
|
||||
|
||||
# Test combined field search
|
||||
queryset = ParkFilter(data={"search": "Park"}).qs
|
||||
self.assertEqual(queryset.count(), 3)
|
||||
|
||||
|
||||
# Test empty search
|
||||
queryset = ParkFilter(data={}).qs
|
||||
self.assertEqual(queryset.count(), 3)
|
||||
|
||||
def test_status_filtering(self):
|
||||
"""Test status filter with various values"""
|
||||
# Test each status
|
||||
status_tests = {
|
||||
"OPERATING": [self.park1],
|
||||
"CLOSED_TEMP": [self.park2],
|
||||
"UNDER_CONSTRUCTION": [self.park3]
|
||||
"UNDER_CONSTRUCTION": [self.park3],
|
||||
}
|
||||
|
||||
|
||||
for status, expected_parks in status_tests.items():
|
||||
queryset = ParkFilter(data={"status": status}).qs
|
||||
self.assertEqual(queryset.count(), len(expected_parks))
|
||||
for park in expected_parks:
|
||||
self.assertIn(park, queryset)
|
||||
|
||||
|
||||
# Test empty status (should return all)
|
||||
queryset = ParkFilter(data={}).qs
|
||||
self.assertEqual(queryset.count(), 3)
|
||||
|
||||
|
||||
# Test empty string status (should return all)
|
||||
queryset = ParkFilter(data={"status": ""}).qs
|
||||
self.assertEqual(queryset.count(), 3)
|
||||
|
||||
|
||||
# Test invalid status (should return no results)
|
||||
queryset = ParkFilter(data={"status": "INVALID"}).qs
|
||||
self.assertEqual(queryset.count(), 0)
|
||||
|
||||
|
||||
def test_date_range_filtering(self):
|
||||
"""Test date range filter functionality"""
|
||||
# Test various date range scenarios
|
||||
test_cases = [
|
||||
# Start date only
|
||||
({
|
||||
"opening_date_after": "2019-01-01"
|
||||
}, [self.park1]),
|
||||
|
||||
({"opening_date_after": "2019-01-01"}, [self.park1]),
|
||||
# End date only
|
||||
({
|
||||
"opening_date_before": "2016-01-01"
|
||||
}, [self.park2]),
|
||||
|
||||
({"opening_date_before": "2016-01-01"}, [self.park2]),
|
||||
# Date range including one park
|
||||
({
|
||||
"opening_date_after": "2014-01-01",
|
||||
"opening_date_before": "2016-01-01"
|
||||
}, [self.park2]),
|
||||
|
||||
(
|
||||
{
|
||||
"opening_date_after": "2014-01-01",
|
||||
"opening_date_before": "2016-01-01",
|
||||
},
|
||||
[self.park2],
|
||||
),
|
||||
# Date range including multiple parks
|
||||
({
|
||||
"opening_date_after": "2014-01-01",
|
||||
"opening_date_before": "2022-01-01"
|
||||
}, [self.park1, self.park2]),
|
||||
|
||||
(
|
||||
{
|
||||
"opening_date_after": "2014-01-01",
|
||||
"opening_date_before": "2022-01-01",
|
||||
},
|
||||
[self.park1, self.park2],
|
||||
),
|
||||
# Empty filter (should return all)
|
||||
({}, [self.park1, self.park2, self.park3]),
|
||||
|
||||
# Future date (should return none)
|
||||
({
|
||||
"opening_date_after": "2030-01-01"
|
||||
}, []),
|
||||
({"opening_date_after": "2030-01-01"}, []),
|
||||
]
|
||||
|
||||
|
||||
for filter_data, expected_parks in test_cases:
|
||||
queryset = ParkFilter(data=filter_data).qs
|
||||
self.assertEqual(
|
||||
set(queryset),
|
||||
set(expected_parks),
|
||||
f"Failed for filter: {filter_data}"
|
||||
f"Failed for filter: {filter_data}",
|
||||
)
|
||||
|
||||
|
||||
# Test invalid date formats
|
||||
invalid_dates = [
|
||||
{"opening_date_after": "invalid-date"},
|
||||
{"opening_date_before": "2023-13-01"}, # Invalid month
|
||||
{"opening_date_after": "2023-01-32"}, # Invalid day
|
||||
{"opening_date_after": "2023-01-32"}, # Invalid day
|
||||
{"opening_date_before": "not-a-date"},
|
||||
]
|
||||
|
||||
|
||||
for invalid_data in invalid_dates:
|
||||
filter_instance = ParkFilter(data=invalid_data)
|
||||
self.assertFalse(
|
||||
filter_instance.is_valid(),
|
||||
f"Filter should be invalid for data: {invalid_data}"
|
||||
f"Filter should be invalid for data: {invalid_data}",
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_numeric_filtering(self):
|
||||
"""Test numeric filters with validation"""
|
||||
# Test minimum rides filter
|
||||
test_cases = [
|
||||
({"min_rides": "18"}, [self.park1]), # Only park1 has >= 18 rides
|
||||
({"min_rides": "10"}, [self.park1, self.park2]), # Both park1 and park2 have >= 10 rides
|
||||
({"min_rides": "0"}, [self.park1, self.park2, self.park3]), # All parks have >= 0 rides
|
||||
({}, [self.park1, self.park2, self.park3]), # No filter should return all
|
||||
(
|
||||
{"min_rides": "10"},
|
||||
[self.park1, self.park2],
|
||||
), # Both park1 and park2 have >= 10 rides
|
||||
(
|
||||
{"min_rides": "0"},
|
||||
[self.park1, self.park2, self.park3],
|
||||
), # All parks have >= 0 rides
|
||||
# No filter should return all
|
||||
({}, [self.park1, self.park2, self.park3]),
|
||||
]
|
||||
|
||||
|
||||
for filter_data, expected_parks in test_cases:
|
||||
queryset = ParkFilter(data=filter_data).qs
|
||||
self.assertEqual(
|
||||
set(queryset),
|
||||
set(expected_parks),
|
||||
f"Failed for filter: {filter_data}"
|
||||
f"Failed for filter: {filter_data}",
|
||||
)
|
||||
|
||||
|
||||
# Test coaster count filter
|
||||
queryset = ParkFilter(data={"min_coasters": "3"}).qs
|
||||
self.assertEqual(queryset.count(), 1)
|
||||
self.assertIn(self.park1, queryset)
|
||||
|
||||
|
||||
# Test size filter
|
||||
queryset = ParkFilter(data={"min_size": "75"}).qs
|
||||
self.assertEqual(queryset.count(), 1)
|
||||
self.assertIn(self.park1, queryset)
|
||||
|
||||
|
||||
# Test validation
|
||||
invalid_values = ["-1", "invalid", "0.5"]
|
||||
for value in invalid_values:
|
||||
filter_instance = ParkFilter(data={"min_rides": value})
|
||||
self.assertFalse(
|
||||
filter_instance.is_valid(),
|
||||
f"Filter should be invalid for value: {value}"
|
||||
)
|
||||
f"Filter should be invalid for value: {value}",
|
||||
)
|
||||
|
||||
@@ -2,33 +2,29 @@
|
||||
Tests for park models functionality including CRUD operations,
|
||||
slug handling, status management, and location integration.
|
||||
"""
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
from django.utils import timezone
|
||||
from datetime import date
|
||||
|
||||
from parks.models import Park, ParkArea, ParkLocation
|
||||
from parks.models.companies import Company
|
||||
from django.test import TestCase
|
||||
from django.db import IntegrityError
|
||||
|
||||
from parks.models import Park, ParkArea, ParkLocation, Company
|
||||
|
||||
# NOTE: These tests need to be updated to work with the new ParkLocation model
|
||||
# instead of the generic Location model
|
||||
|
||||
|
||||
class ParkModelTests(TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
self.operator = Company.objects.create(
|
||||
name="Test Company",
|
||||
slug="test-company"
|
||||
)
|
||||
|
||||
self.operator = Company.objects.create(name="Test Company", slug="test-company")
|
||||
|
||||
# Create a basic park
|
||||
self.park = Park.objects.create(
|
||||
name="Test Park",
|
||||
description="A test park",
|
||||
status="OPERATING",
|
||||
operator=self.operator
|
||||
operator=self.operator,
|
||||
)
|
||||
|
||||
|
||||
# Create location for the park
|
||||
self.location = ParkLocation.objects.create(
|
||||
park=self.park,
|
||||
@@ -53,7 +49,7 @@ class ParkModelTests(TestCase):
|
||||
park = Park.objects.create(
|
||||
name="Another Test Park",
|
||||
status="OPERATING",
|
||||
operator=self.operator
|
||||
operator=self.operator,
|
||||
)
|
||||
self.assertEqual(park.slug, "another-test-park")
|
||||
|
||||
@@ -62,40 +58,40 @@ class ParkModelTests(TestCase):
|
||||
from django.db import transaction
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from core.history import HistoricalSlug
|
||||
|
||||
|
||||
with transaction.atomic():
|
||||
# Create initial park with a specific name/slug
|
||||
park = Park.objects.create(
|
||||
name="Original Park Name",
|
||||
description="Test description",
|
||||
status="OPERATING",
|
||||
operator=self.operator
|
||||
operator=self.operator,
|
||||
)
|
||||
original_slug = park.slug
|
||||
print(f"\nInitial park created with slug: {original_slug}")
|
||||
|
||||
|
||||
# Ensure we have a save to trigger history
|
||||
park.save()
|
||||
|
||||
|
||||
# Modify name to trigger slug change
|
||||
park.name = "Updated Park Name"
|
||||
park.save()
|
||||
new_slug = park.slug
|
||||
print(f"Park updated with new slug: {new_slug}")
|
||||
|
||||
|
||||
# Check HistoricalSlug records
|
||||
historical_slugs = HistoricalSlug.objects.filter(
|
||||
content_type=ContentType.objects.get_for_model(Park),
|
||||
object_id=park.id
|
||||
object_id=park.id,
|
||||
)
|
||||
print(f"Historical slug records: {[h.slug for h in historical_slugs]}")
|
||||
|
||||
|
||||
# Check pghistory records
|
||||
event_model = getattr(Park, 'event_model', None)
|
||||
event_model = getattr(Park, "event_model", None)
|
||||
if event_model:
|
||||
historical_records = event_model.objects.filter(
|
||||
pgh_obj_id=park.id
|
||||
).order_by('-pgh_created_at')
|
||||
).order_by("-pgh_created_at")
|
||||
print(f"\nPG History records:")
|
||||
for record in historical_records:
|
||||
print(f"- Event ID: {record.pgh_id}")
|
||||
@@ -104,56 +100,57 @@ class ParkModelTests(TestCase):
|
||||
print(f" Created At: {record.pgh_created_at}")
|
||||
else:
|
||||
print("\nNo pghistory event model available")
|
||||
|
||||
|
||||
# Try to find by old slug
|
||||
found_park, is_historical = Park.get_by_slug(original_slug)
|
||||
self.assertEqual(found_park.id, park.id)
|
||||
print(f"Found park by old slug: {found_park.slug}, is_historical: {is_historical}")
|
||||
print(
|
||||
f"Found park by old slug: {
|
||||
found_park.slug}, is_historical: {is_historical}"
|
||||
)
|
||||
self.assertTrue(is_historical)
|
||||
|
||||
|
||||
# Try current slug
|
||||
found_park, is_historical = Park.get_by_slug(new_slug)
|
||||
self.assertEqual(found_park.id, park.id)
|
||||
print(f"Found park by new slug: {found_park.slug}, is_historical: {is_historical}")
|
||||
print(
|
||||
f"Found park by new slug: {
|
||||
found_park.slug}, is_historical: {is_historical}"
|
||||
)
|
||||
self.assertFalse(is_historical)
|
||||
|
||||
def test_status_color_mapping(self):
|
||||
"""Test status color class mapping"""
|
||||
status_tests = {
|
||||
'OPERATING': 'bg-green-100 text-green-800',
|
||||
'CLOSED_TEMP': 'bg-yellow-100 text-yellow-800',
|
||||
'CLOSED_PERM': 'bg-red-100 text-red-800',
|
||||
'UNDER_CONSTRUCTION': 'bg-blue-100 text-blue-800',
|
||||
'DEMOLISHED': 'bg-gray-100 text-gray-800',
|
||||
'RELOCATED': 'bg-purple-100 text-purple-800'
|
||||
"OPERATING": "bg-green-100 text-green-800",
|
||||
"CLOSED_TEMP": "bg-yellow-100 text-yellow-800",
|
||||
"CLOSED_PERM": "bg-red-100 text-red-800",
|
||||
"UNDER_CONSTRUCTION": "bg-blue-100 text-blue-800",
|
||||
"DEMOLISHED": "bg-gray-100 text-gray-800",
|
||||
"RELOCATED": "bg-purple-100 text-purple-800",
|
||||
}
|
||||
|
||||
|
||||
for status, expected_color in status_tests.items():
|
||||
self.park.status = status
|
||||
self.assertEqual(self.park.get_status_color(), expected_color)
|
||||
|
||||
|
||||
def test_absolute_url(self):
|
||||
"""Test get_absolute_url method"""
|
||||
expected_url = f"/parks/{self.park.slug}/"
|
||||
self.assertEqual(self.park.get_absolute_url(), expected_url)
|
||||
|
||||
|
||||
class ParkAreaModelTests(TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data"""
|
||||
self.operator = Company.objects.create(
|
||||
name="Test Company 2",
|
||||
slug="test-company-2"
|
||||
name="Test Company 2", slug="test-company-2"
|
||||
)
|
||||
self.park = Park.objects.create(
|
||||
name="Test Park",
|
||||
status="OPERATING",
|
||||
operator=self.operator
|
||||
name="Test Park", status="OPERATING", operator=self.operator
|
||||
)
|
||||
self.area = ParkArea.objects.create(
|
||||
park=self.park,
|
||||
name="Test Area",
|
||||
description="A test area"
|
||||
park=self.park, name="Test Area", description="A test area"
|
||||
)
|
||||
|
||||
def test_area_creation(self):
|
||||
@@ -162,23 +159,18 @@ class ParkAreaModelTests(TestCase):
|
||||
self.assertEqual(self.area.slug, "test-area")
|
||||
self.assertEqual(self.area.park, self.park)
|
||||
|
||||
|
||||
def test_unique_together_constraint(self):
|
||||
"""Test unique_together constraint for park and slug"""
|
||||
from django.db import transaction
|
||||
|
||||
|
||||
# Try to create area with same slug in same park
|
||||
with transaction.atomic():
|
||||
with self.assertRaises(IntegrityError):
|
||||
ParkArea.objects.create(
|
||||
park=self.park,
|
||||
name="Test Area" # Will generate same slug
|
||||
park=self.park, name="Test Area" # Will generate same slug
|
||||
)
|
||||
|
||||
|
||||
# Should be able to use same name in different park
|
||||
other_park = Park.objects.create(name="Other Park", operator=self.operator)
|
||||
area = ParkArea.objects.create(
|
||||
park=other_park,
|
||||
name="Test Area"
|
||||
)
|
||||
area = ParkArea.objects.create(park=other_park, name="Test Area")
|
||||
self.assertEqual(area.slug, "test-area")
|
||||
|
||||
@@ -16,8 +16,8 @@ class TestParkSearch:
|
||||
park3 = Park.objects.create(name="Test Garden")
|
||||
|
||||
# Get autocomplete results
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': 'Test'})
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Test"})
|
||||
|
||||
# Check response
|
||||
assert response.status_code == 200
|
||||
@@ -35,18 +35,15 @@ class TestParkSearch:
|
||||
"""Test ParkAutocomplete configuration"""
|
||||
ac = ParkAutocomplete()
|
||||
assert ac.model == Park
|
||||
assert 'name' in ac.search_attrs
|
||||
assert "name" in ac.search_attrs
|
||||
|
||||
def test_search_with_filters(self, client: Client):
|
||||
"""Test search works with filters"""
|
||||
park = Park.objects.create(name="Test Park", status="OPERATING")
|
||||
|
||||
# Search with status filter
|
||||
url = reverse('parks:park_list')
|
||||
response = client.get(url, {
|
||||
'park': str(park.pk),
|
||||
'status': 'OPERATING'
|
||||
})
|
||||
url = reverse("parks:park_list")
|
||||
response = client.get(url, {"park": str(park.pk), "status": "OPERATING"})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert park.name in response.content.decode()
|
||||
@@ -56,7 +53,7 @@ class TestParkSearch:
|
||||
Park.objects.create(name="Test Park")
|
||||
Park.objects.create(name="Another Park")
|
||||
|
||||
url = reverse('parks:park_list')
|
||||
url = reverse("parks:park_list")
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -69,8 +66,8 @@ class TestParkSearch:
|
||||
Park.objects.create(name="Adventure World")
|
||||
Park.objects.create(name="Water Adventure")
|
||||
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': 'Adv'})
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Adv"})
|
||||
|
||||
assert response.status_code == 200
|
||||
content = response.content.decode()
|
||||
@@ -81,12 +78,8 @@ class TestParkSearch:
|
||||
"""Test HTMX-specific request handling"""
|
||||
Park.objects.create(name="Test Park")
|
||||
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(
|
||||
url,
|
||||
{'search': 'Test'},
|
||||
HTTP_HX_REQUEST='true'
|
||||
)
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Test"}, HTTP_HX_REQUEST="true")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "Test Park" in response.content.decode()
|
||||
@@ -95,11 +88,8 @@ class TestParkSearch:
|
||||
"""Test view mode is maintained during search"""
|
||||
Park.objects.create(name="Test Park")
|
||||
|
||||
url = reverse('parks:park_list')
|
||||
response = client.get(url, {
|
||||
'park': 'Test',
|
||||
'view_mode': 'list'
|
||||
})
|
||||
url = reverse("parks:park_list")
|
||||
response = client.get(url, {"park": "Test", "view_mode": "list"})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'data-view-mode="list"' in response.content.decode()
|
||||
@@ -110,11 +100,11 @@ class TestParkSearch:
|
||||
for i in range(10):
|
||||
Park.objects.create(name=f"Test Park {i}")
|
||||
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': 'Test'})
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Test"})
|
||||
|
||||
content = response.content.decode()
|
||||
result_count = content.count('Test Park')
|
||||
result_count = content.count("Test Park")
|
||||
assert result_count == 8 # Verify limit is enforced
|
||||
|
||||
def test_search_json_format(self, client: Client):
|
||||
@@ -123,61 +113,61 @@ class TestParkSearch:
|
||||
name="Test Park",
|
||||
status="OPERATING",
|
||||
city="Test City",
|
||||
state="Test State"
|
||||
state="Test State",
|
||||
)
|
||||
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': 'Test'})
|
||||
|
||||
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Test"})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert 'results' in data
|
||||
assert len(data['results']) == 1
|
||||
|
||||
result = data['results'][0]
|
||||
assert result['id'] == str(park.pk)
|
||||
assert result['name'] == "Test Park"
|
||||
assert result['status'] == "Operating"
|
||||
assert result['location'] == park.formatted_location
|
||||
assert result['url'] == reverse('parks:park_detail', kwargs={'slug': park.slug})
|
||||
assert "results" in data
|
||||
assert len(data["results"]) == 1
|
||||
|
||||
result = data["results"][0]
|
||||
assert result["id"] == str(park.pk)
|
||||
assert result["name"] == "Test Park"
|
||||
assert result["status"] == "Operating"
|
||||
assert result["location"] == park.formatted_location
|
||||
assert result["url"] == reverse("parks:park_detail", kwargs={"slug": park.slug})
|
||||
|
||||
def test_empty_search_json(self, client: Client):
|
||||
"""Test empty search returns empty results array"""
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': ''})
|
||||
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": ""})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert 'results' in data
|
||||
assert len(data['results']) == 0
|
||||
assert "results" in data
|
||||
assert len(data["results"]) == 0
|
||||
|
||||
def test_search_format_validation(self, client: Client):
|
||||
"""Test that all fields are properly formatted in search results"""
|
||||
park = Park.objects.create(
|
||||
Park.objects.create(
|
||||
name="Test Park",
|
||||
status="OPERATING",
|
||||
city="Test City",
|
||||
state="Test State",
|
||||
country="Test Country"
|
||||
country="Test Country",
|
||||
)
|
||||
|
||||
expected_fields = {'id', 'name', 'status', 'location', 'url'}
|
||||
|
||||
url = reverse('parks:suggest_parks')
|
||||
response = client.get(url, {'search': 'Test'})
|
||||
|
||||
expected_fields = {"id", "name", "status", "location", "url"}
|
||||
|
||||
url = reverse("parks:suggest_parks")
|
||||
response = client.get(url, {"search": "Test"})
|
||||
data = response.json()
|
||||
result = data['results'][0]
|
||||
|
||||
result = data["results"][0]
|
||||
|
||||
# Check all expected fields are present
|
||||
assert set(result.keys()) == expected_fields
|
||||
|
||||
|
||||
# Check field types
|
||||
assert isinstance(result['id'], str)
|
||||
assert isinstance(result['name'], str)
|
||||
assert isinstance(result['status'], str)
|
||||
assert isinstance(result['location'], str)
|
||||
assert isinstance(result['url'], str)
|
||||
|
||||
assert isinstance(result["id"], str)
|
||||
assert isinstance(result["name"], str)
|
||||
assert isinstance(result["status"], str)
|
||||
assert isinstance(result["location"], str)
|
||||
assert isinstance(result["url"], str)
|
||||
|
||||
# Check formatted location includes city and state
|
||||
assert 'Test City' in result['location']
|
||||
assert 'Test State' in result['location']
|
||||
assert "Test City" in result["location"]
|
||||
assert "Test State" in result["location"]
|
||||
|
||||
Reference in New Issue
Block a user