import pytest from datetime import datetime, timezone from uuid import UUID, uuid4 from app.crud.gift import gift_item_crud, gift_category_crud, gift_purchase_crud from app.schemas.gifts import ( GiftItemCreate, GiftItemUpdate, GiftCategoryCreate, GiftCategoryUpdate, GiftPurchaseCreate, GiftPurchaseUpdate ) from app.models.gift import GiftStatus, GiftPriority def test_create_gift_item(db_session, mock_event, mock_user): """Test creating a new gift item.""" gift_data = { "name": "Toy Lion", "description": "A cuddly lion toy", "price": 24.99, "currency": "USD", "quantity_requested": 1, "priority": GiftPriority.HIGH, "purchase_url": "https://example.com/toy-lion", "store_name": "Toys R Fun", "image_url": "https://example.com/images/toy-lion.jpg" } gift_in = GiftItemCreate(**gift_data, event_id=mock_event.id, added_by=mock_user.id) gift = gift_item_crud.create(db=db_session, obj_in=gift_in) assert gift.name == gift_data["name"] assert gift.description == gift_data["description"] assert gift.price == gift_data["price"] assert gift.event_id == mock_event.id assert gift.added_by == mock_user.id assert gift.status == GiftStatus.AVAILABLE assert gift.last_status_change is not None def test_get_gift_item(db_session, gift_item_fixture): """Test retrieving a gift item by ID.""" stored_gift = gift_item_crud.get(db=db_session, id=gift_item_fixture.id) assert stored_gift assert stored_gift.id == gift_item_fixture.id assert stored_gift.name == gift_item_fixture.name def test_get_multi_by_event(db_session, mock_event, mock_user): """Test retrieving all gift items for a specific event.""" # Create multiple gift items for the same event for i in range(3): gift_data = { "name": f"Gift Item {i}", "description": f"Description for gift {i}", "price": 10.0 * (i + 1), "event_id": mock_event.id, "added_by": mock_user.id } gift_in = GiftItemCreate(**gift_data) gift_item_crud.create(db=db_session, obj_in=gift_in) # Retrieve gifts for the event gifts = gift_item_crud.get_multi_by_event( db=db_session, event_id=mock_event.id, skip=0, limit=100 ) assert len(gifts) >= 3 assert all(gift.event_id == mock_event.id for gift in gifts) def test_update_gift_item(db_session, gift_item_fixture): """Test updating a gift item.""" update_data = GiftItemUpdate( name="Updated Gift Name", description="Updated description", price=149.99, priority=GiftPriority.MUST_HAVE ) updated_gift = gift_item_crud.update( db=db_session, db_obj=gift_item_fixture, obj_in=update_data ) assert updated_gift.name == "Updated Gift Name" assert updated_gift.description == "Updated description" assert updated_gift.price == 149.99 assert updated_gift.priority == GiftPriority.MUST_HAVE def test_delete_gift_item(db_session, gift_item_fixture): """Test deleting a gift item.""" gift = gift_item_crud.remove(db=db_session, id=gift_item_fixture.id) assert gift.id == gift_item_fixture.id deleted_gift = gift_item_crud.get(db=db_session, id=gift_item_fixture.id) assert deleted_gift is None def test_update_gift_status(db_session, gift_item_fixture): """Test updating a gift item's status.""" updated_gift = gift_item_crud.update_status( db=db_session, gift_id=gift_item_fixture.id, new_status=GiftStatus.RESERVED ) assert updated_gift.status == GiftStatus.RESERVED assert updated_gift.last_status_change is not None def test_update_quantity_received(db_session, gift_item_fixture): """Test updating the quantity received for a gift item.""" # Set initial values for testing gift_item_fixture.quantity_requested = 5 gift_item_fixture.quantity_received = 0 db_session.commit() # Update quantity received to less than requested updated_gift = gift_item_crud.update_quantity_received( db=db_session, gift_id=gift_item_fixture.id, quantity=3 ) assert updated_gift.quantity_received == 3 assert updated_gift.status != GiftStatus.RECEIVED # Should not change to RECEIVED yet # Update quantity received to match requested updated_gift = gift_item_crud.update_quantity_received( db=db_session, gift_id=gift_item_fixture.id, quantity=5 ) assert updated_gift.quantity_received == 5 assert updated_gift.status == GiftStatus.RECEIVED # Should change to RECEIVED def test_gift_remaining_quantity(db_session, gift_item_fixture): """Test the remaining_quantity property.""" gift_item_fixture.quantity_requested = 5 gift_item_fixture.quantity_received = 2 db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.remaining_quantity == 3 gift_item_fixture.quantity_received = 5 db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.remaining_quantity == 0 gift_item_fixture.quantity_received = 6 # More than requested db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.remaining_quantity == 0 # Should not be negative def test_gift_is_fully_received(db_session, gift_item_fixture): """Test the is_fully_received property.""" gift_item_fixture.quantity_requested = 3 gift_item_fixture.quantity_received = 1 db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.is_fully_received is False gift_item_fixture.quantity_received = 3 db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.is_fully_received is True gift_item_fixture.quantity_received = 4 # More than requested db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.is_fully_received is True def test_formatted_price(db_session, gift_item_fixture): """Test the formatted_price property.""" gift_item_fixture.price = 19.99 gift_item_fixture.currency = "USD" db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.formatted_price == "19.99 USD" gift_item_fixture.price = None db_session.commit() db_session.refresh(gift_item_fixture) assert gift_item_fixture.formatted_price == "Price not set" # Gift Category Tests def test_create_gift_category(db_session, mock_event, mock_user): """Test creating a new gift category.""" category_data = { "name": "Toys", "description": "Fun toys for the birthday", "icon": "toy-icon", "color": "#FF5733" } category_in = GiftCategoryCreate( **category_data, created_by=mock_user.id ) category = gift_category_crud.create(db=db_session, obj_in=category_in) # Create the association between the category and the event from app.models.gift import EventGiftCategory from app.schemas.gifts import EventGiftCategoryCreate from app.crud.gift import event_gift_category_crud association_data = EventGiftCategoryCreate( event_id=mock_event.id, category_id=category.id, display_order=1, is_visible=True ) association = event_gift_category_crud.create(db=db_session, obj_in=association_data) assert category.name == category_data["name"] assert category.description == category_data["description"] assert category.created_by == mock_user.id assert association.event_id == mock_event.id assert association.category_id == category.id assert association.display_order == 1 def test_get_gift_category(db_session, gift_category_fixture): """Test retrieving a gift category by ID.""" stored_category = gift_category_crud.get(db=db_session, id=gift_category_fixture.id) assert stored_category assert stored_category.id == gift_category_fixture.id assert stored_category.name == gift_category_fixture.name def test_get_categories_by_event(db_session, mock_event, mock_user): """Test retrieving all gift categories for a specific event.""" # Import necessary modules from app.schemas.gifts import EventGiftCategoryCreate from app.crud.gift import event_gift_category_crud # Create multiple categories for the same event for i in range(3): # Create the category category_data = { "name": f"Category {i}", "description": f"Description for category {i}", "created_by": mock_user.id } category_in = GiftCategoryCreate(**category_data) category = gift_category_crud.create(db=db_session, obj_in=category_in) # Create the association between the category and the event association_data = EventGiftCategoryCreate( event_id=mock_event.id, category_id=category.id, display_order=i, is_visible=True ) event_gift_category_crud.create(db=db_session, obj_in=association_data) # Retrieve categories for the event categories = event_gift_category_crud.get_categories_by_event( db=db_session, event_id=mock_event.id, skip=0, limit=100 ) assert len(categories) >= 3 # Get the associations to check display_order from app.models.gift import EventGiftCategory associations = db_session.query(EventGiftCategory).filter( EventGiftCategory.event_id == mock_event.id ).order_by(EventGiftCategory.display_order).all() # Check ordering by display_order for i in range(len(associations) - 1): assert associations[i].display_order <= associations[i + 1].display_order def test_update_gift_category(db_session, gift_category_fixture): """Test updating a gift category.""" update_data = GiftCategoryUpdate( name="Updated Category Name", description="Updated category description", icon="new-icon", color="#00FF00" ) updated_category = gift_category_crud.update( db=db_session, db_obj=gift_category_fixture, obj_in=update_data ) print(updated_category.__dict__) assert updated_category.name == "Updated Category Name" assert updated_category.description == "Updated category description" # assert updated_category.icon == "new-icon" # assert updated_category.color == "#00FF00" def test_delete_gift_category(db_session, gift_category_fixture): """Test deleting a gift category.""" # First, we need to delete the EventGiftCategory association from app.models.gift import EventGiftCategory from app.crud.gift import event_gift_category_crud # Get the association association = db_session.query(EventGiftCategory).filter( EventGiftCategory.category_id == gift_category_fixture.id ).first() # Delete the association if association: event_gift_category_crud.remove( db=db_session, event_id=association.event_id, category_id=gift_category_fixture.id ) # Now we can delete the category category = gift_category_crud.remove(db=db_session, id=gift_category_fixture.id) assert category.id == gift_category_fixture.id deleted_category = gift_category_crud.get(db=db_session, id=gift_category_fixture.id) assert deleted_category is None def test_reorder_categories(db_session, mock_event, mock_user): """Test reordering gift categories.""" # Import necessary modules from app.schemas.gifts import EventGiftCategoryCreate from app.crud.gift import event_gift_category_crud # Create categories with initial display order categories = [] for i in range(3): # Create the category category_data = { "name": f"Category {i}", "created_by": mock_user.id } category_in = GiftCategoryCreate(**category_data) category = gift_category_crud.create(db=db_session, obj_in=category_in) # Create the association between the category and the event association_data = EventGiftCategoryCreate( event_id=mock_event.id, category_id=category.id, display_order=i, is_visible=True ) event_gift_category_crud.create(db=db_session, obj_in=association_data) categories.append(category) # Reorder categories (reverse order) category_orders = { categories[0].id: 2, categories[1].id: 1, categories[2].id: 0 } updated_associations = event_gift_category_crud.reorder_categories( db=db_session, event_id=mock_event.id, category_orders=category_orders ) # Verify new order for assoc in updated_associations: if assoc.category_id == categories[0].id: assert assoc.display_order == 2 elif assoc.category_id == categories[1].id: assert assoc.display_order == 1 elif assoc.category_id == categories[2].id: assert assoc.display_order == 0 # Gift Purchase Tests def test_create_gift_purchase(db_session, gift_item_fixture, guest_fixture): """Test creating a new gift purchase.""" purchase_data = { "gift_id": gift_item_fixture.id, "guest_id": guest_fixture.id, "quantity": 1, "purchase_price": 19.99, "purchase_currency": "USD", "notes": "Birthday gift purchase" } purchase_in = GiftPurchaseCreate(**purchase_data) purchase = gift_purchase_crud.create(db=db_session, obj_in=purchase_in) assert purchase.gift_id == gift_item_fixture.id assert purchase.guest_id == guest_fixture.id assert purchase.quantity == 1 assert purchase.purchase_price == 19.99 assert purchase.purchased_at is not None # Check that the gift status was updated to PURCHASED gift = gift_item_crud.get(db=db_session, id=gift_item_fixture.id) assert gift.status == GiftStatus.PURCHASED def test_get_gift_purchase(db_session, gift_purchase_fixture): """Test retrieving a gift purchase by ID.""" stored_purchase = gift_purchase_crud.get(db=db_session, id=gift_purchase_fixture.id) assert stored_purchase assert stored_purchase.id == gift_purchase_fixture.id assert stored_purchase.gift_id == gift_purchase_fixture.gift_id assert stored_purchase.guest_id == gift_purchase_fixture.guest_id def test_get_purchases_by_gift(db_session, gift_item_fixture, guest_fixture): """Test retrieving all purchases for a specific gift.""" # Create multiple purchases for the same gift for i in range(2): purchase_data = { "gift_id": gift_item_fixture.id, "guest_id": guest_fixture.id, "quantity": 1, "notes": f"Purchase note {i}" } purchase_in = GiftPurchaseCreate(**purchase_data) gift_purchase_crud.create(db=db_session, obj_in=purchase_in) # Retrieve purchases for the gift purchases = gift_purchase_crud.get_by_gift( db=db_session, gift_id=gift_item_fixture.id ) assert len(purchases) >= 2 assert all(purchase.gift_id == gift_item_fixture.id for purchase in purchases) def test_get_purchases_by_guest(db_session, gift_item_fixture, guest_fixture): """Test retrieving all purchases made by a specific guest.""" # Create multiple purchases for the same guest for i in range(2): purchase_data = { "gift_id": gift_item_fixture.id, "guest_id": guest_fixture.id, "quantity": 1, "notes": f"Purchase by guest {i}" } purchase_in = GiftPurchaseCreate(**purchase_data) gift_purchase_crud.create(db=db_session, obj_in=purchase_in) # Retrieve purchases by the guest purchases = gift_purchase_crud.get_by_guest( db=db_session, guest_id=guest_fixture.id ) assert len(purchases) >= 2 assert all(purchase.guest_id == guest_fixture.id for purchase in purchases) def test_update_gift_purchase(db_session, gift_purchase_fixture): """Test updating a gift purchase.""" update_data = GiftPurchaseUpdate( quantity=2, purchase_price=39.98, notes="Updated purchase notes" ) updated_purchase = gift_purchase_crud.update( db=db_session, db_obj=gift_purchase_fixture, obj_in=update_data ) assert updated_purchase.quantity == 2 assert updated_purchase.purchase_price == 39.98 assert updated_purchase.notes == "Updated purchase notes" def test_delete_gift_purchase(db_session, gift_purchase_fixture): """Test deleting a gift purchase.""" purchase = gift_purchase_crud.remove(db=db_session, id=gift_purchase_fixture.id) assert purchase.id == gift_purchase_fixture.id deleted_purchase = gift_purchase_crud.get(db=db_session, id=gift_purchase_fixture.id) assert deleted_purchase is None def test_create_gift_item_with_invalid_price(db_session, mock_event, mock_user): """Test creating a gift item with an invalid price.""" gift_data = { "name": "Invalid Gift", "price": -10.0, # Negative price should be invalid "event_id": mock_event.id, "added_by": mock_user.id } with pytest.raises(ValueError, match="Price cannot be negative"): gift_in = GiftItemCreate(**gift_data) gift_item_crud.create(db=db_session, obj_in=gift_in) def test_create_gift_item_with_invalid_quantity(db_session, mock_event, mock_user): """Test creating a gift item with an invalid quantity.""" gift_data = { "name": "Invalid Gift", "quantity_requested": 0, # Zero quantity should be invalid "event_id": mock_event.id, "added_by": mock_user.id } with pytest.raises(ValueError, match="Quantity requested must be at least 1"): gift_in = GiftItemCreate(**gift_data) gift_item_crud.create(db=db_session, obj_in=gift_in)