mirror of
https://github.com/sstent/foodplanner.git
synced 2025-12-05 23:51:46 +00:00
fixing templates tab
This commit is contained in:
199
conftest.py
Normal file
199
conftest.py
Normal file
@@ -0,0 +1,199 @@
|
||||
"""
|
||||
Pytest configuration and fixtures for meal planner tests
|
||||
"""
|
||||
import pytest
|
||||
import os
|
||||
import tempfile
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from datetime import date, timedelta
|
||||
|
||||
# Import from main application
|
||||
from main import app, Base, get_db, Food, Meal, MealFood, Plan, Template, TemplateMeal, WeeklyMenu, WeeklyMenuDay, TrackedDay, TrackedMeal
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def test_db():
|
||||
"""Create a temporary test database for each test"""
|
||||
# Create temporary database file
|
||||
db_fd, db_path = tempfile.mkstemp(suffix='.db')
|
||||
database_url = f"sqlite:///{db_path}"
|
||||
|
||||
# Create engine and tables
|
||||
engine = create_engine(database_url, connect_args={"check_same_thread": False})
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
# Create session
|
||||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
yield TestingSessionLocal
|
||||
|
||||
# Cleanup
|
||||
os.close(db_fd)
|
||||
os.unlink(db_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def db_session(test_db):
|
||||
"""Provide a database session for tests"""
|
||||
session = test_db()
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def client(test_db):
|
||||
"""Create a test client with test database"""
|
||||
def override_get_db():
|
||||
db = test_db()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
|
||||
with TestClient(app) as test_client:
|
||||
yield test_client
|
||||
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_food(db_session):
|
||||
"""Create a sample food item"""
|
||||
food = Food(
|
||||
name="Test Food",
|
||||
serving_size="100",
|
||||
serving_unit="g",
|
||||
calories=200.0,
|
||||
protein=10.0,
|
||||
carbs=20.0,
|
||||
fat=5.0,
|
||||
fiber=2.0,
|
||||
sugar=3.0,
|
||||
sodium=100.0,
|
||||
calcium=50.0,
|
||||
source="manual",
|
||||
brand="Test Brand"
|
||||
)
|
||||
db_session.add(food)
|
||||
db_session.commit()
|
||||
db_session.refresh(food)
|
||||
return food
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_foods(db_session):
|
||||
"""Create multiple sample food items"""
|
||||
foods = [
|
||||
Food(
|
||||
name=f"Food {i}",
|
||||
serving_size="100",
|
||||
serving_unit="g",
|
||||
calories=100.0 * i,
|
||||
protein=5.0 * i,
|
||||
carbs=10.0 * i,
|
||||
fat=2.0 * i,
|
||||
fiber=1.0,
|
||||
sugar=2.0,
|
||||
sodium=50.0,
|
||||
calcium=25.0,
|
||||
source="manual",
|
||||
brand=f"Brand {i}"
|
||||
)
|
||||
for i in range(1, 4)
|
||||
]
|
||||
for food in foods:
|
||||
db_session.add(food)
|
||||
db_session.commit()
|
||||
for food in foods:
|
||||
db_session.refresh(food)
|
||||
return foods
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_meal(db_session, sample_foods):
|
||||
"""Create a sample meal with foods"""
|
||||
meal = Meal(
|
||||
name="Test Meal",
|
||||
meal_type="breakfast",
|
||||
meal_time="Breakfast"
|
||||
)
|
||||
db_session.add(meal)
|
||||
db_session.commit()
|
||||
db_session.refresh(meal)
|
||||
|
||||
# Add foods to meal
|
||||
for i, food in enumerate(sample_foods[:2], 1):
|
||||
meal_food = MealFood(
|
||||
meal_id=meal.id,
|
||||
food_id=food.id,
|
||||
quantity=float(i)
|
||||
)
|
||||
db_session.add(meal_food)
|
||||
|
||||
db_session.commit()
|
||||
db_session.refresh(meal)
|
||||
return meal
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_template(db_session, sample_meal):
|
||||
"""Create a sample template"""
|
||||
template = Template(name="Test Template")
|
||||
db_session.add(template)
|
||||
db_session.commit()
|
||||
db_session.refresh(template)
|
||||
|
||||
template_meal = TemplateMeal(
|
||||
template_id=template.id,
|
||||
meal_id=sample_meal.id,
|
||||
meal_time="Breakfast"
|
||||
)
|
||||
db_session.add(template_meal)
|
||||
db_session.commit()
|
||||
db_session.refresh(template)
|
||||
return template
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_plan(db_session, sample_meal):
|
||||
"""Create a sample plan"""
|
||||
plan = Plan(
|
||||
person="Sarah",
|
||||
date=date.today(),
|
||||
meal_id=sample_meal.id,
|
||||
meal_time="Breakfast"
|
||||
)
|
||||
db_session.add(plan)
|
||||
db_session.commit()
|
||||
db_session.refresh(plan)
|
||||
return plan
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_tracked_day(db_session, sample_meal):
|
||||
"""Create a sample tracked day with meals"""
|
||||
tracked_day = TrackedDay(
|
||||
person="Sarah",
|
||||
date=date.today(),
|
||||
is_modified=False
|
||||
)
|
||||
db_session.add(tracked_day)
|
||||
db_session.commit()
|
||||
db_session.refresh(tracked_day)
|
||||
|
||||
tracked_meal = TrackedMeal(
|
||||
tracked_day_id=tracked_day.id,
|
||||
meal_id=sample_meal.id,
|
||||
meal_time="Breakfast",
|
||||
quantity=1.0
|
||||
)
|
||||
db_session.add(tracked_meal)
|
||||
db_session.commit()
|
||||
db_session.refresh(tracked_day)
|
||||
return tracked_day
|
||||
34
main.py
34
main.py
@@ -60,11 +60,11 @@ app = FastAPI(title="Meal Planner")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
# Add a logging middleware to see incoming requests
|
||||
@app.middleware("http")
|
||||
async def log_requests(request: Request, call_next):
|
||||
logging.info(f"Incoming request: {request.method} {request.url.path}")
|
||||
response = await call_next(request)
|
||||
return response
|
||||
# @app.middleware("http")
|
||||
# async def log_requests(request: Request, call_next):
|
||||
# logging.info(f"Incoming request: {request.method} {request.url.path}")
|
||||
# response = await call_next(request)
|
||||
# return response
|
||||
|
||||
# Get the port from environment variable or default to 8999
|
||||
PORT = int(os.getenv("PORT", 8999))
|
||||
@@ -392,18 +392,18 @@ def scheduled_backup():
|
||||
backup_path = os.path.join(backup_dir, f"meal_planner_{timestamp}.db")
|
||||
backup_database(db_path, backup_path)
|
||||
|
||||
@app.on_event("startup")
|
||||
def startup_event():
|
||||
logging.info("DEBUG: Startup event triggered")
|
||||
run_migrations()
|
||||
logging.info("DEBUG: Startup event completed")
|
||||
|
||||
# Schedule the backup job - temporarily disabled for debugging
|
||||
scheduler = BackgroundScheduler()
|
||||
scheduler.add_job(scheduled_backup, 'cron', hour=0)
|
||||
scheduler.start()
|
||||
logging.info("Scheduled backup job started.")
|
||||
# logging.info("Startup completed - scheduler temporarily disabled")
|
||||
# @app.on_event("startup")
|
||||
# def startup_event():
|
||||
# logging.info("DEBUG: Startup event triggered")
|
||||
# run_migrations()
|
||||
# logging.info("DEBUG: Startup event completed")
|
||||
#
|
||||
# # Schedule the backup job - temporarily disabled for debugging
|
||||
# scheduler = BackgroundScheduler()
|
||||
# scheduler.add_job(scheduled_backup, 'cron', hour=0)
|
||||
# scheduler.start()
|
||||
# logging.info("Scheduled backup job started.")
|
||||
# # logging.info("Startup completed - scheduler temporarily disabled")
|
||||
|
||||
def test_sqlite_connection(db_path):
|
||||
"""Test if we can create and write to SQLite database file"""
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
fastapi==0.104.1
|
||||
fastapi
|
||||
starlette
|
||||
anyio
|
||||
uvicorn[standard]==0.24.0
|
||||
sqlalchemy>=2.0.24
|
||||
#psycopg2-binary==2.9.9
|
||||
|
||||
193
test_meals.py
Normal file
193
test_meals.py
Normal file
@@ -0,0 +1,193 @@
|
||||
"""
|
||||
Tests for Meals CRUD operations
|
||||
"""
|
||||
import pytest
|
||||
import json
|
||||
|
||||
|
||||
class TestMealsRoutes:
|
||||
"""Test meal-related routes"""
|
||||
|
||||
def test_get_meals_page(self, client):
|
||||
"""Test GET /meals page"""
|
||||
response = client.get("/meals")
|
||||
assert response.status_code == 200
|
||||
assert b"Meals" in response.content or b"meals" in response.content
|
||||
|
||||
def test_add_meal(self, client):
|
||||
"""Test POST /meals/add"""
|
||||
response = client.post("/meals/add", data={
|
||||
"name": "New Test Meal",
|
||||
"meal_type": "lunch",
|
||||
"meal_time": "Lunch"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
assert "meal_id" in data
|
||||
|
||||
def test_edit_meal(self, client, sample_meal):
|
||||
"""Test POST /meals/edit"""
|
||||
response = client.post("/meals/edit", data={
|
||||
"meal_id": sample_meal.id,
|
||||
"name": "Updated Meal Name",
|
||||
"meal_type": "dinner",
|
||||
"meal_time": "Dinner"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_edit_nonexistent_meal(self, client):
|
||||
"""Test editing non-existent meal"""
|
||||
response = client.post("/meals/edit", data={
|
||||
"meal_id": 99999,
|
||||
"name": "Updated Meal Name",
|
||||
"meal_type": "dinner",
|
||||
"meal_time": "Dinner"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_get_meal_details(self, client, sample_meal):
|
||||
"""Test GET /meals/{meal_id}"""
|
||||
response = client.get(f"/meals/{sample_meal.id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
assert data["id"] == sample_meal.id
|
||||
assert data["name"] == sample_meal.name
|
||||
|
||||
def test_get_nonexistent_meal_details(self, client):
|
||||
"""Test getting details for non-existent meal"""
|
||||
response = client.get("/meals/99999")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_delete_meals(self, client, sample_meal):
|
||||
"""Test POST /meals/delete"""
|
||||
response = client.post("/meals/delete",
|
||||
json={"meal_ids": [sample_meal.id]})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
|
||||
class TestMealFoods:
|
||||
"""Test meal-food relationships"""
|
||||
|
||||
def test_get_meal_foods(self, client, sample_meal):
|
||||
"""Test GET /meals/{meal_id}/foods"""
|
||||
response = client.get(f"/meals/{sample_meal.id}/foods")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
if len(data) > 0:
|
||||
assert "food_id" in data[0]
|
||||
assert "quantity" in data[0]
|
||||
|
||||
def test_add_food_to_meal(self, client, sample_meal, sample_food):
|
||||
"""Test POST /meals/{meal_id}/add_food"""
|
||||
response = client.post(f"/meals/{sample_meal.id}/add_food", data={
|
||||
"food_id": sample_food.id,
|
||||
"quantity": 2.5
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_remove_food_from_meal(self, client, sample_meal, db_session):
|
||||
"""Test DELETE /meals/remove_food/{meal_food_id}"""
|
||||
# Get the first meal food
|
||||
from main import MealFood
|
||||
meal_food = db_session.query(MealFood).filter(
|
||||
MealFood.meal_id == sample_meal.id
|
||||
).first()
|
||||
|
||||
if meal_food:
|
||||
response = client.delete(f"/meals/remove_food/{meal_food.id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_remove_nonexistent_meal_food(self, client):
|
||||
"""Test removing non-existent meal food"""
|
||||
response = client.delete("/meals/remove_food/99999")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
class TestMealsBulkUpload:
|
||||
"""Test bulk meal upload functionality"""
|
||||
|
||||
def test_bulk_upload_meals_csv(self, client, sample_foods, tmp_path):
|
||||
"""Test POST /meals/upload with CSV"""
|
||||
# Create test CSV file with meal recipes
|
||||
csv_content = f"""Meal Name,Food 1,Grams 1,Food 2,Grams 2
|
||||
Test Meal 1,{sample_foods[0].name},150,{sample_foods[1].name},200
|
||||
Test Meal 2,{sample_foods[1].name},100,{sample_foods[2].name},150"""
|
||||
|
||||
csv_file = tmp_path / "test_meals.csv"
|
||||
csv_file.write_text(csv_content)
|
||||
|
||||
with open(csv_file, 'rb') as f:
|
||||
response = client.post("/meals/upload",
|
||||
files={"file": ("test_meals.csv", f, "text/csv")})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "created" in data or "updated" in data or "errors" in data
|
||||
|
||||
def test_bulk_upload_meals_missing_food(self, client, tmp_path):
|
||||
"""Test bulk upload with missing food"""
|
||||
csv_content = """Meal Name,Food 1,Grams 1,Food 2,Grams 2
|
||||
Invalid Meal,Nonexistent Food,150,Another Fake Food,200"""
|
||||
|
||||
csv_file = tmp_path / "invalid_meals.csv"
|
||||
csv_file.write_text(csv_content)
|
||||
|
||||
with open(csv_file, 'rb') as f:
|
||||
response = client.post("/meals/upload",
|
||||
files={"file": ("invalid_meals.csv", f, "text/csv")})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "errors" in data
|
||||
assert len(data["errors"]) > 0
|
||||
|
||||
|
||||
class TestMealNutrition:
|
||||
"""Test meal nutrition calculations"""
|
||||
|
||||
def test_meal_nutrition_calculation(self, client, sample_meal, db_session):
|
||||
"""Test that meal nutrition is calculated correctly"""
|
||||
from main import calculate_meal_nutrition
|
||||
|
||||
nutrition = calculate_meal_nutrition(sample_meal, db_session)
|
||||
|
||||
assert "calories" in nutrition
|
||||
assert "protein" in nutrition
|
||||
assert "carbs" in nutrition
|
||||
assert "fat" in nutrition
|
||||
assert "fiber" in nutrition
|
||||
assert nutrition["calories"] > 0
|
||||
|
||||
def test_empty_meal_nutrition(self, client, db_session):
|
||||
"""Test nutrition calculation for empty meal"""
|
||||
from main import Meal, calculate_meal_nutrition
|
||||
|
||||
empty_meal = Meal(
|
||||
name="Empty Meal",
|
||||
meal_type="snack",
|
||||
meal_time="Snack 1"
|
||||
)
|
||||
db_session.add(empty_meal)
|
||||
db_session.commit()
|
||||
|
||||
nutrition = calculate_meal_nutrition(empty_meal, db_session)
|
||||
|
||||
assert nutrition["calories"] == 0
|
||||
assert nutrition["protein"] == 0
|
||||
184
test_plans.py
Normal file
184
test_plans.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""
|
||||
Tests for Plans CRUD operations
|
||||
"""
|
||||
import pytest
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
class TestPlansRoutes:
|
||||
"""Test plan-related routes"""
|
||||
|
||||
def test_get_plan_page(self, client):
|
||||
"""Test GET /plan page"""
|
||||
response = client.get("/plan?person=Sarah")
|
||||
assert response.status_code == 200
|
||||
assert b"Plan" in response.content or b"plan" in response.content
|
||||
|
||||
def test_get_plan_page_with_date(self, client):
|
||||
"""Test GET /plan page with specific date"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.get(f"/plan?person=Stuart&week_start_date={test_date}")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_add_to_plan(self, client, sample_meal):
|
||||
"""Test POST /plan/add"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/plan/add", data={
|
||||
"person": "Sarah",
|
||||
"plan_date": test_date,
|
||||
"meal_id": str(sample_meal.id),
|
||||
"meal_time": "Breakfast"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_add_to_plan_missing_fields(self, client):
|
||||
"""Test adding to plan with missing fields"""
|
||||
response = client.post("/plan/add", data={
|
||||
"person": "Sarah"
|
||||
# Missing plan_date, meal_id, meal_time
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_add_to_plan_invalid_meal(self, client):
|
||||
"""Test adding non-existent meal to plan"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/plan/add", data={
|
||||
"person": "Sarah",
|
||||
"plan_date": test_date,
|
||||
"meal_id": "99999",
|
||||
"meal_time": "Breakfast"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_get_day_plan(self, client, sample_plan):
|
||||
"""Test GET /plan/{person}/{date}"""
|
||||
test_date = sample_plan.date.isoformat()
|
||||
response = client.get(f"/plan/{sample_plan.person}/{test_date}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "meals" in data
|
||||
assert "day_totals" in data
|
||||
assert isinstance(data["meals"], list)
|
||||
|
||||
def test_get_day_plan_empty(self, client):
|
||||
"""Test getting plan for day with no meals"""
|
||||
future_date = (date.today() + timedelta(days=365)).isoformat()
|
||||
response = client.get(f"/plan/Sarah/{future_date}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "meals" in data
|
||||
assert len(data["meals"]) == 0
|
||||
|
||||
def test_update_day_plan(self, client, sample_meal):
|
||||
"""Test POST /plan/update_day"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/plan/update_day", data={
|
||||
"person": "Stuart",
|
||||
"date": test_date,
|
||||
"meal_ids": f"{sample_meal.id}"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_update_day_plan_multiple_meals(self, client, sample_meal, sample_foods, db_session):
|
||||
"""Test updating plan with multiple meals"""
|
||||
from main import Meal
|
||||
|
||||
# Create another meal
|
||||
meal2 = Meal(name="Second Meal", meal_type="lunch", meal_time="Lunch")
|
||||
db_session.add(meal2)
|
||||
db_session.commit()
|
||||
db_session.refresh(meal2)
|
||||
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/plan/update_day", data={
|
||||
"person": "Sarah",
|
||||
"date": test_date,
|
||||
"meal_ids": f"{sample_meal.id},{meal2.id}"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_remove_from_plan(self, client, sample_plan):
|
||||
"""Test DELETE /plan/{plan_id}"""
|
||||
response = client.delete(f"/plan/{sample_plan.id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_remove_nonexistent_plan(self, client):
|
||||
"""Test removing non-existent plan"""
|
||||
response = client.delete("/plan/99999")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
class TestPlanNavigation:
|
||||
"""Test plan navigation functionality"""
|
||||
|
||||
def test_plan_week_navigation(self, client, sample_meal):
|
||||
"""Test navigating between weeks"""
|
||||
# Get current week
|
||||
response = client.get("/plan?person=Sarah")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Add meal to today
|
||||
test_date = date.today().isoformat()
|
||||
client.post("/plan/add", data={
|
||||
"person": "Sarah",
|
||||
"plan_date": test_date,
|
||||
"meal_id": str(sample_meal.id),
|
||||
"meal_time": "Breakfast"
|
||||
})
|
||||
|
||||
# Get next week
|
||||
next_week = (date.today() + timedelta(days=7)).isoformat()
|
||||
response = client.get(f"/plan?person=Sarah&week_start_date={next_week}")
|
||||
assert response.status_code == 200
|
||||
|
||||
# Get previous week
|
||||
prev_week = (date.today() - timedelta(days=7)).isoformat()
|
||||
response = client.get(f"/plan?person=Sarah&week_start_date={prev_week}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
class TestDayNutrition:
|
||||
"""Test day nutrition calculations"""
|
||||
|
||||
def test_calculate_day_nutrition(self, client, sample_plan, db_session):
|
||||
"""Test day nutrition calculation"""
|
||||
from main import calculate_day_nutrition, Plan
|
||||
|
||||
plans = db_session.query(Plan).filter(
|
||||
Plan.person == sample_plan.person,
|
||||
Plan.date == sample_plan.date
|
||||
).all()
|
||||
|
||||
nutrition = calculate_day_nutrition(plans, db_session)
|
||||
|
||||
assert "calories" in nutrition
|
||||
assert "protein" in nutrition
|
||||
assert "carbs" in nutrition
|
||||
assert "fat" in nutrition
|
||||
assert "protein_pct" in nutrition
|
||||
assert "carbs_pct" in nutrition
|
||||
assert "fat_pct" in nutrition
|
||||
|
||||
def test_empty_day_nutrition(self, db_session):
|
||||
"""Test nutrition calculation for day with no meals"""
|
||||
from main import calculate_day_nutrition
|
||||
|
||||
nutrition = calculate_day_nutrition([], db_session)
|
||||
|
||||
assert nutrition["calories"] == 0
|
||||
assert nutrition["protein"] == 0
|
||||
assert nutrition["protein_pct"] == 0
|
||||
157
test_templates.py
Normal file
157
test_templates.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import sys
|
||||
import os
|
||||
print(f"DEBUG: Running with Python executable: {sys.executable}")
|
||||
print(f"DEBUG: Python version: {sys.version}")
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from main import app, get_db, SessionLocal, engine, Base, Template, TemplateMeal, Meal, MealFood, Food
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# Setup test database
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./test_meal_planner.db"
|
||||
test_engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=test_engine)
|
||||
|
||||
@pytest.fixture(name="session")
|
||||
def session_fixture():
|
||||
Base.metadata.create_all(bind=test_engine)
|
||||
db = TestingSessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
Base.metadata.drop_all(bind=test_engine)
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def client_fixture(session):
|
||||
def override_get_db():
|
||||
yield session
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
with TestClient(app) as client:
|
||||
yield client
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
def test_templates_page(client, session):
|
||||
response = client.get("/templates")
|
||||
assert response.status_code == 200
|
||||
assert "Meal Templates" in response.text
|
||||
|
||||
def test_create_template(client, session):
|
||||
# Create a food and a meal first
|
||||
food1 = Food(name="Apple", serving_size="1", serving_unit="medium", calories=95, protein=0.5, carbs=25, fat=0.3)
|
||||
session.add(food1)
|
||||
session.commit()
|
||||
session.refresh(food1)
|
||||
|
||||
meal1 = Meal(name="Fruit Salad", meal_type="breakfast", meal_time="Breakfast")
|
||||
session.add(meal1)
|
||||
session.commit()
|
||||
session.refresh(meal1)
|
||||
|
||||
meal_food1 = MealFood(meal_id=meal1.id, food_id=food1.id, quantity=1.0)
|
||||
session.add(meal_food1)
|
||||
session.commit()
|
||||
|
||||
response = client.post(
|
||||
"/templates/create",
|
||||
data={"name": "Test Template", "meal_assignments": f"Breakfast:{meal1.id},Lunch:"}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "success", "message": "Template created successfully"}
|
||||
|
||||
template = session.query(Template).filter(Template.name == "Test Template").first()
|
||||
assert template is not None
|
||||
assert len(template.template_meals) == 1
|
||||
assert template.template_meals[0].meal_time == "Breakfast"
|
||||
assert template.template_meals[0].meal_id == meal1.id
|
||||
|
||||
def test_create_template_duplicate_name(client, session):
|
||||
template = Template(name="Existing Template")
|
||||
session.add(template)
|
||||
session.commit()
|
||||
|
||||
response = client.post("/templates/create", data={"name": "Existing Template"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "error", "message": "Template with name 'Existing Template' already exists"}
|
||||
|
||||
def test_get_template_details(client, session):
|
||||
template = Template(name="Detail Template")
|
||||
session.add(template)
|
||||
session.commit()
|
||||
session.refresh(template)
|
||||
|
||||
response = client.get(f"/templates/{template.id}")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "success"
|
||||
assert response.json()["template"]["name"] == "Detail Template"
|
||||
|
||||
def test_update_template(client, session):
|
||||
food1 = Food(name="Orange", serving_size="1", serving_unit="medium", calories=62, protein=1.2, carbs=15.4, fat=0.2)
|
||||
session.add(food1)
|
||||
session.commit()
|
||||
session.refresh(food1)
|
||||
|
||||
meal1 = Meal(name="Orange Juice", meal_type="breakfast", meal_time="Breakfast")
|
||||
session.add(meal1)
|
||||
session.commit()
|
||||
session.refresh(meal1)
|
||||
|
||||
template = Template(name="Update Template")
|
||||
session.add(template)
|
||||
session.commit()
|
||||
session.refresh(template)
|
||||
|
||||
response = client.put(
|
||||
f"/templates/{template.id}",
|
||||
data={"name": "Updated Template Name", "meal_assignments": f"Breakfast:{meal1.id}"}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "success", "message": "Template updated successfully"}
|
||||
|
||||
updated_template = session.query(Template).filter(Template.id == template.id).first()
|
||||
assert updated_template.name == "Updated Template Name"
|
||||
assert len(updated_template.template_meals) == 1
|
||||
assert updated_template.template_meals[0].meal_time == "Breakfast"
|
||||
assert updated_template.template_meals[0].meal_id == meal1.id
|
||||
|
||||
def test_delete_template(client, session):
|
||||
template = Template(name="Delete Template")
|
||||
session.add(template)
|
||||
session.commit()
|
||||
session.refresh(template)
|
||||
|
||||
response = client.delete(f"/templates/{template.id}")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "success"}
|
||||
|
||||
deleted_template = session.query(Template).filter(Template.id == template.id).first()
|
||||
assert deleted_template is None
|
||||
|
||||
def test_use_template(client, session):
|
||||
food1 = Food(name="Banana", serving_size="1", serving_unit="medium", calories=105, protein=1.3, carbs=27, fat=0.4)
|
||||
session.add(food1)
|
||||
session.commit()
|
||||
session.refresh(food1)
|
||||
|
||||
meal1 = Meal(name="Banana Smoothie", meal_type="breakfast", meal_time="Breakfast")
|
||||
session.add(meal1)
|
||||
session.commit()
|
||||
session.refresh(meal1)
|
||||
|
||||
template = Template(name="Use Template")
|
||||
session.add(template)
|
||||
session.commit()
|
||||
session.refresh(template)
|
||||
|
||||
template_meal = TemplateMeal(template_id=template.id, meal_id=meal1.id, meal_time="Breakfast")
|
||||
session.add(template_meal)
|
||||
session.commit()
|
||||
|
||||
response = client.post(
|
||||
f"/templates/{template.id}/use",
|
||||
data={"person": "Sarah", "start_date": "2025-01-01"}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "success", "message": "Template applied successfully"}
|
||||
215
test_tracker.py
Normal file
215
test_tracker.py
Normal file
@@ -0,0 +1,215 @@
|
||||
"""
|
||||
Tests for Tracker CRUD operations
|
||||
"""
|
||||
import pytest
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
class TestTrackerRoutes:
|
||||
"""Test tracker-related routes"""
|
||||
|
||||
def test_get_tracker_page(self, client):
|
||||
"""Test GET /tracker page"""
|
||||
response = client.get("/tracker?person=Sarah")
|
||||
assert response.status_code == 200
|
||||
assert b"Tracker" in response.content or b"tracker" in response.content
|
||||
|
||||
def test_get_tracker_page_with_date(self, client):
|
||||
"""Test GET /tracker page with specific date"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.get(f"/tracker?person=Stuart&date={test_date}")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_tracker_add_meal(self, client, sample_meal):
|
||||
"""Test POST /tracker/add_meal"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/tracker/add_meal", data={
|
||||
"person": "Sarah",
|
||||
"date": test_date,
|
||||
"meal_id": str(sample_meal.id),
|
||||
"meal_time": "Breakfast",
|
||||
"quantity": "1.5"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_add_meal_default_quantity(self, client, sample_meal):
|
||||
"""Test adding meal with default quantity"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/tracker/add_meal", data={
|
||||
"person": "Stuart",
|
||||
"date": test_date,
|
||||
"meal_id": str(sample_meal.id),
|
||||
"meal_time": "Lunch"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_remove_meal(self, client, sample_tracked_day, db_session):
|
||||
"""Test DELETE /tracker/remove_meal/{tracked_meal_id}"""
|
||||
from main import TrackedMeal
|
||||
|
||||
tracked_meal = db_session.query(TrackedMeal).filter(
|
||||
TrackedMeal.tracked_day_id == sample_tracked_day.id
|
||||
).first()
|
||||
|
||||
if tracked_meal:
|
||||
response = client.delete(f"/tracker/remove_meal/{tracked_meal.id}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_remove_nonexistent_meal(self, client):
|
||||
"""Test removing non-existent tracked meal"""
|
||||
response = client.delete("/tracker/remove_meal/99999")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
class TestTrackerTemplates:
|
||||
"""Test tracker template functionality"""
|
||||
|
||||
def test_tracker_save_template(self, client, sample_tracked_day):
|
||||
"""Test POST /tracker/save_template"""
|
||||
test_date = sample_tracked_day.date.isoformat()
|
||||
response = client.post("/tracker/save_template", data={
|
||||
"person": sample_tracked_day.person,
|
||||
"date": test_date,
|
||||
"template_name": "New Saved Template"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_save_template_no_meals(self, client):
|
||||
"""Test saving template from day with no meals"""
|
||||
future_date = (date.today() + timedelta(days=365)).isoformat()
|
||||
response = client.post("/tracker/save_template", data={
|
||||
"person": "Sarah",
|
||||
"date": future_date,
|
||||
"template_name": "Empty Template"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_tracker_apply_template(self, client, sample_template):
|
||||
"""Test POST /tracker/apply_template"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/tracker/apply_template", data={
|
||||
"person": "Sarah",
|
||||
"date": test_date,
|
||||
"template_id": str(sample_template.id)
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_apply_nonexistent_template(self, client):
|
||||
"""Test applying non-existent template"""
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/tracker/apply_template", data={
|
||||
"person": "Sarah",
|
||||
"date": test_date,
|
||||
"template_id": "99999"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_tracker_apply_empty_template(self, client, db_session):
|
||||
"""Test applying template with no meals"""
|
||||
from main import Template
|
||||
|
||||
empty_template = Template(name="Empty Tracker Template")
|
||||
db_session.add(empty_template)
|
||||
db_session.commit()
|
||||
db_session.refresh(empty_template)
|
||||
|
||||
test_date = date.today().isoformat()
|
||||
response = client.post("/tracker/apply_template", data={
|
||||
"person": "Sarah",
|
||||
"date": test_date,
|
||||
"template_id": str(empty_template.id)
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
class TestTrackerReset:
|
||||
"""Test tracker reset functionality"""
|
||||
|
||||
def test_tracker_reset_to_plan(self, client, sample_tracked_day):
|
||||
"""Test POST /tracker/reset_to_plan"""
|
||||
test_date = sample_tracked_day.date.isoformat()
|
||||
response = client.post("/tracker/reset_to_plan", data={
|
||||
"person": sample_tracked_day.person,
|
||||
"date": test_date
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_tracker_reset_nonexistent_day(self, client):
|
||||
"""Test resetting non-existent tracked day"""
|
||||
future_date = (date.today() + timedelta(days=365)).isoformat()
|
||||
response = client.post("/tracker/reset_to_plan", data={
|
||||
"person": "Sarah",
|
||||
"date": future_date
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
|
||||
class TestTrackerNutrition:
|
||||
"""Test tracker nutrition calculations"""
|
||||
|
||||
def test_calculate_tracked_day_nutrition(self, client, sample_tracked_day, db_session):
|
||||
"""Test tracked day nutrition calculation"""
|
||||
from main import calculate_day_nutrition_tracked, TrackedMeal
|
||||
|
||||
tracked_meals = db_session.query(TrackedMeal).filter(
|
||||
TrackedMeal.tracked_day_id == sample_tracked_day.id
|
||||
).all()
|
||||
|
||||
nutrition = calculate_day_nutrition_tracked(tracked_meals, db_session)
|
||||
|
||||
assert "calories" in nutrition
|
||||
assert "protein" in nutrition
|
||||
assert "carbs" in nutrition
|
||||
assert "fat" in nutrition
|
||||
assert nutrition["calories"] >= 0
|
||||
|
||||
def test_tracked_day_with_quantity_multiplier(self, client, sample_meal, db_session):
|
||||
"""Test nutrition calculation with quantity multiplier"""
|
||||
from main import TrackedDay, TrackedMeal, calculate_day_nutrition_tracked
|
||||
|
||||
# Create tracked day with meal at 2x quantity
|
||||
tracked_day = TrackedDay(
|
||||
person="Sarah",
|
||||
date=date.today(),
|
||||
is_modified=True
|
||||
)
|
||||
db_session.add(tracked_day)
|
||||
db_session.commit()
|
||||
db_session.refresh(tracked_day)
|
||||
|
||||
tracked_meal = TrackedMeal(
|
||||
tracked_day_id=tracked_day.id,
|
||||
meal_id=sample_meal.id,
|
||||
meal_time="Breakfast",
|
||||
quantity=2.0
|
||||
)
|
||||
db_session.add(tracked_meal)
|
||||
db_session.commit()
|
||||
|
||||
tracked_meals = [tracked_meal]
|
||||
nutrition = calculate_day_nutrition_tracked(tracked_meals, db_session)
|
||||
|
||||
# Should be double the base meal nutrition
|
||||
assert nutrition["calories"] > 0
|
||||
62
test_weekly_menu.py
Normal file
62
test_weekly_menu.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
Tests for Weekly Menu operations
|
||||
"""
|
||||
import pytest
|
||||
|
||||
|
||||
class TestWeeklyMenuRoutes:
|
||||
"""Test weekly menu-related routes"""
|
||||
|
||||
def test_get_weekly_menu_page(self, client):
|
||||
"""Test GET /weeklymenu page"""
|
||||
response = client.get("/weeklymenu")
|
||||
assert response.status_code == 200
|
||||
assert b"Weekly" in response.content or b"weekly" in response.content or b"Menu" in response.content
|
||||
|
||||
|
||||
class TestWeeklyMenuCRUD:
|
||||
"""Test weekly menu CRUD operations"""
|
||||
|
||||
def test_create_weekly_menu(self, client, db_session, sample_template):
|
||||
"""Test creating a weekly menu"""
|
||||
from main import WeeklyMenu, WeeklyMenuDay
|
||||
|
||||
weekly_menu = WeeklyMenu(name="Test Weekly Menu")
|
||||
db_session.add(weekly_menu)
|
||||
db_session.commit()
|
||||
db_session.refresh(weekly_menu)
|
||||
|
||||
# Add days to weekly menu
|
||||
for day in range(7):
|
||||
menu_day = WeeklyMenuDay(
|
||||
weekly_menu_id=weekly_menu.id,
|
||||
day_of_week=day,
|
||||
template_id=sample_template.id
|
||||
)
|
||||
db_session.add(menu_day)
|
||||
|
||||
db_session.commit()
|
||||
|
||||
assert weekly_menu.id is not None
|
||||
assert len(weekly_menu.weekly_menu_days) == 7
|
||||
|
||||
def test_weekly_menu_relationships(self, client, db_session, sample_template):
|
||||
"""Test weekly menu relationships"""
|
||||
from main import WeeklyMenu, WeeklyMenuDay
|
||||
|
||||
weekly_menu = WeeklyMenu(name="Relationship Test Menu")
|
||||
db_session.add(weekly_menu)
|
||||
db_session.commit()
|
||||
db_session.refresh(weekly_menu)
|
||||
|
||||
menu_day = WeeklyMenuDay(
|
||||
weekly_menu_id=weekly_menu.id,
|
||||
day_of_week=0, # Monday
|
||||
template_id=sample_template.id
|
||||
)
|
||||
db_session.add(menu_day)
|
||||
db_session.commit()
|
||||
|
||||
# Verify relationships
|
||||
assert menu_day.weekly_menu.id == weekly_menu.id
|
||||
assert menu_day.template.id == sample_template.id
|
||||
200
tests/test_foods.py
Normal file
200
tests/test_foods.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
Tests for Foods CRUD operations
|
||||
"""
|
||||
import pytest
|
||||
from datetime import date
|
||||
import json
|
||||
|
||||
|
||||
class TestFoodsRoutes:
|
||||
"""Test food-related routes"""
|
||||
|
||||
def test_get_foods_page(self, client):
|
||||
"""Test GET /foods page"""
|
||||
response = client.get("/foods")
|
||||
assert response.status_code == 200
|
||||
assert b"Foods" in response.content or b"foods" in response.content
|
||||
|
||||
def test_add_food(self, client):
|
||||
"""Test POST /foods/add"""
|
||||
response = client.post("/foods/add", data={
|
||||
"name": "New Test Food",
|
||||
"serving_size": "100",
|
||||
"serving_unit": "g",
|
||||
"calories": 150.0,
|
||||
"protein": 8.0,
|
||||
"carbs": 15.0,
|
||||
"fat": 3.0,
|
||||
"fiber": 2.0,
|
||||
"sugar": 1.0,
|
||||
"sodium": 75.0,
|
||||
"calcium": 30.0,
|
||||
"source": "manual",
|
||||
"brand": "Test Brand"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_add_food_duplicate_name(self, client, sample_food):
|
||||
"""Test adding food with duplicate name"""
|
||||
response = client.post("/foods/add", data={
|
||||
"name": sample_food.name,
|
||||
"serving_size": "100",
|
||||
"serving_unit": "g",
|
||||
"calories": 150.0,
|
||||
"protein": 8.0,
|
||||
"carbs": 15.0,
|
||||
"fat": 3.0,
|
||||
"fiber": 2.0,
|
||||
"sugar": 1.0,
|
||||
"sodium": 75.0,
|
||||
"calcium": 30.0,
|
||||
"source": "manual",
|
||||
"brand": "Test Brand"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_edit_food(self, client, sample_food):
|
||||
"""Test POST /foods/edit"""
|
||||
response = client.post("/foods/edit", data={
|
||||
"food_id": sample_food.id,
|
||||
"name": "Updated Food Name",
|
||||
"serving_size": "150",
|
||||
"serving_unit": "g",
|
||||
"calories": 250.0,
|
||||
"protein": 12.0,
|
||||
"carbs": 25.0,
|
||||
"fat": 6.0,
|
||||
"fiber": 3.0,
|
||||
"sugar": 4.0,
|
||||
"sodium": 120.0,
|
||||
"calcium": 60.0,
|
||||
"source": "manual",
|
||||
"brand": "Updated Brand"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_edit_nonexistent_food(self, client):
|
||||
"""Test editing non-existent food"""
|
||||
response = client.post("/foods/edit", data={
|
||||
"food_id": 99999,
|
||||
"name": "Updated Food Name",
|
||||
"serving_size": "150",
|
||||
"serving_unit": "g",
|
||||
"calories": 250.0,
|
||||
"protein": 12.0,
|
||||
"carbs": 25.0,
|
||||
"fat": 6.0,
|
||||
"fiber": 3.0,
|
||||
"sugar": 4.0,
|
||||
"sodium": 120.0,
|
||||
"calcium": 60.0,
|
||||
"source": "manual",
|
||||
"brand": "Updated Brand"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "error"
|
||||
|
||||
def test_delete_foods(self, client, sample_foods):
|
||||
"""Test POST /foods/delete"""
|
||||
food_ids = [food.id for food in sample_foods[:2]]
|
||||
response = client.post("/foods/delete",
|
||||
json={"food_ids": food_ids})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_search_openfoodfacts(self, client):
|
||||
"""Test GET /foods/search_openfoodfacts"""
|
||||
# This test requires OpenFoodFacts SDK to be installed
|
||||
response = client.get("/foods/search_openfoodfacts?query=apple&limit=5")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
# Should either succeed or fail gracefully if module not installed
|
||||
assert "status" in data
|
||||
|
||||
|
||||
class TestFoodsBulkUpload:
|
||||
"""Test bulk food upload functionality"""
|
||||
|
||||
def test_bulk_upload_foods_csv(self, client, tmp_path):
|
||||
"""Test POST /foods/upload with CSV"""
|
||||
# Create test CSV file
|
||||
csv_content = """ID,Brand,Serving (g),Calories,Protein (g),Carbohydrate (g),Fat (g),Fiber (g),Sugar (g),Sodium (mg),Calcium (mg)
|
||||
Apple,Generic,100,52,0.3,14,0.2,2.4,10,1,6
|
||||
Banana,Generic,100,89,1.1,23,0.3,2.6,12,1,5"""
|
||||
|
||||
csv_file = tmp_path / "test_foods.csv"
|
||||
csv_file.write_text(csv_content)
|
||||
|
||||
with open(csv_file, 'rb') as f:
|
||||
response = client.post("/foods/upload",
|
||||
files={"file": ("test_foods.csv", f, "text/csv")})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "created" in data or "updated" in data
|
||||
|
||||
def test_bulk_upload_invalid_csv(self, client, tmp_path):
|
||||
"""Test bulk upload with invalid CSV"""
|
||||
csv_content = """Invalid,CSV,Format
|
||||
1,2,3"""
|
||||
|
||||
csv_file = tmp_path / "invalid.csv"
|
||||
csv_file.write_text(csv_content)
|
||||
|
||||
with open(csv_file, 'rb') as f:
|
||||
response = client.post("/foods/upload",
|
||||
files={"file": ("invalid.csv", f, "text/csv")})
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
# Should handle errors gracefully
|
||||
assert "status" in data or "errors" in data
|
||||
|
||||
|
||||
class TestOpenFoodFacts:
|
||||
"""Test OpenFoodFacts integration"""
|
||||
|
||||
def test_add_openfoodfacts_food(self, client):
|
||||
"""Test POST /foods/add_openfoodfacts"""
|
||||
response = client.post("/foods/add_openfoodfacts", data={
|
||||
"name": "OpenFoodFacts Test Food",
|
||||
"serving_size": "100",
|
||||
"serving_unit": "g",
|
||||
"calories": 180.0,
|
||||
"protein": 7.0,
|
||||
"carbs": 18.0,
|
||||
"fat": 4.0,
|
||||
"fiber": 2.5,
|
||||
"sugar": 3.5,
|
||||
"sodium": 90.0,
|
||||
"calcium": 40.0,
|
||||
"openfoodfacts_id": "12345678",
|
||||
"brand": "OFF Brand",
|
||||
"categories": "test,food"
|
||||
})
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["status"] == "success"
|
||||
|
||||
def test_get_openfoodfacts_product(self, client):
|
||||
"""Test GET /foods/get_openfoodfacts_product/{barcode}"""
|
||||
# Test with a well-known barcode (Nutella)
|
||||
response = client.get("/foods/get_openfoodfacts_product/3017620422003")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "status" in data
|
||||
|
||||
def test_openfoodfacts_by_category(self, client):
|
||||
"""Test GET /foods/openfoodfacts_by_category"""
|
||||
response = client.get("/foods/openfoodfacts_by_category?category=beverages&limit=5")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "status" in data
|
||||
Reference in New Issue
Block a user