mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-29 19:07:01 -05:00
feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.
This commit is contained in:
@@ -21,18 +21,18 @@ dependencies are not installed or if running in CI without browser support.
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from django.test import TestCase, LiveServerTestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import LiveServerTestCase, TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
# Check if selenium and axe are available
|
||||
try:
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
HAS_SELENIUM = True
|
||||
except ImportError:
|
||||
HAS_SELENIUM = False
|
||||
@@ -158,7 +158,7 @@ class WCAGComplianceTests(AccessibilityTestMixin, LiveServerTestCase):
|
||||
cls.driver = webdriver.Chrome(options=chrome_options)
|
||||
cls.driver.implicitly_wait(10)
|
||||
except Exception as e:
|
||||
raise unittest.SkipTest(f"Chrome WebDriver not available: {e}")
|
||||
raise unittest.SkipTest(f"Chrome WebDriver not available: {e}") from None
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
||||
@@ -16,17 +16,11 @@ This module provides extensive test coverage for:
|
||||
Test patterns follow Django styleguide conventions.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
SuperUserFactory,
|
||||
)
|
||||
from tests.test_utils import EnhancedAPITestCase
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ with proper error codes, messages, and details.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
class ErrorResponseFormatTestCase(TestCase):
|
||||
|
||||
@@ -6,8 +6,8 @@ similar endpoints and behave as expected.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
class FilterParameterNamingTestCase(TestCase):
|
||||
@@ -137,7 +137,7 @@ class FilterMetadataTestCase(TestCase):
|
||||
if response.status_code == status.HTTP_200_OK:
|
||||
data = response.json()
|
||||
if data.get("data") and data["data"].get("categorical"):
|
||||
for field, options in data["data"]["categorical"].items():
|
||||
for _field, options in data["data"]["categorical"].items():
|
||||
if isinstance(options, list) and options:
|
||||
option = options[0]
|
||||
# Each option should have value and label
|
||||
|
||||
@@ -6,8 +6,8 @@ metadata including count, next, previous, page_size, current_page, and total_pag
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
class PaginationMetadataTestCase(TestCase):
|
||||
|
||||
@@ -13,20 +13,18 @@ Test patterns follow Django styleguide conventions with:
|
||||
- Permission and authorization testing
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from unittest.mock import patch
|
||||
|
||||
from apps.parks.models import Park, ParkPhoto
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.parks.models import Park
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
CompanyFactory,
|
||||
ParkFactory,
|
||||
StaffUserFactory,
|
||||
SuperUserFactory,
|
||||
ParkFactory,
|
||||
CompanyFactory,
|
||||
UserFactory,
|
||||
)
|
||||
from tests.test_utils import EnhancedAPITestCase
|
||||
|
||||
@@ -471,7 +469,7 @@ class TestParkAPIQueryOptimization(EnhancedAPITestCase):
|
||||
def test__park_list__uses_select_related(self):
|
||||
"""Test that park list uses select_related for optimization."""
|
||||
# Create multiple parks
|
||||
for i in range(5):
|
||||
for _i in range(5):
|
||||
ParkFactory(operator=self.operator)
|
||||
|
||||
url = '/api/v1/parks/hybrid/'
|
||||
|
||||
@@ -5,10 +5,9 @@ These tests verify that all API endpoints return responses in the standardized
|
||||
format with proper success/error indicators, data nesting, and error codes.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
class ResponseFormatTestCase(TestCase):
|
||||
|
||||
@@ -15,24 +15,18 @@ This module provides extensive test coverage for:
|
||||
Test patterns follow Django styleguide conventions.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
SuperUserFactory,
|
||||
CoasterFactory,
|
||||
DesignerCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
CoasterFactory,
|
||||
CompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
RideModelFactory,
|
||||
StaffUserFactory,
|
||||
UserFactory,
|
||||
)
|
||||
from tests.test_utils import EnhancedAPITestCase
|
||||
|
||||
@@ -752,7 +746,7 @@ class TestRideAPIQueryOptimization(EnhancedAPITestCase):
|
||||
def test__ride_list__uses_select_related(self):
|
||||
"""Test that ride list uses select_related for optimization."""
|
||||
# Create multiple rides
|
||||
for i in range(5):
|
||||
for _i in range(5):
|
||||
RideFactory(park=self.park)
|
||||
|
||||
response = self.client.get('/api/v1/rides/')
|
||||
|
||||
@@ -9,7 +9,6 @@ import os
|
||||
|
||||
import django
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
|
||||
# Configure Django settings before any tests run
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.django.test")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import pytest
|
||||
import contextlib
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page
|
||||
|
||||
|
||||
@@ -212,9 +214,10 @@ def admin_page(page: Page, live_server, setup_test_data):
|
||||
def submission_pending(db):
|
||||
"""Create a pending EditSubmission for FSM testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -246,19 +249,18 @@ def submission_pending(db):
|
||||
yield submission
|
||||
|
||||
# Cleanup
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
submission.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def submission_approved(db):
|
||||
"""Create an approved EditSubmission for FSM testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -285,10 +287,8 @@ def submission_approved(db):
|
||||
|
||||
yield submission
|
||||
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
submission.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -322,9 +322,10 @@ def park_closed_temp(db):
|
||||
@pytest.fixture
|
||||
def park_closed_perm(db):
|
||||
"""Create a permanently closed Park for FSM testing."""
|
||||
from tests.factories import ParkFactory
|
||||
from datetime import date, timedelta
|
||||
|
||||
from tests.factories import ParkFactory
|
||||
|
||||
park = ParkFactory(
|
||||
name="FSM Test Park Closed Perm",
|
||||
slug="fsm-test-park-closed-perm",
|
||||
@@ -368,9 +369,10 @@ def ride_sbno(db, park_operating):
|
||||
@pytest.fixture
|
||||
def ride_closed_perm(db, park_operating):
|
||||
"""Create a permanently closed Ride for FSM testing."""
|
||||
from tests.factories import RideFactory
|
||||
from datetime import date, timedelta
|
||||
|
||||
from tests.factories import RideFactory
|
||||
|
||||
ride = RideFactory(
|
||||
name="FSM Test Ride Closed Perm",
|
||||
slug="fsm-test-ride-closed-perm",
|
||||
@@ -386,6 +388,7 @@ def ride_closed_perm(db, park_operating):
|
||||
def queue_item_pending(db):
|
||||
"""Create a pending ModerationQueue item for FSM testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.moderation.models import ModerationQueue
|
||||
|
||||
User = get_user_model()
|
||||
@@ -406,16 +409,15 @@ def queue_item_pending(db):
|
||||
|
||||
yield queue_item
|
||||
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
queue_item.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bulk_operation_pending(db):
|
||||
"""Create a pending BulkOperation for FSM testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.moderation.models import BulkOperation
|
||||
|
||||
User = get_user_model()
|
||||
@@ -437,10 +439,8 @@ def bulk_operation_pending(db):
|
||||
|
||||
yield operation
|
||||
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
operation.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from playwright.sync_api import expect, Page
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_login_page(page: Page):
|
||||
|
||||
@@ -17,6 +17,8 @@ These tests verify:
|
||||
- User-friendly error messages are displayed
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
@@ -73,9 +75,10 @@ class TestInvalidTransitionErrors:
|
||||
):
|
||||
"""Test that trying to approve an already-approved submission shows error."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -351,9 +354,10 @@ class TestConfirmationDialogs:
|
||||
):
|
||||
"""Test that confirmation dialog appears for reject transition."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -414,9 +418,10 @@ class TestConfirmationDialogs:
|
||||
):
|
||||
"""Test that canceling the confirmation dialog prevents the transition."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -472,9 +477,10 @@ class TestConfirmationDialogs:
|
||||
):
|
||||
"""Test that accepting the confirmation dialog executes the transition."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -629,7 +635,7 @@ class TestToastNotificationBehavior:
|
||||
expect(toast).to_be_visible(timeout=5000)
|
||||
|
||||
# Should have error/danger styling (red)
|
||||
expect(toast).to_have_class(/error|danger|bg-red|text-red/)
|
||||
expect(toast).to_have_class(re.compile(r"error|danger|bg-red|text-red"))
|
||||
|
||||
def test_success_toast_has_correct_styling(
|
||||
self, mod_page: Page, live_server, db
|
||||
@@ -665,4 +671,4 @@ class TestToastNotificationBehavior:
|
||||
expect(toast).to_be_visible(timeout=5000)
|
||||
|
||||
# Should have success styling (green)
|
||||
expect(toast).to_have_class(/success|bg-green|text-green/)
|
||||
expect(toast).to_have_class(re.compile(r"success|bg-green|text-green"))
|
||||
|
||||
@@ -89,9 +89,10 @@ class TestRegularUserPermissions:
|
||||
):
|
||||
"""Test that regular users cannot approve submissions."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -229,9 +230,10 @@ class TestModeratorPermissions:
|
||||
):
|
||||
"""Test that moderators CAN see and use approve button."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -501,5 +503,5 @@ class TestTransitionButtonVisibility:
|
||||
assert visible, "Expected demolish or relocate button for CLOSED_PERM state"
|
||||
|
||||
# Reopen should still be visible to restore to operating
|
||||
reopen_btn = status_actions.get_by_role("button", name="Reopen")
|
||||
status_actions.get_by_role("button", name="Reopen")
|
||||
# May or may not be visible depending on FSM configuration
|
||||
|
||||
@@ -15,14 +15,15 @@ These tests verify:
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pending_submission(db):
|
||||
"""Create a pending EditSubmission for testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
|
||||
@@ -63,9 +64,10 @@ def pending_submission(db):
|
||||
def pending_photo_submission(db):
|
||||
"""Create a pending PhotoSubmission for testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.moderation.models import PhotoSubmission
|
||||
from apps.parks.models import Park
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -146,7 +148,6 @@ class TestEditSubmissionTransitions:
|
||||
expect(status_badge).to_contain_text("Approved")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import EditSubmission
|
||||
pending_submission.refresh_from_db()
|
||||
assert pending_submission.status == "APPROVED"
|
||||
|
||||
@@ -180,7 +181,6 @@ class TestEditSubmissionTransitions:
|
||||
expect(status_badge).to_contain_text("Rejected")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import EditSubmission
|
||||
pending_submission.refresh_from_db()
|
||||
assert pending_submission.status == "REJECTED"
|
||||
|
||||
@@ -214,7 +214,6 @@ class TestEditSubmissionTransitions:
|
||||
expect(status_badge).to_contain_text("Escalated")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import EditSubmission
|
||||
pending_submission.refresh_from_db()
|
||||
assert pending_submission.status == "ESCALATED"
|
||||
|
||||
@@ -254,7 +253,6 @@ class TestPhotoSubmissionTransitions:
|
||||
expect(toast).to_contain_text("approved")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import PhotoSubmission
|
||||
pending_photo_submission.refresh_from_db()
|
||||
assert pending_photo_submission.status == "APPROVED"
|
||||
|
||||
@@ -290,7 +288,6 @@ class TestPhotoSubmissionTransitions:
|
||||
expect(toast).to_contain_text("rejected")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import PhotoSubmission
|
||||
pending_photo_submission.refresh_from_db()
|
||||
assert pending_photo_submission.status == "REJECTED"
|
||||
|
||||
@@ -302,6 +299,7 @@ class TestModerationQueueTransitions:
|
||||
def pending_queue_item(self, db):
|
||||
"""Create a pending ModerationQueue item for testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.moderation.models import ModerationQueue
|
||||
|
||||
User = get_user_model()
|
||||
@@ -346,7 +344,6 @@ class TestModerationQueueTransitions:
|
||||
expect(status_badge).to_contain_text("In Progress", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import ModerationQueue
|
||||
pending_queue_item.refresh_from_db()
|
||||
assert pending_queue_item.status == "IN_PROGRESS"
|
||||
|
||||
@@ -376,7 +373,6 @@ class TestModerationQueueTransitions:
|
||||
toast = mod_page.locator('[data-toast]')
|
||||
expect(toast).to_be_visible(timeout=5000)
|
||||
|
||||
from apps.moderation.models import ModerationQueue
|
||||
pending_queue_item.refresh_from_db()
|
||||
assert pending_queue_item.status == "COMPLETED"
|
||||
|
||||
@@ -388,6 +384,7 @@ class TestBulkOperationTransitions:
|
||||
def pending_bulk_operation(self, db):
|
||||
"""Create a pending BulkOperation for testing."""
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from apps.moderation.models import BulkOperation
|
||||
|
||||
User = get_user_model()
|
||||
@@ -438,7 +435,6 @@ class TestBulkOperationTransitions:
|
||||
expect(toast).to_contain_text("cancel")
|
||||
|
||||
# Verify database state
|
||||
from apps.moderation.models import BulkOperation
|
||||
pending_bulk_operation.refresh_from_db()
|
||||
assert pending_bulk_operation.status == "CANCELLED"
|
||||
|
||||
@@ -470,7 +466,7 @@ class TestTransitionLoadingStates:
|
||||
|
||||
# Check for htmx-indicator visibility (may be brief)
|
||||
# The indicator should become visible during the request
|
||||
loading_indicator = submission_row.locator('.htmx-indicator')
|
||||
submission_row.locator('.htmx-indicator')
|
||||
|
||||
# Wait for transition to complete
|
||||
toast = mod_page.locator('[data-toast]')
|
||||
|
||||
@@ -83,7 +83,7 @@ class TestParkDetailPage:
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
# Look for rides section/tab
|
||||
rides_section = page.locator(
|
||||
page.locator(
|
||||
"[data-testid='rides-section'], #rides, [role='tabpanel']"
|
||||
)
|
||||
|
||||
@@ -162,7 +162,7 @@ class TestParkNavigation:
|
||||
# Click parks link in breadcrumb
|
||||
breadcrumb.get_by_role("link", name="Parks").click()
|
||||
|
||||
expect(page).to_have_url(f"**/parks/**")
|
||||
expect(page).to_have_url("**/parks/**")
|
||||
|
||||
def test__back_button__returns_to_previous_page(
|
||||
self, page: Page, live_server, parks_data
|
||||
@@ -179,4 +179,4 @@ class TestParkNavigation:
|
||||
# Go back
|
||||
page.go_back()
|
||||
|
||||
expect(page).to_have_url(f"**/parks/**")
|
||||
expect(page).to_have_url("**/parks/**")
|
||||
|
||||
@@ -13,15 +13,16 @@ These tests verify:
|
||||
- StateLog entry created in database
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import date, timedelta
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def operating_park(db):
|
||||
"""Create an operating Park for testing status transitions."""
|
||||
from apps.parks.models import Park
|
||||
from tests.factories import ParkFactory
|
||||
|
||||
# Use factory to create a complete park
|
||||
@@ -93,7 +94,6 @@ class TestParkStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Temporarily Closed", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.parks.models import Park
|
||||
operating_park.refresh_from_db()
|
||||
assert operating_park.status == "CLOSED_TEMP"
|
||||
|
||||
@@ -127,7 +127,6 @@ class TestParkStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Operating", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.parks.models import Park
|
||||
operating_park.refresh_from_db()
|
||||
assert operating_park.status == "OPERATING"
|
||||
|
||||
@@ -165,7 +164,6 @@ class TestParkStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Permanently Closed", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.parks.models import Park
|
||||
operating_park.refresh_from_db()
|
||||
assert operating_park.status == "CLOSED_PERM"
|
||||
|
||||
@@ -206,7 +204,6 @@ class TestParkStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Demolished", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.parks.models import Park
|
||||
operating_park.refresh_from_db()
|
||||
assert operating_park.status == "DEMOLISHED"
|
||||
|
||||
@@ -274,7 +271,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Temporarily Closed", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "CLOSED_TEMP"
|
||||
|
||||
@@ -310,7 +306,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("SBNO", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "SBNO"
|
||||
|
||||
@@ -344,7 +339,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Operating", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "OPERATING"
|
||||
|
||||
@@ -384,7 +378,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Permanently Closed", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "CLOSED_PERM"
|
||||
|
||||
@@ -429,7 +422,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Demolished", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "DEMOLISHED"
|
||||
|
||||
@@ -474,7 +466,6 @@ class TestRideStatusTransitions:
|
||||
expect(status_badge).to_contain_text("Relocated", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "RELOCATED"
|
||||
|
||||
@@ -507,7 +498,6 @@ class TestRideClosingWorkflow:
|
||||
expect(status_badge).to_contain_text("Closing", timeout=5000)
|
||||
|
||||
# Verify database state
|
||||
from apps.rides.models import Ride
|
||||
operating_ride.refresh_from_db()
|
||||
assert operating_ride.status == "CLOSING"
|
||||
else:
|
||||
@@ -549,7 +539,7 @@ class TestStatusBadgeStyling:
|
||||
mod_page.wait_for_load_state("networkidle")
|
||||
|
||||
status_badge = mod_page.locator('[data-status-badge]')
|
||||
expect(status_badge).to_have_class(/bg-green|text-green|success/)
|
||||
expect(status_badge).to_have_class(re.compile(r"bg-green|text-green|success"))
|
||||
|
||||
def test_closed_temp_status_badge_style(
|
||||
self, mod_page: Page, operating_park, live_server
|
||||
@@ -562,7 +552,7 @@ class TestStatusBadgeStyling:
|
||||
mod_page.wait_for_load_state("networkidle")
|
||||
|
||||
status_badge = mod_page.locator('[data-status-badge]')
|
||||
expect(status_badge).to_have_class(/bg-yellow|text-yellow|warning/)
|
||||
expect(status_badge).to_have_class(re.compile(r"bg-yellow|text-yellow|warning"))
|
||||
|
||||
def test_closed_perm_status_badge_style(
|
||||
self, mod_page: Page, operating_park, live_server
|
||||
@@ -575,7 +565,7 @@ class TestStatusBadgeStyling:
|
||||
mod_page.wait_for_load_state("networkidle")
|
||||
|
||||
status_badge = mod_page.locator('[data-status-badge]')
|
||||
expect(status_badge).to_have_class(/bg-red|text-red|danger/)
|
||||
expect(status_badge).to_have_class(re.compile(r"bg-red|text-red|danger"))
|
||||
|
||||
def test_demolished_status_badge_style(
|
||||
self, mod_page: Page, operating_park, live_server
|
||||
@@ -588,4 +578,4 @@ class TestStatusBadgeStyling:
|
||||
mod_page.wait_for_load_state("networkidle")
|
||||
|
||||
status_badge = mod_page.locator('[data-status-badge]')
|
||||
expect(status_badge).to_have_class(/bg-gray|text-gray|muted/)
|
||||
expect(status_badge).to_have_class(re.compile(r"bg-gray|text-gray|muted"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from playwright.sync_api import expect, Page
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_parks_list_page(page: Page):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from playwright.sync_api import expect, Page
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_profile_page(page: Page):
|
||||
|
||||
@@ -168,7 +168,6 @@ class TestReviewEditing:
|
||||
def test__own_review__shows_edit_button(self, auth_page: Page, live_server, test_review):
|
||||
"""Test user's own review shows edit button."""
|
||||
# Navigate to reviews after creating one
|
||||
park_url = auth_page.url
|
||||
|
||||
# Look for edit button on own review
|
||||
edit_button = auth_page.locator(
|
||||
@@ -324,7 +323,7 @@ class TestRideReviews:
|
||||
intensity_field = auth_page.locator(
|
||||
"select[name='intensity'], input[name='intensity']"
|
||||
)
|
||||
wait_time_field = auth_page.locator(
|
||||
auth_page.locator(
|
||||
"input[name='wait_time'], select[name='wait_time']"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from playwright.sync_api import expect, Page
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_reviews_list_page(page: Page):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from playwright.sync_api import expect, Page
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
def test_rides_list_page(page: Page):
|
||||
|
||||
@@ -4,11 +4,11 @@ Following Django styleguide pattern for test data creation using factory_boy.
|
||||
"""
|
||||
|
||||
import factory
|
||||
from factory import fuzzy
|
||||
from factory.django import DjangoModelFactory
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.utils.text import slugify
|
||||
from factory import fuzzy
|
||||
from factory.django import DjangoModelFactory
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
@@ -4,21 +4,18 @@ Tests for Park forms.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from decimal import Decimal
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.parks.forms import (
|
||||
ParkAutocomplete,
|
||||
ParkForm,
|
||||
ParkSearchForm,
|
||||
ParkAutocomplete,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
OperatorCompanyFactory,
|
||||
LocationFactory,
|
||||
ParkFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,21 +4,20 @@ Tests for Ride forms.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.rides.forms import (
|
||||
RideForm,
|
||||
RideSearchForm,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
DesignerCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
ParkAreaFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
RideModelFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ These are faster than E2E tests and don't require Playwright.
|
||||
"""
|
||||
|
||||
import json
|
||||
import pytest
|
||||
from django.test import TestCase, Client
|
||||
from django.urls import reverse
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -730,9 +730,10 @@ class TestFSMTransitionViewStateLog(TestCase):
|
||||
|
||||
def test_transition_creates_state_log(self):
|
||||
"""Test that FSM transition creates a StateLog entry."""
|
||||
from django_fsm_log.models import StateLog
|
||||
|
||||
from apps.moderation.models import EditSubmission
|
||||
from apps.parks.models import Park
|
||||
from django_fsm_log.models import StateLog
|
||||
|
||||
park = Park.objects.first()
|
||||
if not park:
|
||||
|
||||
@@ -5,19 +5,16 @@ These tests verify the complete state transition workflows for
|
||||
Parks and Rides using the FSM implementation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import date, timedelta
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from apps.parks.models import Park
|
||||
from apps.rides.models import Ride
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
|
||||
from tests.factories import (
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
UserFactory,
|
||||
ParkAreaFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,18 +6,15 @@ validation, location creation, and related operations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.db import transaction
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.parks.models import Park, ParkArea, ParkReview
|
||||
from apps.parks.forms import ParkForm
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
ParkAreaFactory,
|
||||
OperatorCompanyFactory,
|
||||
UserFactory,
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -61,9 +58,9 @@ class TestParkCreationWorkflow(TestCase):
|
||||
park = ParkFactory()
|
||||
|
||||
# Add areas
|
||||
area1 = ParkAreaFactory(park=park, name="Main Entrance")
|
||||
area2 = ParkAreaFactory(park=park, name="Thrill Zone")
|
||||
area3 = ParkAreaFactory(park=park, name="Kids Area")
|
||||
ParkAreaFactory(park=park, name="Main Entrance")
|
||||
ParkAreaFactory(park=park, name="Thrill Zone")
|
||||
ParkAreaFactory(park=park, name="Kids Area")
|
||||
|
||||
# Verify structure
|
||||
assert park.areas.count() == 3
|
||||
@@ -156,7 +153,7 @@ class TestParkReviewWorkflow(TestCase):
|
||||
|
||||
from tests.factories import ParkReviewFactory
|
||||
|
||||
review1 = ParkReviewFactory(park=park, user=user1, rating=10, is_published=True)
|
||||
ParkReviewFactory(park=park, user=user1, rating=10, is_published=True)
|
||||
review2 = ParkReviewFactory(park=park, user=user2, rating=2, is_published=True)
|
||||
|
||||
# Unpublish the low rating
|
||||
|
||||
@@ -5,22 +5,21 @@ These tests verify the complete workflow of photo uploads including
|
||||
validation, processing, and moderation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
from django.test import TestCase
|
||||
|
||||
import pytest
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.parks.models import ParkPhoto
|
||||
from apps.rides.models import RidePhoto
|
||||
from apps.parks.services.media_service import ParkMediaService
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
ParkPhotoFactory,
|
||||
RideFactory,
|
||||
RidePhotoFactory,
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -215,9 +214,9 @@ class TestRidePhotoWorkflow(TestCase):
|
||||
"""Test ride photos can have different types."""
|
||||
ride = RideFactory()
|
||||
|
||||
exterior = RidePhotoFactory(ride=ride, photo_type="exterior")
|
||||
queue = RidePhotoFactory(ride=ride, photo_type="queue")
|
||||
onride = RidePhotoFactory(ride=ride, photo_type="onride")
|
||||
RidePhotoFactory(ride=ride, photo_type="exterior")
|
||||
RidePhotoFactory(ride=ride, photo_type="queue")
|
||||
RidePhotoFactory(ride=ride, photo_type="onride")
|
||||
|
||||
assert ride.photos.filter(photo_type="exterior").count() == 1
|
||||
assert ride.photos.filter(photo_type="queue").count() == 1
|
||||
|
||||
@@ -4,31 +4,13 @@ Tests for Core managers and querysets.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from apps.core.managers import (
|
||||
BaseQuerySet,
|
||||
BaseManager,
|
||||
LocationQuerySet,
|
||||
LocationManager,
|
||||
ReviewableQuerySet,
|
||||
ReviewableManager,
|
||||
HierarchicalQuerySet,
|
||||
HierarchicalManager,
|
||||
TimestampedQuerySet,
|
||||
TimestampedManager,
|
||||
StatusQuerySet,
|
||||
StatusManager,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -4,32 +4,23 @@ Tests for Park managers and querysets.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
from apps.parks.models import Park, ParkArea, ParkReview, Company
|
||||
from apps.parks.managers import (
|
||||
ParkQuerySet,
|
||||
ParkManager,
|
||||
ParkAreaQuerySet,
|
||||
ParkAreaManager,
|
||||
ParkReviewQuerySet,
|
||||
ParkReviewManager,
|
||||
CompanyQuerySet,
|
||||
CompanyManager,
|
||||
)
|
||||
|
||||
from apps.parks.models import Company, Park, ParkArea, ParkReview
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
CoasterFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
OperatorCompanyFactory,
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
CoasterFactory,
|
||||
UserFactory,
|
||||
OperatorCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -290,7 +281,7 @@ class TestParkReviewQuerySet(TestCase):
|
||||
"""Test moderation_required filters reviews needing moderation."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
published = ParkReviewFactory(is_published=True, user=user1)
|
||||
ParkReviewFactory(is_published=True, user=user1)
|
||||
unpublished = ParkReviewFactory(is_published=False, user=user2)
|
||||
|
||||
result = ParkReview.objects.moderation_required()
|
||||
|
||||
@@ -6,29 +6,22 @@ Following Django styleguide pattern: test__<context>__<action>__<expected_outcom
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.rides.models import Ride, RideModel, RideReview
|
||||
from apps.rides.managers import (
|
||||
RideQuerySet,
|
||||
RideManager,
|
||||
RideModelQuerySet,
|
||||
RideModelManager,
|
||||
RideQuerySet,
|
||||
RideReviewQuerySet,
|
||||
RideReviewManager,
|
||||
RollerCoasterStatsQuerySet,
|
||||
RollerCoasterStatsManager,
|
||||
)
|
||||
|
||||
from apps.rides.models import Ride, RideModel, RideReview
|
||||
from tests.factories import (
|
||||
RideFactory,
|
||||
CoasterFactory,
|
||||
DesignerCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
RideModelFactory,
|
||||
RideReviewFactory,
|
||||
UserFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,10 @@ Tests for ContractValidationMiddleware.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase, RequestFactory, override_settings
|
||||
from django.http import JsonResponse, HttpResponse
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
|
||||
from apps.api.v1.middleware import (
|
||||
ContractValidationMiddleware,
|
||||
|
||||
@@ -4,29 +4,26 @@ Tests for Account serializers.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from apps.accounts.serializers import (
|
||||
UserSerializer,
|
||||
LoginSerializer,
|
||||
SignupSerializer,
|
||||
PasswordResetSerializer,
|
||||
PasswordChangeSerializer,
|
||||
PasswordResetSerializer,
|
||||
SignupSerializer,
|
||||
SocialProviderSerializer,
|
||||
UserSerializer,
|
||||
)
|
||||
|
||||
from apps.api.v1.accounts.serializers import (
|
||||
UserProfileCreateInputSerializer,
|
||||
UserProfileUpdateInputSerializer,
|
||||
UserProfileOutputSerializer,
|
||||
|
||||
UserProfileUpdateInputSerializer,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -169,7 +166,7 @@ class TestSignupSerializer(TestCase):
|
||||
|
||||
def test__validate_email__duplicate_email__returns_error(self):
|
||||
"""Test validation fails with duplicate email."""
|
||||
existing_user = UserFactory(email="existing@example.com")
|
||||
UserFactory(email="existing@example.com")
|
||||
data = {
|
||||
"username": "newuser",
|
||||
"email": "existing@example.com",
|
||||
@@ -185,7 +182,7 @@ class TestSignupSerializer(TestCase):
|
||||
|
||||
def test__validate_email__case_insensitive__returns_error(self):
|
||||
"""Test email validation is case insensitive."""
|
||||
existing_user = UserFactory(email="existing@example.com")
|
||||
UserFactory(email="existing@example.com")
|
||||
data = {
|
||||
"username": "newuser",
|
||||
"email": "EXISTING@EXAMPLE.COM",
|
||||
@@ -201,7 +198,7 @@ class TestSignupSerializer(TestCase):
|
||||
|
||||
def test__validate_username__duplicate_username__returns_error(self):
|
||||
"""Test validation fails with duplicate username."""
|
||||
existing_user = UserFactory(username="existinguser")
|
||||
UserFactory(username="existinguser")
|
||||
data = {
|
||||
"username": "existinguser",
|
||||
"email": "new@example.com",
|
||||
@@ -262,7 +259,7 @@ class TestPasswordResetSerializer(TestCase):
|
||||
|
||||
def test__validate__valid_email__returns_normalized_email(self):
|
||||
"""Test validation normalizes email."""
|
||||
user = UserFactory(email="test@example.com")
|
||||
UserFactory(email="test@example.com")
|
||||
data = {"email": " TEST@EXAMPLE.COM "}
|
||||
|
||||
serializer = PasswordResetSerializer(data=data)
|
||||
@@ -302,7 +299,7 @@ class TestPasswordResetSerializer(TestCase):
|
||||
@patch("apps.accounts.serializers.EmailService.send_email")
|
||||
def test__save__existing_user__sends_email(self, mock_send_email):
|
||||
"""Test save sends email for existing user."""
|
||||
user = UserFactory(email="reset@example.com")
|
||||
UserFactory(email="reset@example.com")
|
||||
data = {"email": "reset@example.com"}
|
||||
|
||||
factory = RequestFactory()
|
||||
|
||||
@@ -4,27 +4,27 @@ Tests for Park serializers.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.api.v1.parks.serializers import (
|
||||
ParkPhotoOutputSerializer,
|
||||
ParkPhotoCreateInputSerializer,
|
||||
ParkPhotoUpdateInputSerializer,
|
||||
ParkPhotoListOutputSerializer,
|
||||
ParkPhotoApprovalInputSerializer,
|
||||
ParkPhotoStatsOutputSerializer,
|
||||
ParkPhotoSerializer,
|
||||
HybridParkSerializer,
|
||||
ParkPhotoApprovalInputSerializer,
|
||||
ParkPhotoCreateInputSerializer,
|
||||
ParkPhotoListOutputSerializer,
|
||||
ParkPhotoOutputSerializer,
|
||||
ParkPhotoSerializer,
|
||||
ParkPhotoStatsOutputSerializer,
|
||||
ParkPhotoUpdateInputSerializer,
|
||||
ParkSerializer,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
CloudflareImageFactory,
|
||||
ParkFactory,
|
||||
ParkPhotoFactory,
|
||||
UserFactory,
|
||||
CloudflareImageFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,31 +4,30 @@ Tests for Ride serializers.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, MagicMock
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.api.v1.rides.serializers import (
|
||||
RidePhotoOutputSerializer,
|
||||
RidePhotoCreateInputSerializer,
|
||||
RidePhotoUpdateInputSerializer,
|
||||
RidePhotoListOutputSerializer,
|
||||
HybridRideSerializer,
|
||||
RidePhotoApprovalInputSerializer,
|
||||
RidePhotoCreateInputSerializer,
|
||||
RidePhotoListOutputSerializer,
|
||||
RidePhotoOutputSerializer,
|
||||
RidePhotoSerializer,
|
||||
RidePhotoStatsOutputSerializer,
|
||||
RidePhotoTypeFilterSerializer,
|
||||
RidePhotoSerializer,
|
||||
HybridRideSerializer,
|
||||
RidePhotoUpdateInputSerializer,
|
||||
RideSerializer,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
CloudflareImageFactory,
|
||||
DesignerCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
RideFactory,
|
||||
RidePhotoFactory,
|
||||
ParkFactory,
|
||||
UserFactory,
|
||||
CloudflareImageFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,20 +4,19 @@ Tests for ParkMediaService.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase
|
||||
|
||||
from apps.parks.services.media_service import ParkMediaService
|
||||
from apps.parks.models import ParkPhoto
|
||||
|
||||
from apps.parks.services.media_service import ParkMediaService
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
ParkPhotoFactory,
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
CloudflareImageFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -114,7 +113,7 @@ class TestParkMediaServiceGetParkPhotos(TestCase):
|
||||
def test__get_park_photos__primary_first__orders_primary_first(self):
|
||||
"""Test get_park_photos with primary_first orders primary photos first."""
|
||||
park = ParkFactory()
|
||||
non_primary = ParkPhotoFactory(park=park, is_primary=False, is_approved=True)
|
||||
ParkPhotoFactory(park=park, is_primary=False, is_approved=True)
|
||||
primary = ParkPhotoFactory(park=park, is_primary=True, is_approved=True)
|
||||
|
||||
result = ParkMediaService.get_park_photos(park, primary_first=True)
|
||||
|
||||
@@ -4,22 +4,21 @@ Tests for RideService.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from apps.rides.services import RideService
|
||||
from apps.rides.models import Ride
|
||||
|
||||
from apps.rides.services import RideService
|
||||
from tests.factories import (
|
||||
DesignerCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
RideModelFactory,
|
||||
ParkAreaFactory,
|
||||
UserFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,22 +4,20 @@ Tests for UserDeletionService and AccountService.
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.utils import timezone
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from apps.accounts.services import UserDeletionService, AccountService
|
||||
from apps.accounts.models import User
|
||||
|
||||
from apps.accounts.services import AccountService, UserDeletionService
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
SuperUserFactory,
|
||||
ParkReviewFactory,
|
||||
RideReviewFactory,
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
RideReviewFactory,
|
||||
SuperUserFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@@ -92,7 +90,7 @@ class TestUserDeletionServiceDeleteUserPreserveSubmissions(TestCase):
|
||||
|
||||
user_pk = user.pk
|
||||
|
||||
result = UserDeletionService.delete_user_preserve_submissions(user)
|
||||
UserDeletionService.delete_user_preserve_submissions(user)
|
||||
|
||||
# User should be deleted
|
||||
assert not User.objects.filter(pk=user_pk).exists()
|
||||
@@ -270,7 +268,7 @@ class TestAccountServiceInitiateEmailChange(TestCase):
|
||||
|
||||
def test__initiate_email_change__duplicate_email__returns_error(self):
|
||||
"""Test initiate_email_change returns error for duplicate email."""
|
||||
existing_user = UserFactory(email="existing@example.com")
|
||||
UserFactory(email="existing@example.com")
|
||||
user = UserFactory()
|
||||
|
||||
factory = RequestFactory()
|
||||
|
||||
@@ -3,17 +3,17 @@ Test cases demonstrating the factory pattern usage.
|
||||
Following Django styleguide pattern for test data creation.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase
|
||||
|
||||
from .factories import (
|
||||
UserFactory,
|
||||
ParkFactory,
|
||||
RideFactory,
|
||||
ParkReviewFactory,
|
||||
RideReviewFactory,
|
||||
CompanyFactory,
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
RideReviewFactory,
|
||||
Traits,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -4,15 +4,15 @@ Comprehensive API endpoint testing with proper naming conventions.
|
||||
"""
|
||||
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient, APITestCase
|
||||
|
||||
from apps.parks.models import Park
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
StaffUserFactory,
|
||||
CompanyFactory,
|
||||
ParkFactory,
|
||||
StaffUserFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -4,18 +4,19 @@ Uses proper naming conventions and comprehensive coverage.
|
||||
"""
|
||||
|
||||
from datetime import date, timedelta
|
||||
from django.test import TestCase
|
||||
|
||||
from django.db import IntegrityError
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.parks.models import Park, Company
|
||||
from apps.parks.models import Company, Park
|
||||
from tests.factories import (
|
||||
UserFactory,
|
||||
CompanyFactory,
|
||||
ParkFactory,
|
||||
ParkAreaFactory,
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
TestScenarios,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
import coverage # type: ignore
|
||||
import django
|
||||
from django.test.runner import DiscoverRunner
|
||||
import coverage # type: ignore
|
||||
|
||||
|
||||
def setup_django():
|
||||
|
||||
@@ -3,12 +3,13 @@ Test utilities and helpers following Django styleguide patterns.
|
||||
Provides reusable testing patterns and assertion helpers.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, Optional, List
|
||||
from datetime import date, datetime
|
||||
from django.test import TestCase
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from rest_framework.test import APITestCase
|
||||
from django.test import TestCase
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -22,8 +23,8 @@ class ApiTestMixin:
|
||||
*,
|
||||
status_code: int = status.HTTP_200_OK,
|
||||
response_status: str = "success",
|
||||
data_type: Optional[type] = None,
|
||||
contains_fields: Optional[List[str]] = None,
|
||||
data_type: type | None = None,
|
||||
contains_fields: list[str] | None = None,
|
||||
):
|
||||
"""
|
||||
Assert API response has correct structure and content.
|
||||
@@ -68,8 +69,8 @@ class ApiTestMixin:
|
||||
response,
|
||||
*,
|
||||
status_code: int,
|
||||
error_code: Optional[str] = None,
|
||||
message_contains: Optional[str] = None,
|
||||
error_code: str | None = None,
|
||||
message_contains: str | None = None,
|
||||
):
|
||||
"""
|
||||
Assert API response is an error with specific characteristics.
|
||||
@@ -94,9 +95,9 @@ class ApiTestMixin:
|
||||
self,
|
||||
response,
|
||||
*,
|
||||
expected_count: Optional[int] = None,
|
||||
has_next: Optional[bool] = None,
|
||||
has_previous: Optional[bool] = None,
|
||||
expected_count: int | None = None,
|
||||
has_next: bool | None = None,
|
||||
has_previous: bool | None = None,
|
||||
):
|
||||
"""
|
||||
Assert API response has correct pagination structure.
|
||||
@@ -136,7 +137,7 @@ class ApiTestMixin:
|
||||
class ModelTestMixin:
|
||||
"""Mixin providing common model testing utilities."""
|
||||
|
||||
def assertModelFields(self, instance, expected_fields: Dict[str, Any]):
|
||||
def assertModelFields(self, instance, expected_fields: dict[str, Any]):
|
||||
"""
|
||||
Assert model instance has expected field values.
|
||||
|
||||
@@ -155,8 +156,8 @@ class ModelTestMixin:
|
||||
def assertModelValidation(
|
||||
self,
|
||||
model_class,
|
||||
invalid_data: Dict[str, Any],
|
||||
expected_errors: List[str],
|
||||
invalid_data: dict[str, Any],
|
||||
expected_errors: list[str],
|
||||
):
|
||||
"""
|
||||
Assert model validation catches expected errors.
|
||||
@@ -175,7 +176,7 @@ class ModelTestMixin:
|
||||
for expected_error in expected_errors:
|
||||
self.assertIn(expected_error, exception_str)
|
||||
|
||||
def assertDatabaseConstraint(self, model_factory, invalid_data: Dict[str, Any]):
|
||||
def assertDatabaseConstraint(self, model_factory, invalid_data: dict[str, Any]):
|
||||
"""
|
||||
Assert database constraint is enforced.
|
||||
|
||||
@@ -299,7 +300,7 @@ class GeographyTestMixin:
|
||||
point2: (latitude, longitude) tuple
|
||||
max_distance_km: Maximum allowed distance in kilometers
|
||||
"""
|
||||
from math import radians, cos, sin, asin, sqrt
|
||||
from math import asin, cos, radians, sin, sqrt
|
||||
|
||||
lat1, lon1 = point1
|
||||
lat2, lon2 = point2
|
||||
|
||||
@@ -6,14 +6,14 @@ for testing FSM transitions, HTMX interactions, and other common scenarios.
|
||||
"""
|
||||
|
||||
from .fsm_test_helpers import (
|
||||
create_test_submission,
|
||||
assert_state_log_created,
|
||||
assert_status_changed,
|
||||
assert_toast_triggered,
|
||||
create_test_park,
|
||||
create_test_ride,
|
||||
assert_status_changed,
|
||||
assert_state_log_created,
|
||||
assert_toast_triggered,
|
||||
wait_for_htmx_swap,
|
||||
create_test_submission,
|
||||
verify_transition_buttons_visible,
|
||||
wait_for_htmx_swap,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -9,10 +9,11 @@ Reusable utility functions for testing FSM transitions:
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
from django.db.models import Model
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Model
|
||||
from django.http import HttpResponse
|
||||
|
||||
User = get_user_model()
|
||||
@@ -25,10 +26,10 @@ User = get_user_model()
|
||||
|
||||
def create_test_submission(
|
||||
status: str = "PENDING",
|
||||
user: Optional[User] = None,
|
||||
park: Optional[Model] = None,
|
||||
user: User | None = None,
|
||||
park: Model | None = None,
|
||||
submission_type: str = "EDIT",
|
||||
changes: Optional[Dict[str, Any]] = None,
|
||||
changes: dict[str, Any] | None = None,
|
||||
reason: str = "Test submission",
|
||||
**kwargs
|
||||
) -> "EditSubmission":
|
||||
@@ -87,8 +88,8 @@ def create_test_submission(
|
||||
|
||||
def create_test_park(
|
||||
status: str = "OPERATING",
|
||||
name: Optional[str] = None,
|
||||
slug: Optional[str] = None,
|
||||
name: str | None = None,
|
||||
slug: str | None = None,
|
||||
**kwargs
|
||||
) -> "Park":
|
||||
"""
|
||||
@@ -125,9 +126,9 @@ def create_test_park(
|
||||
|
||||
def create_test_ride(
|
||||
status: str = "OPERATING",
|
||||
name: Optional[str] = None,
|
||||
slug: Optional[str] = None,
|
||||
park: Optional[Model] = None,
|
||||
name: str | None = None,
|
||||
slug: str | None = None,
|
||||
park: Model | None = None,
|
||||
**kwargs
|
||||
) -> "Ride":
|
||||
"""
|
||||
@@ -170,8 +171,8 @@ def create_test_ride(
|
||||
|
||||
def create_test_photo_submission(
|
||||
status: str = "PENDING",
|
||||
user: Optional[User] = None,
|
||||
park: Optional[Model] = None,
|
||||
user: User | None = None,
|
||||
park: Model | None = None,
|
||||
**kwargs
|
||||
) -> "PhotoSubmission":
|
||||
"""
|
||||
@@ -254,8 +255,8 @@ def assert_status_changed(obj: Model, expected_status: str) -> None:
|
||||
def assert_state_log_created(
|
||||
obj: Model,
|
||||
transition_name: str,
|
||||
user: Optional[User] = None,
|
||||
expected_state: Optional[str] = None
|
||||
user: User | None = None,
|
||||
expected_state: str | None = None
|
||||
) -> None:
|
||||
"""
|
||||
Assert that a StateLog entry was created for a transition.
|
||||
@@ -295,7 +296,7 @@ def assert_state_log_created(
|
||||
|
||||
def assert_toast_triggered(
|
||||
response: HttpResponse,
|
||||
message: Optional[str] = None,
|
||||
message: str | None = None,
|
||||
toast_type: str = "success"
|
||||
) -> None:
|
||||
"""
|
||||
@@ -375,9 +376,9 @@ def wait_for_htmx_swap(
|
||||
|
||||
def verify_transition_buttons_visible(
|
||||
page,
|
||||
transitions: List[str],
|
||||
transitions: list[str],
|
||||
container_selector: str = "[data-status-actions]"
|
||||
) -> Dict[str, bool]:
|
||||
) -> dict[str, bool]:
|
||||
"""
|
||||
Verify which transition buttons are visible on the page.
|
||||
|
||||
@@ -487,7 +488,7 @@ def click_and_confirm(page, button_locator, accept: bool = True) -> None:
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def make_htmx_post(client, url: str, data: Optional[Dict] = None) -> HttpResponse:
|
||||
def make_htmx_post(client, url: str, data: dict | None = None) -> HttpResponse:
|
||||
"""
|
||||
Make a POST request with HTMX headers.
|
||||
|
||||
@@ -529,7 +530,7 @@ def get_fsm_transition_url(
|
||||
pk: int,
|
||||
transition_name: str,
|
||||
use_slug: bool = False,
|
||||
slug: Optional[str] = None
|
||||
slug: str | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Generate the URL for an FSM transition.
|
||||
|
||||
@@ -5,9 +5,7 @@ These tests verify that the breadcrumb system generates
|
||||
correct navigation structures and Schema.org markup.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.core.utils.breadcrumbs import (
|
||||
Breadcrumb,
|
||||
|
||||
@@ -7,7 +7,6 @@ with various parameter combinations.
|
||||
|
||||
import pytest
|
||||
from django.template import Context, Template
|
||||
from django.test import RequestFactory, override_settings
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -8,7 +8,6 @@ correct responses with proper headers and content.
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from django.http import HttpRequest
|
||||
from django.test import RequestFactory
|
||||
|
||||
from apps.core.htmx_utils import (
|
||||
|
||||
@@ -5,7 +5,6 @@ These tests verify that message helper functions generate
|
||||
consistent, user-friendly messages.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from apps.core.utils.messages import (
|
||||
confirm_delete,
|
||||
|
||||
@@ -5,7 +5,6 @@ These tests verify that meta tag helpers generate
|
||||
correct SEO and social sharing metadata.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import RequestFactory
|
||||
|
||||
from apps.core.utils.meta import (
|
||||
|
||||
Reference in New Issue
Block a user