import pytest from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.database import Base, get_db, Food, Meal, MealFood from main import app # Setup a test database SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) @pytest.fixture(name="session") def session_fixture(): Base.metadata.create_all(bind=engine) db = TestingSessionLocal() try: yield db finally: db.close() Base.metadata.drop_all(bind=engine) @pytest.fixture(name="client") def client_fixture(session): def override_get_db(): yield session app.dependency_overrides[get_db] = override_get_db yield TestClient(app) app.dependency_overrides.clear() @pytest.fixture def sample_food_100g(session): food = Food(name="Sample Food 100g", serving_size="100", serving_unit="g", calories=100.0, protein=10.0, carbs=20.0, fat=5.0) session.add(food) session.commit() session.refresh(food) return food @pytest.fixture def sample_food_50g(session): food = Food(name="Sample Food 50g", serving_size="50", serving_unit="g", calories=50.0, protein=5.0, carbs=10.0, fat=2.5) session.add(food) session.commit() session.refresh(food) return food def test_convert_grams_to_quantity_100g_food(session, sample_food_100g): """Test convert_grams_to_quantity for a 100g serving size food""" grams = 150.0 quantity = session.convert_grams_to_quantity(sample_food_100g.id, grams, session) assert quantity == 1.5 def test_convert_grams_to_quantity_50g_food(session, sample_food_50g): """Test convert_grams_to_quantity for a 50g serving size food""" grams = 125.0 quantity = session.convert_grams_to_quantity(sample_food_50g.id, grams, session) assert quantity == 2.5 def test_convert_grams_to_quantity_invalid_food_id(session): """Test convert_grams_to_quantity with an invalid food ID""" with pytest.raises(ValueError, match="Food with ID 999 not found."): session.convert_grams_to_quantity(999, 100.0, session) def test_convert_grams_to_quantity_zero_serving_size(session): """Test convert_grams_to_quantity with zero serving size""" food = Food(name="Zero Serving Food", serving_size="0", serving_unit="g", calories=0, protein=0, carbs=0, fat=0) session.add(food) session.commit() session.refresh(food) with pytest.raises(ValueError, match="Serving size for food ID .* cannot be zero."): session.convert_grams_to_quantity(food.id, 100.0, session) def test_add_food_to_meal_grams_input(client, session, sample_food_100g): """Test adding food to a meal with grams input""" meal = Meal(name="Test Meal", meal_type="custom") session.add(meal) session.commit() session.refresh(meal) response = client.post( f"/meals/{meal.id}/add_food", data={"food_id": sample_food_100g.id, "quantity": 250.0} # 250 grams ) assert response.status_code == 200 assert response.json()["status"] == "success" meal_food = session.query(MealFood).filter(MealFood.meal_id == meal.id).first() assert meal_food.food_id == sample_food_100g.id assert meal_food.quantity == 2.5 # 250g / 100g serving = 2.5 multiplier def test_update_meal_food_quantity_grams_input(client, session, sample_food_50g): """Test updating meal food quantity with grams input""" meal = Meal(name="Update Meal", meal_type="custom") session.add(meal) session.commit() session.refresh(meal) # Add initial food with 100g (2.0 multiplier for 50g serving) initial_grams = 100.0 initial_quantity = session.convert_grams_to_quantity(sample_food_50g.id, initial_grams, session) meal_food = MealFood(meal_id=meal.id, food_id=sample_food_50g.id, quantity=initial_quantity) session.add(meal_food) session.commit() session.refresh(meal_food) updated_grams = 150.0 response = client.post( "/meals/update_food_quantity", data={"meal_food_id": meal_food.id, "quantity": updated_grams} ) assert response.status_code == 200 assert response.json()["status"] == "success" session.refresh(meal_food) expected_quantity = session.convert_grams_to_quantity(sample_food_50g.id, updated_grams, session) assert meal_food.quantity == expected_quantity # Test for bulk_upload_meals would require creating a mock UploadFile and CSV content # This is more complex and might be deferred or tested manually if the tool's capabilities are limited. # For now, we'll assume the backend change correctly handles the quantity. def test_tracker_add_food_grams_input(client, session, sample_food_100g): """Test adding single food to tracker with grams input""" person = "TestPerson" date_str = "2023-01-01" grams = 75.0 response = client.post( "/tracker/add_food", json={ "person": person, "date": date_str, "food_id": sample_food_100g.id, "quantity": grams, # 75 grams "meal_time": "Breakfast" } ) assert response.status_code == 200 assert response.json()["status"] == "success" # Verify the tracked meal food quantity tracked_meal = session.query(Meal).filter(Meal.name == sample_food_100g.name).first() assert tracked_meal is not None meal_food = session.query(MealFood).filter(MealFood.meal_id == tracked_meal.id).first() assert meal_food.quantity == 0.75 # 75g / 100g serving = 0.75 multiplier def test_update_tracked_meal_foods_grams_input(client, session, sample_food_100g, sample_food_50g): """Test updating tracked meal foods with grams input""" person = "TestPerson" date_str = "2023-01-02" # Create a tracked day and meal tracked_day = TrackedDay(person=person, date="2023-01-02", is_modified=False) session.add(tracked_day) session.commit() session.refresh(tracked_day) meal = Meal(name="Tracked Meal", meal_type="custom", meal_time="Lunch") session.add(meal) session.commit() session.refresh(meal) tracked_meal = TrackedMeal(tracked_day_id=tracked_day.id, meal_id=meal.id, meal_time="Lunch") session.add(tracked_meal) session.commit() session.refresh(tracked_meal) # Add initial foods meal_food_100g = MealFood(meal_id=meal.id, food_id=sample_food_100g.id, quantity=1.0) # 100g meal_food_50g = MealFood(meal_id=meal.id, food_id=sample_food_50g.id, quantity=2.0) # 100g session.add_all([meal_food_100g, meal_food_50g]) session.commit() session.refresh(meal_food_100g) session.refresh(meal_food_50g) # Update quantities: 100g food to 200g, 50g food to 75g updated_foods_data = [ {"id": meal_food_100g.id, "food_id": sample_food_100g.id, "quantity": 200.0, "is_custom": False}, {"id": meal_food_50g.id, "food_id": sample_food_50g.id, "quantity": 75.0, "is_custom": False} ] response = client.post( "/tracker/update_tracked_meal_foods", json={"tracked_meal_id": tracked_meal.id, "foods": updated_foods_data} ) assert response.status_code == 200 assert response.json()["status"] == "success" # Verify updated quantities session.refresh(tracked_meal) # Check if MealFood was converted to TrackedMealFood for changes tracked_food_100g = session.query(TrackedMealFood).filter( TrackedMealFood.tracked_meal_id == tracked_meal.id, TrackedMealFood.food_id == sample_food_100g.id ).first() assert tracked_food_100g.quantity == 2.0 # 200g / 100g serving = 2.0 tracked_food_50g = session.query(TrackedMealFood).filter( TrackedMealFood.tracked_meal_id == tracked_meal.id, TrackedMealFood.food_id == sample_food_50g.id ).first() assert tracked_food_50g.quantity == 1.5 # 75g / 50g serving = 1.5