commit 1c707260a4059cbdebb3e2720d3899b8773a1c37
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 22:03:48 2025 +1000
Sync tests
commit 5a71077121921a2cea9f3be9c1d1f7770691520a
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 21:57:00 2025 +1000
minor refactorings
commit 0bafec1b40bf5cf6f1554f6e5ec6e8f4e043d1cc
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 21:39:52 2025 +1000
Remove ids from shopping list
commit c1ee30d1b591b241dfa152d8a0fa9af1f6aaaed1
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 21:04:48 2025 +1000
Added more cases to shopping tests
commit ad3d598bdb574b1aeaabc354bb825ee428aaaa41
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 20:26:36 2025 +1000
Added 404 response when list not found
commit 336538dc47e12736f7f57d2c80252a700dc1e83b
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 20:15:52 2025 +1000
Tests for main
commit 238c0f89b2671348091a162ee519e5f1bf2cd66c
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 20:07:12 2025 +1000
Meals tests
commit b92f2432ee13f5384b6f1a9b8efe76bec9dbd920
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 19:57:45 2025 +1000
Shopping tests
commit b3c7ae20e6207cf0f9f04bdec5cb6145b04638d9
Author: jableader <jacobdunk@gmail.com>
Date: Wed Jul 30 19:40:19 2025 +1000
product tests passing
1249 lines
46 KiB
Python
1249 lines
46 KiB
Python
import unittest
|
|
import asyncio
|
|
from datetime import datetime
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import tests.test_data as test_data
|
|
import importlib
|
|
|
|
def reload_test_data():
|
|
global test_data
|
|
test_data = importlib.reload(test_data)
|
|
|
|
from db import connect, create
|
|
import shopping
|
|
import shopping.db as shopping_db
|
|
from shopping.db import ShoppingList, ShoppingListItem, StoreEnum
|
|
import ingredients.db as ingredients_db
|
|
import products.db as products_db
|
|
import persons
|
|
import meals
|
|
from meals.db import MealRecipe
|
|
import recipes
|
|
|
|
|
|
class TestShoppingModels(unittest.IsolatedAsyncioTestCase):
|
|
"""Test the shopping data models"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
def test_shopping_list_item_creation(self):
|
|
"""Test basic ShoppingListItem creation"""
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
|
|
item = ShoppingListItem(
|
|
id=1,
|
|
ingredient_id=ingredient.id,
|
|
person_id=1,
|
|
created_date=datetime.now()
|
|
)
|
|
|
|
self.assertEqual(item.id, 1)
|
|
self.assertEqual(item.ingredient_id, 1)
|
|
self.assertEqual(item.person_id, 1)
|
|
|
|
def test_shopping_list_creation(self):
|
|
"""Test basic ShoppingList creation"""
|
|
shopping_list = ShoppingList(
|
|
id=1,
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=1
|
|
)
|
|
|
|
self.assertEqual(shopping_list.id, 1)
|
|
self.assertEqual(shopping_list.store_name, StoreEnum.woolworths)
|
|
self.assertEqual(shopping_list.purchased_by_id, 1)
|
|
self.assertEqual(shopping_list.items, [])
|
|
|
|
def test_store_enum_values(self):
|
|
"""Test StoreEnum values"""
|
|
self.assertEqual(StoreEnum.woolworths, 'woolworths')
|
|
self.assertEqual(StoreEnum.coles, 'coles')
|
|
self.assertEqual(StoreEnum.home, '')
|
|
|
|
|
|
class TestShoppingValidation(unittest.IsolatedAsyncioTestCase):
|
|
"""Test shopping validation functions"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
def test_validate_request_valid_ingredient(self):
|
|
"""Test validation of valid ingredient request"""
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
item = ShoppingListItem(
|
|
ingredient_id=ingredient.id,
|
|
person_id=1
|
|
)
|
|
|
|
# Should not raise any exception
|
|
shopping_db.validate_request(item)
|
|
|
|
def test_validate_request_valid_meal(self):
|
|
"""Test validation of valid meal request"""
|
|
meal = meals.Meal(
|
|
id=1,
|
|
name="Dinner",
|
|
suggested_date=datetime.now()
|
|
)
|
|
item = ShoppingListItem(
|
|
meal_id=meal.id,
|
|
person_id=1
|
|
)
|
|
|
|
# Should not raise any exception
|
|
shopping_db.validate_request(item)
|
|
|
|
def test_validate_request_no_person(self):
|
|
"""Test validation fails when no person is specified"""
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
item = ShoppingListItem(
|
|
ingredient_id=ingredient.id,
|
|
person_id=-1 # Invalid person id
|
|
)
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
shopping_db.validate_request(item)
|
|
self.assertIn("Requests must have a person", str(context.exception))
|
|
|
|
def test_validate_request_no_ingredient_or_meal(self):
|
|
"""Test validation fails when neither ingredient nor meal is specified"""
|
|
item = ShoppingListItem(person_id=1)
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
shopping_db.validate_request(item)
|
|
self.assertIn("Request must have either an ingredient or a meal", str(context.exception))
|
|
|
|
|
|
class TestShoppingRequests(unittest.IsolatedAsyncioTestCase):
|
|
"""Test shopping request functionality"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
async def test_request_ingredient(self):
|
|
"""Test requesting an ingredient"""
|
|
# Create test ingredient
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# Request the ingredient
|
|
person = test_data.Persons.jacob
|
|
item = await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
self.assertIsNotNone(item.id)
|
|
self.assertEqual(item.ingredient_id, ingredient.id)
|
|
self.assertEqual(item.person_id, person.id)
|
|
self.assertIsNone(item.meal_id)
|
|
|
|
async def test_request_meal(self):
|
|
"""Test requesting a meal"""
|
|
# Create a mock meal with proper structure
|
|
meal = meals.Meal(
|
|
id=1,
|
|
name="Test Meal",
|
|
suggested_date=datetime.now()
|
|
)
|
|
|
|
with patch('shopping.db.is_requested', return_value=False):
|
|
person = test_data.Persons.jacob
|
|
item = await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
self.assertIsNotNone(item.id)
|
|
self.assertEqual(item.meal_id, meal.id)
|
|
self.assertEqual(item.person_id, person.id)
|
|
self.assertIsNone(item.ingredient_id)
|
|
|
|
async def test_request_both_ingredient_and_meal_fails(self):
|
|
"""Test that requesting both ingredient and meal fails"""
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
meal = meals.Meal(
|
|
id=1,
|
|
name="Test Meal",
|
|
suggested_date=datetime.now()
|
|
)
|
|
person = test_data.Persons.jacob
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient, meal=meal)
|
|
self.assertIn("Cannot request both an ingredient and a meal", str(context.exception))
|
|
|
|
async def test_request_neither_ingredient_nor_meal_fails(self):
|
|
"""Test that requesting neither ingredient nor meal fails"""
|
|
person = test_data.Persons.jacob
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.request(self.conn, person)
|
|
self.assertIn("Must specify either an ingredient or a meal to request", str(context.exception))
|
|
|
|
async def test_request_meal_already_requested_fails(self):
|
|
"""Test that requesting an already requested meal fails"""
|
|
meal = meals.Meal(
|
|
id=1,
|
|
name="Test Meal",
|
|
suggested_date=datetime.now()
|
|
)
|
|
person = test_data.Persons.jacob
|
|
|
|
with patch('shopping.db.is_requested', return_value=True):
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
self.assertIn("Meal is already requested", str(context.exception))
|
|
|
|
async def test_remove_request_meal(self):
|
|
"""Test removing a meal request"""
|
|
# First create a meal request
|
|
meal = meals.Meal(
|
|
id=1,
|
|
name="Test Meal",
|
|
suggested_date=datetime.now()
|
|
)
|
|
person = test_data.Persons.jacob
|
|
|
|
with patch('shopping.db.is_requested', return_value=False):
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Then remove it
|
|
result = await shopping_db.remove_request(self.conn, meal=meal)
|
|
self.assertTrue(result)
|
|
|
|
async def test_remove_request_ingredient(self):
|
|
"""Test removing an ingredient request"""
|
|
# Create test ingredient
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
# Remove the request
|
|
result = await shopping_db.remove_request(self.conn, person=person, ingredient=ingredient)
|
|
self.assertTrue(result)
|
|
|
|
async def test_remove_request_invalid_parameters_fails(self):
|
|
"""Test that removing request with invalid parameters fails"""
|
|
person = test_data.Persons.jacob
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.remove_request(self.conn, person=person)
|
|
self.assertIn("Must specify either a meal or an ingredient to remove", str(context.exception))
|
|
|
|
|
|
class TestShoppingPurchase(unittest.IsolatedAsyncioTestCase):
|
|
"""Test shopping purchase functionality"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
async def test_purchase_shopping_list(self):
|
|
"""Test purchasing a shopping list"""
|
|
# Create test ingredient
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# First create a request for the ingredient
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
# Create shopping list item (this will reference the existing request)
|
|
item = ShoppingListItem(
|
|
ingredient=ingredient,
|
|
person_id=test_data.Persons.jacob.id
|
|
)
|
|
|
|
# Create shopping list
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=test_data.Persons.jacob.id,
|
|
items=[item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
self.assertIsNotNone(shopping_list.id)
|
|
self.assertIsNotNone(item.list_id)
|
|
self.assertEqual(item.list_id, shopping_list.id)
|
|
|
|
async def test_purchase_no_person_fails(self):
|
|
"""Test that purchasing without a person fails"""
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=-1, # Invalid person id
|
|
items=[]
|
|
)
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
self.assertIn("Shopping list must have a person id", str(context.exception))
|
|
|
|
async def test_purchase_no_items_fails(self):
|
|
"""Test that purchasing with no items fails"""
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=test_data.Persons.jacob.id,
|
|
items=[]
|
|
)
|
|
|
|
with self.assertRaises(ValueError) as context:
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
self.assertIn("Shopping list must have items", str(context.exception))
|
|
|
|
|
|
class TestShoppingHelperFunctions(unittest.IsolatedAsyncioTestCase):
|
|
"""Test shopping helper functions"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
async def test_to_lookups(self):
|
|
"""Test to_lookups function"""
|
|
# Create actual items with proper IDs - need to insert them first to get valid lookups
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
|
|
# Insert the ingredient to get a valid ID
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# Insert test meal and recipe to get valid IDs
|
|
meal = meals.Meal(
|
|
id=1,
|
|
suggested_date=datetime.now()
|
|
)
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
recipe = recipes.Recipe(
|
|
id=1,
|
|
name="Test Recipe",
|
|
link="http://example.com",
|
|
serves=4,
|
|
created_by_id=1
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
items = [
|
|
ShoppingListItem(ingredient_id=ingredient.id, meal_id=meal.id, recipe_id=recipe.id)
|
|
]
|
|
|
|
# Test the function - it should populate the lookups based on IDs
|
|
meals_lookup, recipes_lookup, ingredients_lookup = await shopping.to_lookups(
|
|
self.conn, items
|
|
)
|
|
|
|
# Check that the lookups contain our objects
|
|
self.assertEqual(len(meals_lookup), 1)
|
|
self.assertEqual(len(recipes_lookup), 1)
|
|
self.assertEqual(len(ingredients_lookup), 1)
|
|
self.assertEqual(meals_lookup[1].id, meal.id)
|
|
self.assertEqual(recipes_lookup[1].id, recipe.id)
|
|
self.assertEqual(ingredients_lookup[1].id, ingredient.id)
|
|
|
|
# Items should still have their IDs
|
|
self.assertEqual(items[0].meal_id, meal.id)
|
|
self.assertEqual(items[0].recipe_id, recipe.id)
|
|
self.assertEqual(items[0].ingredient_id, ingredient.id)
|
|
|
|
def test_flatten_items_with_meal(self):
|
|
"""Test flatten_items function with meal items"""
|
|
# Create meal with recipes and ingredients
|
|
recipe_ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Recipe Ingredient",
|
|
line="500g recipe ingredient",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
extra_ingredient = ingredients_db.Ingredient(
|
|
id=2,
|
|
name="Extra Ingredient",
|
|
line="200g extra ingredient",
|
|
unit="g",
|
|
quantity=200.0,
|
|
preparation="diced"
|
|
)
|
|
|
|
recipe = recipes.Recipe(
|
|
id=1,
|
|
name="Test Recipe",
|
|
link="http://example.com",
|
|
serves=4,
|
|
created_by_id=1,
|
|
ingredients=[recipe_ingredient]
|
|
)
|
|
meal_recipe = MealRecipe(
|
|
meal_id=1,
|
|
recipe_id=1,
|
|
servings=2.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=1,
|
|
suggested_date=datetime.now(),
|
|
recipes=[meal_recipe],
|
|
extra_ingredients=[extra_ingredient]
|
|
)
|
|
|
|
item = ShoppingListItem(meal_id=meal.id, person_id=1)
|
|
|
|
# Create lookups for flatten_items
|
|
meals_lookup = {meal.id: meal}
|
|
flattened = list(shopping.flatten_items([item], meals_lookup))
|
|
|
|
# Should have 2 items: one for recipe ingredient, one for extra ingredient
|
|
self.assertEqual(len(flattened), 2)
|
|
self.assertEqual(flattened[0].ingredient_id, 1)
|
|
self.assertEqual(flattened[1].ingredient_id, 2)
|
|
|
|
def test_flatten_items_without_meal(self):
|
|
"""Test flatten_items function with non-meal items"""
|
|
ingredient = ingredients_db.Ingredient(
|
|
id=1,
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
item = ShoppingListItem(ingredient_id=ingredient.id, person_id=1)
|
|
|
|
# Empty lookups since no meal/recipe is involved
|
|
flattened = list(shopping.flatten_items([item], {}))
|
|
|
|
self.assertEqual(len(flattened), 1)
|
|
self.assertEqual(flattened[0], item)
|
|
|
|
async def test_get_persons_requests(self):
|
|
"""Test get_persons_requests function"""
|
|
person_id = 1
|
|
|
|
# Create actual ingredients and requests
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# Create a request for person 1
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
# Get the person's requests
|
|
requests = await shopping.get_persons_requests(self.conn, person_id)
|
|
|
|
# Should have one request for the ingredient
|
|
self.assertEqual(len(requests), 1)
|
|
self.assertEqual(requests[0].id, ingredient.id)
|
|
|
|
async def test_get_outstanding_requests(self):
|
|
"""Test get_outstanding_requests function"""
|
|
# Create actual data
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# Create requests
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
# Test the function
|
|
outstanding, purchased, meal_requests, _, _, _ = await shopping.get_outstanding_requests(self.conn)
|
|
|
|
# Should have one outstanding ingredient request
|
|
self.assertGreaterEqual(len(outstanding), 1)
|
|
self.assertEqual(len(purchased), 0)
|
|
self.assertEqual(len(meal_requests), 0)
|
|
|
|
async def test_is_requested(self):
|
|
"""Test is_requested function"""
|
|
meal = meals.Meal(
|
|
id=1,
|
|
suggested_date=datetime.now()
|
|
)
|
|
|
|
# Test with a meal that hasn't been requested
|
|
result = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertFalse(result)
|
|
|
|
# Create a request for the meal
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Now it should be requested
|
|
result = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertTrue(result)
|
|
|
|
async def test_is_requested_invalid_meal(self):
|
|
"""Test is_requested with invalid meal"""
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
name="Invalid Meal",
|
|
suggested_date=datetime.now()
|
|
)
|
|
|
|
result = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertFalse(result)
|
|
|
|
async def test_load_shopping_list(self):
|
|
"""Test load_shopping_list function"""
|
|
# Create and purchase a shopping list first
|
|
ingredient = ingredients_db.Ingredient(
|
|
name="Broccoli",
|
|
line="500g fresh broccoli",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="chopped"
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
|
|
|
# First create a request for the ingredient
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient)
|
|
|
|
item = ShoppingListItem(
|
|
ingredient_id=ingredient.id,
|
|
person_id=test_data.Persons.jacob.id
|
|
)
|
|
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=test_data.Persons.jacob.id,
|
|
items=[item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Now load it back
|
|
loaded_list = await shopping_db.load_shopping_list(self.conn, shopping_list.id)
|
|
|
|
self.assertIsNotNone(loaded_list)
|
|
self.assertEqual(loaded_list.id, shopping_list.id)
|
|
self.assertEqual(loaded_list.store_name, shopping_list.store_name)
|
|
self.assertEqual(len(loaded_list.items), 1)
|
|
|
|
|
|
class TestShoppingComplexEdgeCases(unittest.IsolatedAsyncioTestCase):
|
|
"""Test shopping complex edge cases for meal requests and purchases"""
|
|
|
|
async def asyncSetUp(self):
|
|
self.conn = await connect(':memory:')
|
|
await create(self.conn)
|
|
await test_data.create_persons(self.conn)
|
|
reload_test_data()
|
|
return await super().asyncSetUp()
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.conn.close()
|
|
return await super().asyncTearDown()
|
|
|
|
async def test_meal_request_includes_ingredients_in_outstanding(self):
|
|
"""Test that when user requests a meal, get_outstanding_requests includes the ingredients of that meal"""
|
|
# Create a recipe with ingredients
|
|
recipe = recipes.Recipe(
|
|
id=-1,
|
|
name="Test Pasta Recipe",
|
|
link="http://example.com/pasta",
|
|
serves=4,
|
|
created_by_id=test_data.Persons.jacob.id
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
# Create ingredients for the recipe
|
|
pasta_ingredient = ingredients_db.Ingredient(
|
|
name="Pasta",
|
|
line="500g pasta",
|
|
unit="g",
|
|
quantity=500.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
tomato_ingredient = ingredients_db.Ingredient(
|
|
name="Tomatoes",
|
|
line="400g canned tomatoes",
|
|
unit="g",
|
|
quantity=400.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, pasta_ingredient)
|
|
await ingredients_db.insert_ingredient(self.conn, tomato_ingredient)
|
|
|
|
# Load the recipe with its ingredients
|
|
await recipes.load_recipe_ingredients(self.conn, recipe)
|
|
|
|
# Create a meal with this recipe and extra ingredients
|
|
extra_ingredient = ingredients_db.Ingredient(
|
|
name="Garlic Bread",
|
|
line="1 loaf garlic bread",
|
|
unit="loaf",
|
|
quantity=1.0,
|
|
preparation=""
|
|
)
|
|
|
|
meal_recipe = MealRecipe(
|
|
meal_id=-1,
|
|
recipe_id=recipe.id,
|
|
servings=2.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
suggested_date=datetime.now(),
|
|
chefs=[test_data.Persons.jacob],
|
|
cleanup=[test_data.Persons.ryan],
|
|
consumers=[test_data.Persons.ellie],
|
|
recipes=[meal_recipe],
|
|
extra_ingredients=[extra_ingredient]
|
|
)
|
|
|
|
# Insert the meal
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
# Request the meal
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Get outstanding requests
|
|
outstanding, purchased, meal_requests, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
|
|
# Should have one meal request
|
|
self.assertEqual(len(meal_requests), 1)
|
|
self.assertEqual(meal_requests[0].meal_id, meal.id)
|
|
|
|
# Should have 3 outstanding items: 2 from recipe + 1 extra ingredient
|
|
self.assertEqual(len(outstanding), 3)
|
|
|
|
# Check that all ingredients are included
|
|
ingredient_names = {ingredients_lookup[item.ingredient_id].name for item in outstanding if item.ingredient_id in ingredients_lookup}
|
|
self.assertIn("Pasta", ingredient_names)
|
|
self.assertIn("Tomatoes", ingredient_names)
|
|
self.assertIn("Garlic Bread", ingredient_names)
|
|
|
|
# All should be associated with the meal
|
|
for item in outstanding:
|
|
self.assertEqual(item.meal_id, meal.id)
|
|
|
|
async def test_partial_meal_purchase_moves_item_to_purchased(self):
|
|
"""Test that a user can purchase an individual item from a meal, moving it to purchased items"""
|
|
# Create a recipe with multiple ingredients
|
|
recipe = recipes.Recipe(
|
|
id=-1,
|
|
name="Multi-Ingredient Recipe",
|
|
link="http://example.com/multi",
|
|
serves=4,
|
|
created_by_id=test_data.Persons.jacob.id
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
# Create multiple ingredients for the recipe
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
name="Rice",
|
|
line="200g rice",
|
|
unit="g",
|
|
quantity=200.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
name="Chicken",
|
|
line="300g chicken breast",
|
|
unit="g",
|
|
quantity=300.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
ingredient3 = ingredients_db.Ingredient(
|
|
name="Vegetables",
|
|
line="150g mixed vegetables",
|
|
unit="g",
|
|
quantity=150.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient3)
|
|
|
|
# Load the recipe with its ingredients
|
|
await recipes.load_recipe_ingredients(self.conn, recipe)
|
|
|
|
# Create and insert a meal
|
|
meal_recipe = MealRecipe(
|
|
meal_id=-1,
|
|
recipe_id=recipe.id,
|
|
servings=2.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
suggested_date=datetime.now(),
|
|
chefs=[test_data.Persons.jacob],
|
|
cleanup=[test_data.Persons.ryan],
|
|
consumers=[test_data.Persons.ellie],
|
|
recipes=[meal_recipe]
|
|
)
|
|
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
# Request the meal
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Get initial outstanding requests
|
|
outstanding_before, purchased_before, meal_requests_before, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_before), 3) # All 3 ingredients
|
|
self.assertEqual(len(purchased_before), 0) # Nothing purchased yet
|
|
|
|
# Purchase only one ingredient (Rice) from the meal
|
|
rice_item = None
|
|
for item in outstanding_before:
|
|
if item.ingredient_id in ingredients_lookup and ingredients_lookup[item.ingredient_id].name == "Rice":
|
|
rice_item = ShoppingListItem(
|
|
ingredient_id=item.ingredient_id,
|
|
person_id=person.id,
|
|
meal_id=meal.id,
|
|
recipe_id=recipe.id
|
|
)
|
|
break
|
|
|
|
self.assertIsNotNone(rice_item)
|
|
|
|
# Create and purchase a shopping list with just the rice
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=person.id,
|
|
items=[rice_item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Get outstanding requests after purchase
|
|
outstanding_after, purchased_after, meal_requests_after, meals_lookup2, recipes_lookup2, ingredients_lookup2 = await shopping.get_outstanding_requests(self.conn)
|
|
|
|
# Should have 2 outstanding items (Chicken and Vegetables)
|
|
self.assertEqual(len(outstanding_after), 2)
|
|
outstanding_names = {ingredients_lookup2[item.ingredient_id].name for item in outstanding_after if item.ingredient_id in ingredients_lookup2}
|
|
self.assertIn("Chicken", outstanding_names)
|
|
self.assertIn("Vegetables", outstanding_names)
|
|
self.assertNotIn("Rice", outstanding_names)
|
|
|
|
# Should have 1 purchased item (Rice)
|
|
self.assertEqual(len(purchased_after), 1)
|
|
self.assertEqual(purchased_after[0].ingredient_id, ingredient1.id)
|
|
|
|
# Meal should still be requested (not all ingredients purchased)
|
|
self.assertEqual(len(meal_requests_after), 1)
|
|
|
|
async def test_complete_meal_purchase_unrequests_and_marks_purchased(self):
|
|
"""Test that when all items of a meal are purchased, the meal is unrequested and marked as purchased"""
|
|
# Create a simple recipe with 2 ingredients
|
|
recipe = recipes.Recipe(
|
|
id=-1,
|
|
name="Simple Recipe",
|
|
link="http://example.com/simple",
|
|
serves=2,
|
|
created_by_id=test_data.Persons.jacob.id
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
# Create ingredients for the recipe
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
name="Bread",
|
|
line="2 slices bread",
|
|
unit="slices",
|
|
quantity=2.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
name="Butter",
|
|
line="10g butter",
|
|
unit="g",
|
|
quantity=10.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
|
|
|
# Load the recipe with its ingredients
|
|
await recipes.load_recipe_ingredients(self.conn, recipe)
|
|
|
|
# Create and insert a meal
|
|
meal_recipe = MealRecipe(
|
|
meal_id=-1,
|
|
recipe_id=recipe.id,
|
|
servings=1.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
suggested_date=datetime.now(),
|
|
chefs=[test_data.Persons.jacob],
|
|
cleanup=[test_data.Persons.ryan],
|
|
consumers=[test_data.Persons.ellie],
|
|
recipes=[meal_recipe]
|
|
)
|
|
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
# Request the meal
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Verify meal is requested
|
|
is_requested_before = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertTrue(is_requested_before)
|
|
|
|
# Get initial outstanding requests
|
|
outstanding_before, purchased_before, meal_requests_before, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_before), 2) # Both ingredients
|
|
self.assertEqual(len(meal_requests_before), 1) # Meal is requested
|
|
|
|
# Verify the meal is not marked as purchased yet
|
|
found_meal_before = await meals.find_meal_by_id(self.conn, meal.id)
|
|
self.assertIsNone(found_meal_before.purchase_date)
|
|
|
|
# Purchase all ingredients from the meal
|
|
shopping_items = []
|
|
for item in outstanding_before:
|
|
shopping_items.append(ShoppingListItem(
|
|
ingredient_id=item.ingredient_id,
|
|
person_id=person.id,
|
|
meal_id=meal.id,
|
|
recipe_id=recipe.id
|
|
))
|
|
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=person.id,
|
|
items=shopping_items
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Verify meal is no longer requested
|
|
is_requested_after = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertFalse(is_requested_after)
|
|
|
|
# Verify meal is marked as purchased
|
|
found_meal_after = await meals.find_meal_by_id(self.conn, meal.id)
|
|
self.assertIsNotNone(found_meal_after.purchase_date)
|
|
|
|
# Get outstanding requests after complete purchase
|
|
outstanding_after, purchased_after, meal_requests_after, _, _, _ = await shopping.get_outstanding_requests(self.conn)
|
|
|
|
# Should have no outstanding items from this meal
|
|
self.assertEqual(len(outstanding_after), 0)
|
|
|
|
# Should have no purchased items (meal is complete so ingredients don't appear)
|
|
self.assertEqual(len(purchased_after), 0)
|
|
|
|
# Should have no meal requests
|
|
self.assertEqual(len(meal_requests_after), 0)
|
|
|
|
async def test_complete_meal_with_extra_ingredients_purchase(self):
|
|
"""Test that meals with both recipe ingredients and extra ingredients are properly handled"""
|
|
# Create a recipe with 1 ingredient
|
|
recipe = recipes.Recipe(
|
|
id=-1,
|
|
name="Recipe with Extra",
|
|
link="http://example.com/extra",
|
|
serves=2,
|
|
created_by_id=test_data.Persons.jacob.id
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
# Create recipe ingredient
|
|
recipe_ingredient = ingredients_db.Ingredient(
|
|
name="Main Ingredient",
|
|
line="200g main ingredient",
|
|
unit="g",
|
|
quantity=200.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, recipe_ingredient)
|
|
await recipes.load_recipe_ingredients(self.conn, recipe)
|
|
|
|
# Create extra ingredient (not part of recipe)
|
|
extra_ingredient = ingredients_db.Ingredient(
|
|
name="Side Dish",
|
|
line="1 side dish",
|
|
unit="item",
|
|
quantity=1.0,
|
|
preparation=""
|
|
)
|
|
|
|
# Create and insert a meal with both recipe and extra ingredients
|
|
meal_recipe = MealRecipe(
|
|
meal_id=-1,
|
|
recipe_id=recipe.id,
|
|
servings=1.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
suggested_date=datetime.now(),
|
|
chefs=[test_data.Persons.jacob],
|
|
cleanup=[test_data.Persons.ryan],
|
|
consumers=[test_data.Persons.ellie],
|
|
recipes=[meal_recipe],
|
|
extra_ingredients=[extra_ingredient]
|
|
)
|
|
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
# Request the meal
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
|
|
# Get initial outstanding requests
|
|
outstanding_before, purchased_before, meal_requests_before, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_before), 2) # Recipe ingredient + extra ingredient
|
|
self.assertEqual(len(meal_requests_before), 1)
|
|
|
|
# Purchase all ingredients
|
|
shopping_items = []
|
|
for item in outstanding_before:
|
|
shopping_items.append(ShoppingListItem(
|
|
ingredient_id=item.ingredient_id,
|
|
person_id=person.id,
|
|
meal_id=meal.id,
|
|
recipe_id=item.recipe_id
|
|
))
|
|
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=person.id,
|
|
items=shopping_items
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Verify meal is unrequested and marked as purchased
|
|
is_requested_after = await shopping_db.is_requested(self.conn, meal)
|
|
self.assertFalse(is_requested_after)
|
|
|
|
found_meal_after = await meals.find_meal_by_id(self.conn, meal.id)
|
|
self.assertIsNotNone(found_meal_after.purchase_date)
|
|
|
|
# Get final state
|
|
outstanding_after, purchased_after, meal_requests_after, _, _, _ = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_after), 0)
|
|
self.assertEqual(len(purchased_after), 0) # No purchased items since meal is complete
|
|
self.assertEqual(len(meal_requests_after), 0)
|
|
|
|
async def test_individual_ingredient_purchase_without_meal(self):
|
|
"""Test purchasing individual ingredients that are not part of a meal"""
|
|
# Create individual ingredients
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
name="Milk",
|
|
line="1L milk",
|
|
unit="L",
|
|
quantity=1.0,
|
|
preparation=""
|
|
)
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
name="Eggs",
|
|
line="12 eggs",
|
|
unit="dozen",
|
|
quantity=1.0,
|
|
preparation=""
|
|
)
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
|
|
|
# Request individual ingredients (not part of any meal)
|
|
person = test_data.Persons.jacob
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient1)
|
|
await shopping_db.request(self.conn, person, ingredient=ingredient2)
|
|
|
|
# Verify both ingredients appear in outstanding requests
|
|
outstanding_before, purchased_before, meal_requests_before, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_before), 2)
|
|
self.assertEqual(len(purchased_before), 0)
|
|
self.assertEqual(len(meal_requests_before), 0) # No meal requests
|
|
|
|
# Verify the ingredients in outstanding requests
|
|
ingredient_names = {ingredients_lookup[item.ingredient_id].name for item in outstanding_before if item.ingredient_id in ingredients_lookup}
|
|
self.assertIn("Milk", ingredient_names)
|
|
self.assertIn("Eggs", ingredient_names)
|
|
|
|
# All should be individual requests (no meal_id)
|
|
for item in outstanding_before:
|
|
self.assertIsNone(item.meal_id)
|
|
self.assertEqual(item.person_id, person.id)
|
|
|
|
# Purchase only one ingredient (Milk)
|
|
milk_item = None
|
|
for item in outstanding_before:
|
|
if item.ingredient_id in ingredients_lookup and ingredients_lookup[item.ingredient_id].name == "Milk":
|
|
milk_item = ShoppingListItem(
|
|
ingredient_id=item.ingredient_id,
|
|
person_id=person.id
|
|
)
|
|
break
|
|
|
|
self.assertIsNotNone(milk_item)
|
|
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=person.id,
|
|
items=[milk_item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Verify only eggs remains in outstanding, milk is purchased
|
|
outstanding_after, purchased_after, meal_requests_after, meals_lookup_after, recipes_lookup_after, ingredients_lookup_after = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_after), 1)
|
|
self.assertEqual(len(purchased_after), 0) # Individual purchases don't appear in purchased list
|
|
self.assertEqual(len(meal_requests_after), 0)
|
|
|
|
# Verify only eggs remains
|
|
self.assertEqual(ingredients_lookup_after[outstanding_after[0].ingredient_id].name, "Eggs")
|
|
self.assertIsNone(outstanding_after[0].meal_id)
|
|
|
|
# Purchase the remaining ingredient (Eggs)
|
|
eggs_item = ShoppingListItem(
|
|
ingredient_id=outstanding_after[0].ingredient_id,
|
|
person_id=person.id
|
|
)
|
|
|
|
shopping_list2 = ShoppingList(
|
|
store_name=StoreEnum.coles,
|
|
purchased_by_id=person.id,
|
|
items=[eggs_item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list2)
|
|
|
|
# Verify no outstanding requests remain
|
|
outstanding_final, purchased_final, meal_requests_final, _, _, _ = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_final), 0)
|
|
self.assertEqual(len(purchased_final), 0)
|
|
self.assertEqual(len(meal_requests_final), 0)
|
|
|
|
async def test_mixed_meal_and_individual_requests(self):
|
|
"""Test a mix of meal requests and individual ingredient requests"""
|
|
# Create a simple recipe and meal
|
|
recipe = recipes.Recipe(
|
|
id=-1,
|
|
name="Simple Pasta",
|
|
link="http://example.com/pasta",
|
|
serves=2,
|
|
created_by_id=test_data.Persons.jacob.id
|
|
)
|
|
await recipes.insert_recipe(self.conn, recipe)
|
|
|
|
# Create recipe ingredient
|
|
pasta_ingredient = ingredients_db.Ingredient(
|
|
name="Pasta",
|
|
line="200g pasta",
|
|
unit="g",
|
|
quantity=200.0,
|
|
preparation="",
|
|
recipe_id=recipe.id
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, pasta_ingredient)
|
|
await recipes.load_recipe_ingredients(self.conn, recipe)
|
|
|
|
# Create meal
|
|
meal_recipe = MealRecipe(
|
|
meal_id=-1,
|
|
recipe_id=recipe.id,
|
|
servings=1.0,
|
|
recipe=recipe
|
|
)
|
|
|
|
meal = meals.Meal(
|
|
id=-1,
|
|
suggested_date=datetime.now(),
|
|
chefs=[test_data.Persons.jacob],
|
|
cleanup=[test_data.Persons.ryan],
|
|
consumers=[test_data.Persons.ellie],
|
|
recipes=[meal_recipe]
|
|
)
|
|
|
|
await meals.insert_meal(self.conn, meal)
|
|
|
|
# Create individual ingredient
|
|
snack_ingredient = ingredients_db.Ingredient(
|
|
name="Chips",
|
|
line="1 bag chips",
|
|
unit="bag",
|
|
quantity=1.0,
|
|
preparation=""
|
|
)
|
|
await ingredients_db.insert_ingredient(self.conn, snack_ingredient)
|
|
|
|
person = test_data.Persons.jacob
|
|
|
|
# Request both meal and individual ingredient
|
|
await shopping_db.request(self.conn, person, meal=meal)
|
|
await shopping_db.request(self.conn, person, ingredient=snack_ingredient)
|
|
|
|
# Verify we have both meal and individual requests
|
|
outstanding_before, purchased_before, meal_requests_before, meals_lookup, recipes_lookup, ingredients_lookup = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_before), 2) # Pasta from meal + Chips individual
|
|
self.assertEqual(len(purchased_before), 0)
|
|
self.assertEqual(len(meal_requests_before), 1) # One meal request
|
|
|
|
# Verify the mix of ingredients
|
|
ingredient_names = {ingredients_lookup[item.ingredient_id].name for item in outstanding_before if item.ingredient_id in ingredients_lookup}
|
|
self.assertIn("Pasta", ingredient_names)
|
|
self.assertIn("Chips", ingredient_names)
|
|
|
|
# Check that pasta is from meal, chips is individual
|
|
pasta_item = None
|
|
chips_item = None
|
|
for item in outstanding_before:
|
|
if item.ingredient_id in ingredients_lookup:
|
|
ingredient_name = ingredients_lookup[item.ingredient_id].name
|
|
if ingredient_name == "Pasta":
|
|
pasta_item = item
|
|
elif ingredient_name == "Chips":
|
|
chips_item = item
|
|
|
|
self.assertIsNotNone(pasta_item)
|
|
self.assertIsNotNone(chips_item)
|
|
self.assertEqual(pasta_item.meal_id, meal.id)
|
|
self.assertIsNone(chips_item.meal_id)
|
|
|
|
# Purchase the individual ingredient (Chips)
|
|
chips_shopping_item = ShoppingListItem(
|
|
ingredient_id=chips_item.ingredient_id,
|
|
person_id=person.id
|
|
)
|
|
|
|
shopping_list = ShoppingList(
|
|
store_name=StoreEnum.woolworths,
|
|
purchased_by_id=person.id,
|
|
items=[chips_shopping_item]
|
|
)
|
|
|
|
await shopping_db.purchase(self.conn, shopping_list)
|
|
|
|
# Verify only meal ingredient remains
|
|
outstanding_after, purchased_after, meal_requests_after, meals_lookup_after, recipes_lookup_after, ingredients_lookup_after = await shopping.get_outstanding_requests(self.conn)
|
|
self.assertEqual(len(outstanding_after), 1) # Only pasta from meal
|
|
self.assertEqual(len(purchased_after), 0)
|
|
self.assertEqual(len(meal_requests_after), 1) # Meal still requested
|
|
|
|
self.assertEqual(ingredients_lookup_after[outstanding_after[0].ingredient_id].name, "Pasta")
|
|
self.assertEqual(outstanding_after[0].meal_id, meal.id)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|