import uuid from datetime import datetime, timezone, timedelta from typing import Dict from typing import Optional import pytest from fastapi import FastAPI from fastapi.routing import APIRouter from fastapi.security import OAuth2PasswordBearer from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app.api.dependencies.auth import get_current_user from app.core.database import get_db from app.models import Event, GiftItem, GiftStatus, GiftPriority, GiftCategory, GiftPurchase, RSVP, RSVPStatus, \ EventMedia, MediaType, MediaPurpose, \ EventTheme, Guest, GuestStatus, ActivityType, ActivityLog, EmailTemplate, TemplateType, NotificationLog, \ NotificationType, NotificationStatus from app.models.user import User from app.utils.test_utils import setup_test_db, teardown_test_db, setup_async_test_db, teardown_async_test_db pytest_plugins = ["pytest_asyncio"] @pytest.fixture(scope="function") def db_session(): """ Creates a fresh SQLite in-memory database for each test function. Yields a SQLAlchemy session that can be used for testing. """ # Set up the database test_engine, TestingSessionLocal = setup_test_db() # Create a session with TestingSessionLocal() as session: yield session # Clean up teardown_test_db(test_engine) @pytest.fixture(scope="function") # Define a fixture async def async_test_db(): """Fixture provides new testing engine and session for each test run to improve isolation.""" test_engine, AsyncTestingSessionLocal = await setup_async_test_db() yield test_engine, AsyncTestingSessionLocal await teardown_async_test_db(test_engine) @pytest.fixture def user_create_data(): return { "email": "newtest@example.com", # Changed to avoid conflict with mock_user "password": "TestPassword123!", "first_name": "Test", "last_name": "User", "phone_number": "+1234567890", "is_superuser": False, "preferences": None } @pytest.fixture def mock_user(db_session): """Fixture to create and return a mock User instance.""" mock_user = User( id=uuid.uuid4(), email="mockuser@example.com", password_hash="mockhashedpassword", first_name="Mock", last_name="User", phone_number="1234567890", is_active=True, is_superuser=False, preferences=None, ) db_session.add(mock_user) db_session.commit() return mock_user @pytest.fixture def mock_superuser(db_session): """Fixture to create and return a mock User instance.""" mock_user = User( id=uuid.uuid4(), email="mocksuperuser@example.com", password_hash="mockhashedpassword", first_name="Mock", last_name="SuperUser", phone_number="1234567890", is_active=True, is_superuser=True, preferences=None, ) db_session.add(mock_user) db_session.commit() return mock_user @pytest.fixture def mock_event(db_session, mock_user): """Create a test event fixture.""" event_data = { "title": "Birthday Party", "slug": "birthday-party", "description": "A test birthday party", "event_date": datetime.now(tz=timezone.utc) + timedelta(days=30), "timezone": "UTC", "is_public": True, # Make sure this is set to True "created_by": mock_user.id } event = Event(**event_data) db_session.add(event) db_session.commit() db_session.refresh(event) return event @pytest.fixture def gift_item_fixture(db_session, mock_user, mock_event): """ Fixture to create and return a default GiftItem instance. The event_id, added_by, and other necessary attributes are predefined. """ gift_item = GiftItem( id=uuid.uuid4(), event_id=mock_event.id, added_by=mock_user.id, name="Default Gift", description="Default gift description.", price=100.00, currency="USD", quantity_requested=5, quantity_received=0, status=GiftStatus.AVAILABLE, priority=GiftPriority.MEDIUM, is_visible=True ) db_session.add(gift_item) db_session.commit() return gift_item @pytest.fixture def rsvp_fixture(db_session, mock_user): """ Fixture to create and return a default RSVP instance. The event_id and guest_id fields are predefined for testing. """ rsvp = RSVP( id=uuid.uuid4(), event_id=uuid.uuid4(), guest_id=uuid.uuid4(), status=RSVPStatus.ATTENDING, number_of_guests=2, response_message="Looking forward to the event!", dietary_requirements="Vegetarian", ) db_session.add(rsvp) db_session.commit() return rsvp @pytest.fixture def event_media_fixture(db_session, mock_user): """ Fixture to create and return a default EventMedia instance. """ event_media = EventMedia( id=uuid.uuid4(), event_id=uuid.uuid4(), uploaded_by=mock_user.id, file_path="uploads/sample.jpg", original_filename="sample.jpg", media_type=MediaType.IMAGE, content_type="image/jpeg", file_size=2048, purpose=MediaPurpose.GALLERY, is_public=True, display_order=1, title="Sample Image", description="A sample image for testing.", media_metadata={"resolution": "1920x1080"}, ) db_session.add(event_media) db_session.commit() return event_media @pytest.fixture def event_theme_fixture(db_session): """ Fixture to create and return a default EventTheme instance. """ event_theme = EventTheme( id=uuid.uuid4(), name="Animal Theme", description="An animal-themed design for events.", preview_image_url="https://example.com/preview/animal_theme.jpg", background_image_url="https://example.com/preview/animal_theme.jpg", foreground_image_url="https://example.com/preview/animal_theme.jpg", is_active=True, color_palette={ "primary": "#ff5722", "secondary": "#4caf50", "background": "#ffffff", "text": "#000000" }, fonts={ "header": "Roboto", "body": "Open Sans" } ) db_session.add(event_theme) db_session.commit() return event_theme @pytest.fixture def guest_fixture(db_session, mock_user, mock_event): """ Fixture to create and return a default Guest instance. """ guest = Guest( id=uuid.uuid4(), event_id=mock_event.id, invited_by=mock_user.id, full_name="John Doe", email="johndoe@example.com", phone="1234567890", invitation_code="INV123456", status=GuestStatus.INVITED, max_additional_guests=2, actual_additional_guests=1, invitation_sent_at=datetime.now(timezone.utc), dietary_restrictions="None", notes="VIP Guest", custom_fields={"allergies": "None", "special_request": "Near the stage"}, is_blocked=False, can_bring_guests=True, ) db_session.add(guest) db_session.commit() return guest @pytest.fixture def activity_log_fixture(db_session, mock_user, guest_fixture): """ Fixture to create and return a default ActivityLog entry. """ activity_log_entry = ActivityLog( id=uuid.uuid4(), activity_type=ActivityType.USER_LOGIN, description="User successfully logged in.", event_id=None, user_id=mock_user.id, guest_id=None, target_id=None, target_type=None, ip_address="192.168.1.1", user_agent="Mozilla/5.0", activity_data={"extra_info": "Login from web app"}, created_at=datetime.now(timezone.utc) ) db_session.add(activity_log_entry) db_session.commit() return activity_log_entry @pytest.fixture def email_template_fixture(db_session, mock_user): """ Fixture to create and return a default EmailTemplate instance. """ email_template = EmailTemplate( id=uuid.uuid4(), name="Invitation Template", description="A template for event invitations.", template_type=TemplateType.INVITATION, subject="You're invited to {{event_name}}!", html_content="
We look forward to seeing you!
", text_content="Welcome to {{event_name}}. We look forward to seeing you!", variables={"event_name": "Birthday Party"}, event_id=None, # Global template created_by=mock_user.id, is_active=True, is_system=False, ) db_session.add(email_template) db_session.commit() return email_template @pytest.fixture def notification_log_fixture(db_session, email_template_fixture, guest_fixture): """ Fixture to create and return a default NotificationLog entry. """ notification_log = NotificationLog( id=uuid.uuid4(), notification_type=NotificationType.EMAIL, status=NotificationStatus.QUEUED, subject="You're invited!", content_preview="Join us for an unforgettable event.", template_id=email_template_fixture.id, event_id=guest_fixture.event_id, guest_id=guest_fixture.id, recipient="example@example.com", notification_metadata={"extra_info": "Email sent via SendGrid"}, ) db_session.add(notification_log) db_session.commit() return notification_log @pytest.fixture def gift_purchase_fixture(db_session, gift_item_fixture, guest_fixture): """ Fixture to create and return a GiftPurchase instance. """ gift_purchase = GiftPurchase( id=uuid.uuid4(), gift_id=gift_item_fixture.id, guest_id=guest_fixture.id, quantity=1, purchased_at=datetime.now(timezone.utc), purchase_price=25.0, purchase_currency="USD", notes="Gift purchased through store link" ) db_session.add(gift_purchase) db_session.commit() return gift_purchase @pytest.fixture def gift_category_fixture(db_session, mock_user, mock_event): """ Fixture to create and return a GiftCategory instance. """ # Create the category without event_id and display_order gift_category = GiftCategory( id=uuid.uuid4(), name="Electronics", description="Category for electronic gifts", created_by=mock_user.id ) db_session.add(gift_category) db_session.commit() # Create the association between the category and the event from app.models.gift import EventGiftCategory event_gift_category = EventGiftCategory( event_id=mock_event.id, category_id=gift_category.id, display_order=0, is_visible=True ) db_session.add(event_gift_category) db_session.commit() return gift_category @pytest.fixture def theme_data() -> Dict: return { "name": "Animal Safari", "description": "A wild and fun theme with safari animals", "preview_image_url": "https://example.com/safari.jpg", "color_palette": { "primary": "#f4a261", "secondary": "#2a9d8f", "background": "#ffffff", "text": "#264653" }, "fonts": { "header": "Safari Display", "body": "Nunito Sans" } } @pytest.fixture def oauth2_scheme(): """Return the OAuth2PasswordBearer instance used by the app.""" return OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") @pytest.fixture def create_test_client(): def _create_test_client(router: APIRouter, prefix: str, db_session, user): from fastapi import FastAPI app = FastAPI() # Mimic your dependency overrides here def get_db_override(): yield db_session def get_current_user_override(): return user # Include your router app.include_router(router, prefix=prefix) # Override dependencies app.dependency_overrides[get_db] = get_db_override app.dependency_overrides[get_current_user] = get_current_user_override return TestClient(app) return _create_test_client