diff --git a/api/shopping.py b/api/shopping.py index bcc98b0..06f28b8 100644 --- a/api/shopping.py +++ b/api/shopping.py @@ -167,6 +167,7 @@ async def request_meal_scoped( r: MealIdWrapper, request: Request, household=Depends(get_household_from_slug), + user=Depends(get_current_user), conn: aiosqlite.Connection = Depends(get_db), ): hid = household["id"] @@ -177,7 +178,7 @@ async def request_meal_scoped( return error_response(request, 404, "Meal not found") try: - item = await shopping.request_meal_scoped(conn, meal, hid) + item = await shopping.request_meal_scoped(conn, meal, hid, user.id) except ValueError as e: return error_response(request, 400, str(e)) return _to_meal_item(item) diff --git a/shopping/repository.py b/shopping/repository.py index 842c90a..1f33b2d 100644 --- a/shopping/repository.py +++ b/shopping/repository.py @@ -280,24 +280,25 @@ async def request( async def request_meal_scoped( - conn, meal: Any, household_id: int, person_id: Optional[int] = None + conn, meal: Any, household_id: int, person_id: int ) -> ShoppingListItem: if meal is None or getattr(meal, "id", -1) < 0: raise ValueError("Meal must have a valid id") if await is_requested_scoped(conn, meal.id, household_id): raise ValueError("Meal is already requested") - # Use 0 for outward personId to satisfy schema without binding to v1 persons - pid = person_id if person_id is not None else 0 + # Require a valid person id + if person_id is None or person_id < 0: + raise ValueError("Meal request must have a valid person id") - item = ShoppingListItem(ingredient_id=None, person_id=pid, meal_id=meal.id) + item = ShoppingListItem(ingredient_id=None, person_id=person_id, meal_id=meal.id) async with conn.execute( """ INSERT INTO ShoppingListItem (ingredient_id, person_id, meal_id, created_date, household_id) VALUES (?, ?, ?, ?, ?) """, - (None, pid, meal.id, item.created_date.isoformat(), household_id), + (None, person_id, meal.id, item.created_date.isoformat(), household_id), ) as cursor: item.id = cursor.lastrowid diff --git a/tests/test_migration_households.py b/tests/test_migration_households.py deleted file mode 100644 index 1a4cdda..0000000 --- a/tests/test_migration_households.py +++ /dev/null @@ -1,91 +0,0 @@ -import asyncio - -import aiosqlite - -import db -from tests.test_data import create_test_data -from scripts.migration_to_households import run_migration - - -async def table_has_column(conn: aiosqlite.Connection, table: str, col: str) -> bool: - async with conn.execute(f"PRAGMA table_info({table});") as c: - async for row in c: - if row[1] == col: - return True - return False - - -def test_migration_adds_tables_and_columns_and_ports_data(tmp_path): - async def _run(): - db_path = tmp_path / "test.sqlite" - conn = await db.connect(str(db_path)) - # Bootstrap v1 schema - await db.create(conn) - await conn.commit() - - # Seed some v1 data (persons, products, recipes, meals) - await create_test_data(conn) - await conn.commit() - - # Run migration - await run_migration(conn) - - # Verify new tables exist - for tbl in [ - "User", - "LocalCredentials", - "OAuthCredentials", - "Household", - "HouseholdMember", - "HouseholdInvitation", - ]: - async with conn.execute( - "SELECT name FROM sqlite_master WHERE type='table' AND name=?;", (tbl,) - ) as c: - assert await c.fetchone() is not None, f"Missing table {tbl}" - - # Verify household_id column exists on tenant tables - tenant_tables = [ - "Recipe", - "Ingredient", - "Meal", - "MealParticipant", - "MealRecipe", - "ShoppingList", - "ShoppingListItem", - ] - for tbl in tenant_tables: - assert await table_has_column(conn, tbl, "household_id"), f"{tbl} lacks household_id" - - # Default household exists - async with conn.execute( - "SELECT id, slug FROM Household WHERE slug='default' LIMIT 1;" - ) as c: - row = await c.fetchone() - assert row is not None - - # Persons were ported to Users and memberships created (if legacy Person table exists) - try: - async with conn.execute("SELECT COUNT(1) FROM Person;") as c: - row = await c.fetchone() - assert row is not None - person_count = int(row[0]) - except Exception: - person_count = 0 - async with conn.execute("SELECT COUNT(1) FROM User;") as c: - row = await c.fetchone() - assert row is not None - user_count = int(row[0]) - if person_count > 0: - assert user_count == person_count - - async with conn.execute("SELECT COUNT(1) FROM HouseholdMember;") as c: - row = await c.fetchone() - assert row is not None - member_count = int(row[0]) - if person_count > 0: - assert member_count == person_count - - await conn.close() - - asyncio.run(_run())