2025-07-30 08:55:51 +00:00
|
|
|
import unittest
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
import tests.test_data as test_data
|
|
|
|
|
import importlib
|
|
|
|
|
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
def reload_test_data():
|
|
|
|
|
global test_data
|
|
|
|
|
test_data = importlib.reload(test_data)
|
|
|
|
|
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
from db import connect, create
|
|
|
|
|
import ingredients
|
|
|
|
|
import ingredients.db as ingredients_db
|
|
|
|
|
import products.db as products_db
|
|
|
|
|
import units
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestIngredient(unittest.IsolatedAsyncioTestCase):
|
|
|
|
|
async def asyncSetUp(self):
|
2025-10-18 03:26:42 +00:00
|
|
|
self.conn = await connect(":memory:")
|
2025-07-30 08:55:51 +00:00
|
|
|
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_ingredient_creation(self):
|
|
|
|
|
"""Test basic ingredient creation"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Broccoli",
|
|
|
|
|
line="500g fresh broccoli",
|
|
|
|
|
unit="g",
|
|
|
|
|
quantity=500.0,
|
2025-10-18 03:26:42 +00:00
|
|
|
preparation="chopped",
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(ingredient.name, "Broccoli")
|
|
|
|
|
self.assertEqual(ingredient.line, "500g fresh broccoli")
|
|
|
|
|
self.assertEqual(ingredient.unit, "g")
|
|
|
|
|
self.assertEqual(ingredient.quantity, 500.0)
|
|
|
|
|
self.assertEqual(ingredient.preparation, "chopped")
|
|
|
|
|
|
|
|
|
|
async def test_insert_ingredient(self):
|
|
|
|
|
"""Test inserting an ingredient into the database"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
2025-10-18 03:26:42 +00:00
|
|
|
name="Garlic", line="2 cloves garlic", unit="Items", quantity=2.0, preparation="minced"
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertGreater(ingredient.id, 0)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify it was inserted correctly
|
|
|
|
|
found_ingredient = await ingredients_db.find_ingredient_by_id(self.conn, ingredient.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient)
|
|
|
|
|
self.assertEqual(found_ingredient.name, "Garlic")
|
|
|
|
|
self.assertEqual(found_ingredient.quantity, 2.0)
|
|
|
|
|
|
|
|
|
|
async def test_insert_ingredient_with_product(self):
|
|
|
|
|
"""Test inserting an ingredient with an associated product"""
|
|
|
|
|
# First create and insert a product
|
|
|
|
|
product = test_data.Products.broccoli
|
|
|
|
|
await products_db.insert_product(self.conn, product, {})
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Fresh Broccoli",
|
|
|
|
|
line="1 piece fresh broccoli",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
|
|
|
|
product_id=product.id,
|
2025-10-18 03:26:42 +00:00
|
|
|
product=product,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify the ingredient was inserted with the product reference
|
|
|
|
|
found_ingredient = await ingredients_db.find_ingredient_by_id(self.conn, ingredient.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient)
|
|
|
|
|
self.assertEqual(found_ingredient.product_id, product.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient.product)
|
|
|
|
|
self.assertEqual(found_ingredient.product.name, product.name)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredient_by_id_not_found(self):
|
|
|
|
|
"""Test finding a non-existent ingredient returns None"""
|
|
|
|
|
result = await ingredients_db.find_ingredient_by_id(self.conn, 999)
|
|
|
|
|
self.assertIsNone(result)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredients_by_recipe_id(self):
|
|
|
|
|
"""Test finding ingredients by recipe ID"""
|
|
|
|
|
# Create ingredients with the same recipe_id
|
|
|
|
|
recipe_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
|
|
|
name="Flour",
|
|
|
|
|
line="2 cups flour",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
recipe_id=recipe_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
|
|
|
name="Sugar",
|
|
|
|
|
line="1 cup sugar",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
recipe_id=recipe_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Find ingredients by recipe ID
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ingredient in ingredients_db.find_ingredients_by_recipe_id(self.conn, recipe_id):
|
|
|
|
|
ingredients_list.append(ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(len(ingredients_list), 2)
|
|
|
|
|
names = [ing.name for ing in ingredients_list]
|
|
|
|
|
self.assertIn("Flour", names)
|
|
|
|
|
self.assertIn("Sugar", names)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredients_by_meal_id(self):
|
|
|
|
|
"""Test finding ingredients by meal ID"""
|
|
|
|
|
# Create ingredients with the same meal_id
|
|
|
|
|
meal_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
|
|
|
name="Chicken",
|
|
|
|
|
line="1 lb chicken breast",
|
|
|
|
|
unit="lb",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="diced",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
|
|
|
name="Rice",
|
|
|
|
|
line="2 cups rice",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Find ingredients by meal ID
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ingredient in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(len(ingredients_list), 2)
|
|
|
|
|
names = [ing.name for ing in ingredients_list]
|
|
|
|
|
self.assertIn("Chicken", names)
|
|
|
|
|
self.assertIn("Rice", names)
|
|
|
|
|
|
|
|
|
|
async def test_delete_ingredients_by_meal_id(self):
|
|
|
|
|
"""Test deleting ingredients by meal ID"""
|
|
|
|
|
meal_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Tomato",
|
|
|
|
|
line="2 tomatoes",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="sliced",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify ingredient exists
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ing in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ing)
|
|
|
|
|
self.assertEqual(len(ingredients_list), 1)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Delete ingredients by meal ID
|
|
|
|
|
await ingredients_db.delete_ingredients_by_meal_id(self.conn, meal_id)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify ingredients are deleted
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ing in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ing)
|
|
|
|
|
self.assertEqual(len(ingredients_list), 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
import tests.test_data as test_data
|
|
|
|
|
import importlib
|
|
|
|
|
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
def reload_test_data():
|
|
|
|
|
global test_data
|
|
|
|
|
test_data = importlib.reload(test_data)
|
|
|
|
|
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
from db import connect, create
|
|
|
|
|
import ingredients
|
|
|
|
|
import ingredients.db as ingredients_db
|
|
|
|
|
import products.db as products_db
|
|
|
|
|
import units
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestIngredient(unittest.IsolatedAsyncioTestCase):
|
|
|
|
|
async def asyncSetUp(self):
|
2025-10-18 03:26:42 +00:00
|
|
|
self.conn = await connect(":memory:")
|
2025-07-30 08:55:51 +00:00
|
|
|
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_ingredient_creation(self):
|
|
|
|
|
"""Test basic ingredient creation"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Broccoli",
|
|
|
|
|
line="500g fresh broccoli",
|
|
|
|
|
unit="g",
|
|
|
|
|
quantity=500.0,
|
2025-10-18 03:26:42 +00:00
|
|
|
preparation="chopped",
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(ingredient.name, "Broccoli")
|
|
|
|
|
self.assertEqual(ingredient.line, "500g fresh broccoli")
|
|
|
|
|
self.assertEqual(ingredient.unit, "g")
|
|
|
|
|
self.assertEqual(ingredient.quantity, 500.0)
|
|
|
|
|
self.assertEqual(ingredient.preparation, "chopped")
|
|
|
|
|
|
|
|
|
|
async def test_insert_ingredient(self):
|
|
|
|
|
"""Test inserting an ingredient into the database"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
2025-10-18 03:26:42 +00:00
|
|
|
name="Garlic", line="2 cloves garlic", unit="Items", quantity=2.0, preparation="minced"
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertGreater(ingredient.id, 0)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify it was inserted correctly
|
|
|
|
|
found_ingredient = await ingredients_db.find_ingredient_by_id(self.conn, ingredient.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient)
|
|
|
|
|
self.assertEqual(found_ingredient.name, "Garlic")
|
|
|
|
|
self.assertEqual(found_ingredient.quantity, 2.0)
|
|
|
|
|
|
|
|
|
|
async def test_insert_ingredient_with_product(self):
|
|
|
|
|
"""Test inserting an ingredient with an associated product"""
|
|
|
|
|
# First create and insert a product
|
|
|
|
|
product = test_data.Products.broccoli
|
|
|
|
|
await products_db.insert_product(self.conn, product, {})
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Fresh Broccoli",
|
|
|
|
|
line="1 piece fresh broccoli",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
|
|
|
|
product_id=product.id,
|
2025-10-18 03:26:42 +00:00
|
|
|
product=product,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify the ingredient was inserted with the product reference
|
|
|
|
|
found_ingredient = await ingredients_db.find_ingredient_by_id(self.conn, ingredient.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient)
|
|
|
|
|
self.assertEqual(found_ingredient.product_id, product.id)
|
|
|
|
|
self.assertIsNotNone(found_ingredient.product)
|
|
|
|
|
self.assertEqual(found_ingredient.product.name, product.name)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredient_by_id_not_found(self):
|
|
|
|
|
"""Test finding a non-existent ingredient returns None"""
|
|
|
|
|
result = await ingredients_db.find_ingredient_by_id(self.conn, 999)
|
|
|
|
|
self.assertIsNone(result)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredients_by_recipe_id(self):
|
|
|
|
|
"""Test finding ingredients by recipe ID"""
|
|
|
|
|
# Create ingredients with the same recipe_id
|
|
|
|
|
recipe_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
|
|
|
name="Flour",
|
|
|
|
|
line="2 cups flour",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
recipe_id=recipe_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
|
|
|
name="Sugar",
|
|
|
|
|
line="1 cup sugar",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
recipe_id=recipe_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Find ingredients by recipe ID
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ingredient in ingredients_db.find_ingredients_by_recipe_id(self.conn, recipe_id):
|
|
|
|
|
ingredients_list.append(ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(len(ingredients_list), 2)
|
|
|
|
|
names = [ing.name for ing in ingredients_list]
|
|
|
|
|
self.assertIn("Flour", names)
|
|
|
|
|
self.assertIn("Sugar", names)
|
|
|
|
|
|
|
|
|
|
async def test_find_ingredients_by_meal_id(self):
|
|
|
|
|
"""Test finding ingredients by meal ID"""
|
|
|
|
|
# Create ingredients with the same meal_id
|
|
|
|
|
meal_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient1 = ingredients_db.Ingredient(
|
|
|
|
|
name="Chicken",
|
|
|
|
|
line="1 lb chicken breast",
|
|
|
|
|
unit="lb",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="diced",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
|
|
|
name="Rice",
|
|
|
|
|
line="2 cups rice",
|
|
|
|
|
unit="cups",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient1)
|
|
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient2)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Find ingredients by meal ID
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ingredient in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(len(ingredients_list), 2)
|
|
|
|
|
names = [ing.name for ing in ingredients_list]
|
|
|
|
|
self.assertIn("Chicken", names)
|
|
|
|
|
self.assertIn("Rice", names)
|
|
|
|
|
|
|
|
|
|
async def test_delete_ingredients_by_meal_id(self):
|
|
|
|
|
"""Test deleting ingredients by meal ID"""
|
|
|
|
|
meal_id = 1
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Tomato",
|
|
|
|
|
line="2 tomatoes",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=2.0,
|
|
|
|
|
preparation="sliced",
|
2025-10-18 03:26:42 +00:00
|
|
|
meal_id=meal_id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify ingredient exists
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ing in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ing)
|
|
|
|
|
self.assertEqual(len(ingredients_list), 1)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Delete ingredients by meal ID
|
|
|
|
|
await ingredients_db.delete_ingredients_by_meal_id(self.conn, meal_id)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Verify ingredients are deleted
|
|
|
|
|
ingredients_list = []
|
|
|
|
|
async for ing in ingredients_db.find_ingredients_by_meal_id(self.conn, meal_id):
|
|
|
|
|
ingredients_list.append(ing)
|
|
|
|
|
self.assertEqual(len(ingredients_list), 0)
|
|
|
|
|
|
|
|
|
|
async def test_ingredient_with_negative_product_id(self):
|
|
|
|
|
"""Test that negative product_id is converted to None during insertion"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="Test Ingredient",
|
|
|
|
|
line="1 test ingredient",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
2025-10-18 03:26:42 +00:00
|
|
|
product_id=-1,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
await ingredients_db.insert_ingredient(self.conn, ingredient)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
found_ingredient = await ingredients_db.find_ingredient_by_id(self.conn, ingredient.id)
|
|
|
|
|
self.assertIsNone(found_ingredient.product_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestIngredientParsing(unittest.IsolatedAsyncioTestCase):
|
|
|
|
|
async def asyncSetUp(self):
|
2025-10-18 03:26:42 +00:00
|
|
|
self.conn = await connect(":memory:")
|
2025-07-30 08:55:51 +00:00
|
|
|
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_parse_ingredient_from_link_invalid_format(self):
|
|
|
|
|
"""Test parsing ingredient from invalid link format returns None"""
|
|
|
|
|
invalid_links = [
|
|
|
|
|
"invalid link format",
|
|
|
|
|
"just a url https://example.com",
|
|
|
|
|
"no quantity https://example.com",
|
|
|
|
|
"",
|
2025-10-18 03:26:42 +00:00
|
|
|
"abc https://example.com",
|
2025-07-30 08:55:51 +00:00
|
|
|
]
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
for invalid_link in invalid_links:
|
|
|
|
|
result = await ingredients.parse_ingredient_from_link(self.conn, invalid_link)
|
|
|
|
|
self.assertIsNone(result, f"Should return None for: {invalid_link}")
|
|
|
|
|
|
|
|
|
|
async def test_parse_ingredient_from_link_valid_format_no_quantity(self):
|
|
|
|
|
"""Test parsing ingredient from valid link format without explicit quantity"""
|
|
|
|
|
link = "https://www.woolworths.com.au/shop/productdetails/134681/fresh-broccoli"
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
result = await ingredients.parse_ingredient_from_link(self.conn, link)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# The scraper actually works for this URL, so we should get a result
|
|
|
|
|
self.assertIsNotNone(result)
|
|
|
|
|
self.assertEqual(result.quantity, 1.0) # Default quantity when none specified
|
|
|
|
|
self.assertEqual(result.unit, units.ITEMS.name)
|
|
|
|
|
self.assertIsInstance(result, ingredients_db.Ingredient)
|
|
|
|
|
|
|
|
|
|
async def test_parse_ingredient_from_link_regex_parsing(self):
|
|
|
|
|
"""Test that the regex correctly parses quantity and URL from valid links"""
|
|
|
|
|
import re
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Test the regex pattern used in parse_ingredient_from_link
|
|
|
|
|
test_cases = [
|
|
|
|
|
("2 https://example.com", "2", "https://example.com"),
|
2025-10-18 03:26:42 +00:00
|
|
|
(
|
|
|
|
|
"10 https://www.woolworths.com.au/product",
|
|
|
|
|
"10",
|
|
|
|
|
"https://www.woolworths.com.au/product",
|
|
|
|
|
),
|
2025-07-30 08:55:51 +00:00
|
|
|
("https://example.com", None, "https://example.com"),
|
2025-10-18 03:26:42 +00:00
|
|
|
("1 https://test.com", "1", "https://test.com"),
|
2025-07-30 08:55:51 +00:00
|
|
|
]
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
for link, expected_qty, expected_url in test_cases:
|
2025-10-18 03:26:42 +00:00
|
|
|
match = re.match(r"^(\d+)?\s*(http.*)$", link)
|
2025-07-30 08:55:51 +00:00
|
|
|
if match:
|
|
|
|
|
quantity = int(match.group(1)) if match.group(1) else 1
|
|
|
|
|
url = match.group(2)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
if expected_qty:
|
|
|
|
|
self.assertEqual(quantity, int(expected_qty))
|
|
|
|
|
else:
|
|
|
|
|
self.assertEqual(quantity, 1) # Default quantity
|
|
|
|
|
self.assertEqual(url, expected_url)
|
|
|
|
|
|
|
|
|
|
async def test_parse_ingredient_from_nlp_simple_cases(self):
|
|
|
|
|
"""Test parsing simple ingredient cases that don't require external dependencies"""
|
|
|
|
|
# Test the basic structure without relying on ingredient_parser
|
|
|
|
|
# Since ingredient_parser is an external dependency, we'll test what we can
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# We can test that the function exists and handles basic error cases
|
|
|
|
|
try:
|
|
|
|
|
result = ingredients.parse_ingredient_from_nlp("2 cups flour")
|
|
|
|
|
# The function may fail due to missing ingredient_parser, but it should not crash
|
|
|
|
|
# If it works, result should be an Ingredient object
|
|
|
|
|
if result is not None:
|
|
|
|
|
self.assertIsInstance(result, ingredients_db.Ingredient)
|
|
|
|
|
except ImportError:
|
|
|
|
|
# If ingredient_parser is not available, that's expected
|
|
|
|
|
self.skipTest("ingredient_parser not available")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# Other exceptions should not occur in normal operation
|
|
|
|
|
self.fail(f"Unexpected exception: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestIngredientMatching(unittest.IsolatedAsyncioTestCase):
|
|
|
|
|
async def asyncSetUp(self):
|
2025-10-18 03:26:42 +00:00
|
|
|
self.conn = await connect(":memory:")
|
2025-07-30 08:55:51 +00:00
|
|
|
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_match_existing_products_with_real_data(self):
|
|
|
|
|
"""Test matching ingredients to existing products using real operations"""
|
|
|
|
|
# Setup: Create and insert a product with tags
|
|
|
|
|
product = test_data.Products.broccoli
|
|
|
|
|
await products_db.insert_product(self.conn, product, {})
|
|
|
|
|
await products_db.add_tag(self.conn, product, "broccoli")
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Create ingredients without products
|
|
|
|
|
ingredient1 = ingredients_db.Ingredient(
|
2025-10-18 03:26:42 +00:00
|
|
|
name="broccoli", line="1 piece broccoli", unit="Items", quantity=1.0, preparation=""
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient2 = ingredients_db.Ingredient(
|
|
|
|
|
name="unknown vegetable",
|
|
|
|
|
line="1 piece unknown vegetable",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=1.0,
|
2025-10-18 03:26:42 +00:00
|
|
|
preparation="",
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredients_list = [ingredient1, ingredient2]
|
|
|
|
|
result = await ingredients.match_existing_products(self.conn, ingredients_list)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Check that first ingredient got matched
|
|
|
|
|
self.assertEqual(result[0].product_id, product.id)
|
|
|
|
|
self.assertIsNotNone(result[0].product)
|
|
|
|
|
self.assertEqual(result[0].product.name, product.name)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Check that second ingredient remained unmatched
|
|
|
|
|
self.assertIsNone(result[1].product)
|
|
|
|
|
|
|
|
|
|
async def test_match_existing_products_already_has_product(self):
|
|
|
|
|
"""Test that ingredients with existing products are not re-matched"""
|
|
|
|
|
product = test_data.Products.broccoli
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredient = ingredients_db.Ingredient(
|
|
|
|
|
name="broccoli",
|
|
|
|
|
line="1 piece broccoli",
|
|
|
|
|
unit="Items",
|
|
|
|
|
quantity=1.0,
|
|
|
|
|
preparation="",
|
|
|
|
|
product=product,
|
2025-10-18 03:26:42 +00:00
|
|
|
product_id=product.id,
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
ingredients_list = [ingredient]
|
|
|
|
|
result = await ingredients.match_existing_products(self.conn, ingredients_list)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
# Should remain unchanged
|
|
|
|
|
self.assertEqual(result[0].product_id, product.id)
|
|
|
|
|
self.assertEqual(result[0].product, product)
|
|
|
|
|
|
|
|
|
|
async def test_match_existing_products_empty_list(self):
|
|
|
|
|
"""Test matching empty ingredients list"""
|
|
|
|
|
result = await ingredients.match_existing_products(self.conn, [])
|
|
|
|
|
self.assertEqual(result, [])
|
|
|
|
|
|
|
|
|
|
async def test_ingredient_keys_constant(self):
|
|
|
|
|
"""Test that the KEYS constant contains expected fields"""
|
2025-10-18 03:26:42 +00:00
|
|
|
expected_keys = [
|
|
|
|
|
"id",
|
|
|
|
|
"name",
|
|
|
|
|
"line",
|
|
|
|
|
"preparation",
|
|
|
|
|
"unit",
|
|
|
|
|
"quantity",
|
|
|
|
|
"product_id",
|
|
|
|
|
"recipe_id",
|
|
|
|
|
"meal_id",
|
|
|
|
|
]
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(ingredients_db.Ingredient.KEYS, expected_keys)
|
|
|
|
|
|
|
|
|
|
async def test_ingredient_default_values(self):
|
|
|
|
|
"""Test ingredient default values"""
|
|
|
|
|
ingredient = ingredients_db.Ingredient(
|
2025-10-18 03:26:42 +00:00
|
|
|
name="Test", line="Test line", unit="Items", quantity=1.0, preparation=""
|
2025-07-30 08:55:51 +00:00
|
|
|
)
|
2025-10-18 03:26:42 +00:00
|
|
|
|
2025-07-30 08:55:51 +00:00
|
|
|
self.assertEqual(ingredient.id, -1)
|
|
|
|
|
self.assertIsNone(ingredient.product_id)
|
|
|
|
|
self.assertIsNone(ingredient.recipe_id)
|
|
|
|
|
self.assertIsNone(ingredient.meal_id)
|
|
|
|
|
self.assertIsNone(ingredient.product)
|
|
|
|
|
|
|
|
|
|
|
2025-10-18 03:26:42 +00:00
|
|
|
if __name__ == "__main__":
|
2025-07-30 08:55:51 +00:00
|
|
|
unittest.main()
|