""" Test factories for ThrillWiki models. 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 decimal import Decimal import random User = get_user_model() class UserFactory(DjangoModelFactory): """Factory for creating User instances.""" class Meta: model = User django_get_or_create = ('username',) username = factory.Sequence(lambda n: f"testuser{n}") email = factory.LazyAttribute(lambda obj: f"{obj.username}@example.com") first_name = factory.Faker('first_name') last_name = factory.Faker('last_name') is_active = True is_staff = False is_superuser = False @factory.post_generation def set_password(obj, create, extracted, **kwargs): if create: [PASSWORD-REMOVED] or 'testpass123' obj.set_password(password) obj.save() class StaffUserFactory(UserFactory): """Factory for creating staff User instances.""" is_staff = True class SuperUserFactory(UserFactory): """Factory for creating superuser instances.""" is_staff = True is_superuser = True class CompanyFactory(DjangoModelFactory): """Factory for creating Company instances.""" class Meta: model = 'parks.Company' django_get_or_create = ('name',) name = factory.Faker('company') slug = factory.LazyAttribute(lambda obj: slugify(obj.name)) description = factory.Faker('text', max_nb_chars=500) website = factory.Faker('url') founded_year = fuzzy.FuzzyInteger(1800, 2024) roles = factory.LazyFunction(lambda: ['OPERATOR']) @factory.post_generation def multiple_roles(obj, create, extracted, **kwargs): """Optionally add multiple roles.""" if create and extracted: obj.roles = extracted obj.save() class OperatorCompanyFactory(CompanyFactory): """Factory for companies that operate parks.""" roles = factory.LazyFunction(lambda: ['OPERATOR']) class ManufacturerCompanyFactory(CompanyFactory): """Factory for companies that manufacture rides.""" roles = factory.LazyFunction(lambda: ['MANUFACTURER']) class DesignerCompanyFactory(CompanyFactory): """Factory for companies that design rides.""" roles = factory.LazyFunction(lambda: ['DESIGNER']) class LocationFactory(DjangoModelFactory): """Factory for creating Location instances.""" class Meta: model = 'location.Location' name = factory.Faker('city') location_type = 'park' latitude = fuzzy.FuzzyFloat(-90, 90) longitude = fuzzy.FuzzyFloat(-180, 180) street_address = factory.Faker('street_address') city = factory.Faker('city') state = factory.Faker('state') country = factory.Faker('country') postal_code = factory.Faker('postcode') @factory.lazy_attribute def point(self): return Point(float(self.longitude), float(self.latitude)) class ParkFactory(DjangoModelFactory): """Factory for creating Park instances.""" class Meta: model = 'parks.Park' django_get_or_create = ('slug',) name = factory.Sequence(lambda n: f"Test Park {n}") slug = factory.LazyAttribute(lambda obj: slugify(obj.name)) description = factory.Faker('text', max_nb_chars=1000) status = 'OPERATING' opening_date = factory.Faker('date_between', start_date='-50y', end_date='today') closing_date = None operating_season = factory.Faker('sentence', nb_words=4) size_acres = fuzzy.FuzzyDecimal(1, 1000, precision=2) website = factory.Faker('url') average_rating = fuzzy.FuzzyDecimal(1, 10, precision=2) ride_count = fuzzy.FuzzyInteger(5, 100) coaster_count = fuzzy.FuzzyInteger(1, 20) # Relationships operator = factory.SubFactory(OperatorCompanyFactory) property_owner = factory.SubFactory(OperatorCompanyFactory) @factory.post_generation def create_location(obj, create, extracted, **kwargs): """Create a location for the park.""" if create: LocationFactory( content_object=obj, name=obj.name, location_type='park' ) class ClosedParkFactory(ParkFactory): """Factory for creating closed parks.""" status = 'CLOSED_PERM' closing_date = factory.Faker('date_between', start_date='-10y', end_date='today') class ParkAreaFactory(DjangoModelFactory): """Factory for creating ParkArea instances.""" class Meta: model = 'parks.ParkArea' django_get_or_create = ('park', 'slug') name = factory.Faker('word') slug = factory.LazyAttribute(lambda obj: slugify(obj.name)) description = factory.Faker('text', max_nb_chars=500) # Relationships park = factory.SubFactory(ParkFactory) class RideModelFactory(DjangoModelFactory): """Factory for creating RideModel instances.""" class Meta: model = 'rides.RideModel' django_get_or_create = ('name', 'manufacturer') name = factory.Faker('word') description = factory.Faker('text', max_nb_chars=500) # Relationships manufacturer = factory.SubFactory(ManufacturerCompanyFactory) class RideFactory(DjangoModelFactory): """Factory for creating Ride instances.""" class Meta: model = 'rides.Ride' django_get_or_create = ('park', 'slug') name = factory.Sequence(lambda n: f"Test Ride {n}") slug = factory.LazyAttribute(lambda obj: slugify(obj.name)) description = factory.Faker('text', max_nb_chars=1000) category = fuzzy.FuzzyChoice(['RC', 'WC', 'TR', 'WR', 'DR', 'CR', 'FR', 'SP']) status = 'OPERATING' opening_date = factory.Faker('date_between', start_date='-30y', end_date='today') closing_date = None min_height_in = fuzzy.FuzzyInteger(36, 48) max_height_in = None capacity_per_hour = fuzzy.FuzzyInteger(500, 3000) ride_duration_seconds = fuzzy.FuzzyInteger(60, 300) average_rating = fuzzy.FuzzyDecimal(1, 10, precision=2) # Relationships park = factory.SubFactory(ParkFactory) manufacturer = factory.SubFactory(ManufacturerCompanyFactory) designer = factory.SubFactory(DesignerCompanyFactory) ride_model = factory.SubFactory(RideModelFactory) park_area = factory.SubFactory(ParkAreaFactory, park=factory.SelfAttribute('..park')) @factory.post_generation def create_location(obj, create, extracted, **kwargs): """Create a location for the ride.""" if create: LocationFactory( content_object=obj, name=obj.name, location_type='ride' ) class CoasterFactory(RideFactory): """Factory for creating roller coaster rides.""" category = fuzzy.FuzzyChoice(['RC', 'WC']) min_height_in = fuzzy.FuzzyInteger(42, 54) ride_duration_seconds = fuzzy.FuzzyInteger(90, 240) class ParkReviewFactory(DjangoModelFactory): """Factory for creating ParkReview instances.""" class Meta: model = 'parks.ParkReview' django_get_or_create = ('park', 'user') rating = fuzzy.FuzzyInteger(1, 10) title = factory.Faker('sentence', nb_words=6) content = factory.Faker('text', max_nb_chars=2000) visit_date = factory.Faker('date_between', start_date='-2y', end_date='today') is_published = True moderation_notes = '' # Relationships park = factory.SubFactory(ParkFactory) user = factory.SubFactory(UserFactory) class RideReviewFactory(DjangoModelFactory): """Factory for creating RideReview instances.""" class Meta: model = 'rides.RideReview' django_get_or_create = ('ride', 'user') rating = fuzzy.FuzzyInteger(1, 10) title = factory.Faker('sentence', nb_words=6) content = factory.Faker('text', max_nb_chars=2000) visit_date = factory.Faker('date_between', start_date='-2y', end_date='today') is_published = True moderation_notes = '' # Relationships ride = factory.SubFactory(RideFactory) user = factory.SubFactory(UserFactory) class ModeratedReviewFactory(ParkReviewFactory): """Factory for creating moderated reviews.""" moderation_notes = factory.Faker('sentence') moderated_by = factory.SubFactory(StaffUserFactory) moderated_at = factory.Faker('date_time_between', start_date='-1y', end_date='now') class EditSubmissionFactory(DjangoModelFactory): """Factory for creating EditSubmission instances.""" class Meta: model = 'moderation.EditSubmission' submission_type = 'UPDATE' changes = factory.LazyFunction(lambda: {'name': 'Updated Name'}) status = 'PENDING' notes = factory.Faker('sentence') # Relationships submitted_by = factory.SubFactory(UserFactory) content_object = factory.SubFactory(ParkFactory) # Trait mixins for common scenarios class Traits: """Common trait mixins for factories.""" @staticmethod def operating_park(): """Trait for operating parks.""" return { 'status': 'OPERATING', 'closing_date': None } @staticmethod def closed_park(): """Trait for closed parks.""" return { 'status': 'CLOSED_PERM', 'closing_date': factory.Faker('date_between', start_date='-10y', end_date='today') } @staticmethod def high_rated(): """Trait for highly rated items.""" return { 'average_rating': fuzzy.FuzzyDecimal(8, 10, precision=2) } @staticmethod def recent_submission(): """Trait for recent submissions.""" return { 'submitted_at': factory.Faker('date_time_between', start_date='-7d', end_date='now') } # Specialized factories for testing scenarios class TestScenarios: """Pre-configured factory combinations for common test scenarios.""" @staticmethod def complete_park_with_rides(num_rides=5): """Create a complete park with rides and reviews.""" park = ParkFactory() rides = [RideFactory(park=park) for _ in range(num_rides)] park_review = ParkReviewFactory(park=park) ride_reviews = [RideReviewFactory(ride=ride) for ride in rides[:2]] return { 'park': park, 'rides': rides, 'park_review': park_review, 'ride_reviews': ride_reviews } @staticmethod def moderation_workflow(): """Create a complete moderation workflow scenario.""" user = UserFactory() moderator = StaffUserFactory() park = ParkFactory() submission = EditSubmissionFactory( submitted_by=user, content_object=park ) return { 'user': user, 'moderator': moderator, 'park': park, 'submission': submission } @staticmethod def review_scenario(): """Create a scenario with multiple reviews and ratings.""" park = ParkFactory() users = [UserFactory() for _ in range(5)] reviews = [ParkReviewFactory(park=park, user=user) for user in users] return { 'park': park, 'users': users, 'reviews': reviews }