Public Access
166 lines
4.8 KiB
Python
166 lines
4.8 KiB
Python
from datetime import date, datetime, timezone
|
|
from decimal import Decimal
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.requires_postgres
|
|
|
|
|
|
def _admin() -> dict:
|
|
return {"Authorization": "Bearer test-admin-token"}
|
|
|
|
|
|
@pytest.fixture
|
|
def family_with_groceries(db_session):
|
|
from app.models import FamilyProfile, GroceryItem, Ingredient
|
|
|
|
family = FamilyProfile(
|
|
id=uuid4(),
|
|
name="API Smoke Family",
|
|
household_size=4,
|
|
adult_count=2,
|
|
child_count=2,
|
|
calorie_target=500, # per-serving target; recipes are 360-640 range
|
|
)
|
|
db_session.add(family)
|
|
db_session.commit()
|
|
|
|
for ing in db_session.query(Ingredient).limit(20).all():
|
|
db_session.add(
|
|
GroceryItem(
|
|
id=uuid4(),
|
|
name=ing.name,
|
|
source="lucky_california",
|
|
external_id=f"ext-api-{ing.id}",
|
|
current_price=Decimal("3.99"),
|
|
regular_price=Decimal("4.99"),
|
|
is_on_sale=True,
|
|
scraped_at=datetime.now(timezone.utc),
|
|
)
|
|
)
|
|
db_session.commit()
|
|
|
|
from app.services.matcher import run_match_job
|
|
|
|
run_match_job(db_session)
|
|
return family
|
|
|
|
|
|
def test_generate_endpoint_returns_meal_plan(client, family_with_groceries):
|
|
body = {
|
|
"family_profile_id": str(family_with_groceries.id),
|
|
"week_start_date": "2026-05-11",
|
|
}
|
|
r = client.post("/api/admin/meal-plans/generate", json=body, headers=_admin())
|
|
assert r.status_code == 201, r.text
|
|
data = r.json()
|
|
assert "meal_plan_id" in data
|
|
assert 1 <= len(data["items"]) <= 3
|
|
assert "debug" in data
|
|
assert data["debug"]["feasible_count"] >= 1
|
|
|
|
|
|
def test_generate_endpoint_requires_admin_token(client, family_with_groceries):
|
|
body = {
|
|
"family_profile_id": str(family_with_groceries.id),
|
|
"week_start_date": "2026-05-18",
|
|
}
|
|
r = client.post("/api/admin/meal-plans/generate", json=body)
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_public_generate_uses_session_family_and_three_dinner_schedule(
|
|
client, family_with_groceries
|
|
):
|
|
from app.security import SESSION_COOKIE, issue_session
|
|
|
|
client.cookies.set(
|
|
SESSION_COOKIE,
|
|
issue_session(str(family_with_groceries.id)),
|
|
)
|
|
r = client.post(
|
|
"/api/meal-plans/generate",
|
|
json={"week_start_date": "2026-06-01"},
|
|
)
|
|
|
|
assert r.status_code == 201, r.text
|
|
data = r.json()
|
|
assert [item["day_of_week"] for item in data["items"]] == [1, 3, 5]
|
|
|
|
|
|
def test_public_generate_requires_session(client):
|
|
r = client.post(
|
|
"/api/meal-plans/generate",
|
|
json={"week_start_date": "2026-06-08"},
|
|
)
|
|
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_public_generate_preserves_populated_plan(client, db, family_with_groceries):
|
|
from app.models import MealPlan, MealPlanItem
|
|
from app.security import SESSION_COOKIE, issue_session
|
|
|
|
client.cookies.set(
|
|
SESSION_COOKIE,
|
|
issue_session(str(family_with_groceries.id)),
|
|
)
|
|
payload = {"week_start_date": "2026-06-15"}
|
|
first = client.post("/api/meal-plans/generate", json=payload)
|
|
assert first.status_code == 201, first.text
|
|
plan_id = first.json()["meal_plan_id"]
|
|
item_ids = {
|
|
item_id
|
|
for (item_id,) in db.query(MealPlanItem.id).filter(
|
|
MealPlanItem.meal_plan_id == plan_id
|
|
).all()
|
|
}
|
|
|
|
second = client.post("/api/meal-plans/generate", json=payload)
|
|
|
|
assert second.status_code == 409
|
|
assert db.query(MealPlan).filter(MealPlan.id == plan_id).first() is not None
|
|
assert {
|
|
item_id
|
|
for (item_id,) in db.query(MealPlanItem.id).filter(
|
|
MealPlanItem.meal_plan_id == plan_id
|
|
).all()
|
|
} == item_ids
|
|
|
|
|
|
def test_public_generate_replaces_only_empty_plan(client, db, family_with_groceries):
|
|
from app.models import MealPlan
|
|
from app.security import SESSION_COOKIE, issue_session
|
|
|
|
empty_plan = MealPlan(
|
|
family_profile_id=family_with_groceries.id,
|
|
week_start_date=date(2026, 6, 22),
|
|
status="draft",
|
|
)
|
|
db.add(empty_plan)
|
|
db.flush()
|
|
empty_plan_id = empty_plan.id
|
|
client.cookies.set(
|
|
SESSION_COOKIE,
|
|
issue_session(str(family_with_groceries.id)),
|
|
)
|
|
|
|
response = client.post(
|
|
"/api/meal-plans/generate",
|
|
json={"week_start_date": "2026-06-22"},
|
|
)
|
|
|
|
assert response.status_code == 201, response.text
|
|
assert response.json()["meal_plan_id"] != str(empty_plan_id)
|
|
assert db.query(MealPlan).filter(MealPlan.id == empty_plan_id).first() is None
|
|
|
|
|
|
def test_generate_endpoint_returns_404_for_unknown_family(client):
|
|
body = {
|
|
"family_profile_id": str(uuid4()),
|
|
"week_start_date": "2026-05-11",
|
|
}
|
|
r = client.post("/api/admin/meal-plans/generate", json=body, headers=_admin())
|
|
assert r.status_code == 404
|