""" 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 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 = extracted 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}