mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-04-05 12:35:35 -04:00
feat: Unit standardization / conversion (#7121)
This commit is contained in:
@@ -4,6 +4,9 @@ CWD = Path(__file__).parent
|
||||
|
||||
locale_dir = CWD / "locale"
|
||||
|
||||
backup_version_1d9a002d7234_1 = CWD / "backups/backup-version-1d9a002d7234-1.zip"
|
||||
"""1d9a002d7234: add referenced_recipe to ingredients"""
|
||||
|
||||
backup_version_44e8d670719d_1 = CWD / "backups/backup-version-44e8d670719d-1.zip"
|
||||
"""44e8d670719d: add extras to shopping lists, list items, and ingredient foods"""
|
||||
|
||||
|
||||
BIN
tests/data/backups/backup-version-1d9a002d7234-1.zip
Normal file
BIN
tests/data/backups/backup-version-1d9a002d7234-1.zip
Normal file
Binary file not shown.
@@ -15,14 +15,12 @@ def test_seed_foods(api_client: TestClient, unique_user: TestUser):
|
||||
CREATED_FOODS = 2687
|
||||
database = unique_user.repos
|
||||
|
||||
# Check that the foods was created
|
||||
foods = database.ingredient_foods.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(foods) == 0
|
||||
|
||||
resp = api_client.post(api_routes.groups_seeders_foods, json={"locale": "en-US"}, headers=unique_user.token)
|
||||
assert resp.status_code == 200
|
||||
|
||||
# Check that the foods was created
|
||||
foods = database.ingredient_foods.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(foods) == CREATED_FOODS
|
||||
|
||||
@@ -31,29 +29,37 @@ def test_seed_units(api_client: TestClient, unique_user: TestUser):
|
||||
CREATED_UNITS = 24
|
||||
database = unique_user.repos
|
||||
|
||||
# Check that the foods was created
|
||||
units = database.ingredient_units.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(units) == 0
|
||||
|
||||
resp = api_client.post(api_routes.groups_seeders_units, json={"locale": "en-US"}, headers=unique_user.token)
|
||||
assert resp.status_code == 200
|
||||
|
||||
# Check that the foods was created
|
||||
units = database.ingredient_units.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(units) == CREATED_UNITS
|
||||
|
||||
# Check that the "pint" unit was created and includes standardized data
|
||||
pint_found = False
|
||||
for unit in units:
|
||||
if unit.name != "pint":
|
||||
continue
|
||||
|
||||
pint_found = True
|
||||
assert unit.standard_quantity == 2
|
||||
assert unit.standard_unit == "cup"
|
||||
|
||||
assert pint_found
|
||||
|
||||
|
||||
def test_seed_labels(api_client: TestClient, unique_user: TestUser):
|
||||
CREATED_LABELS = 32
|
||||
database = unique_user.repos
|
||||
|
||||
# Check that the foods was created
|
||||
labels = database.group_multi_purpose_labels.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(labels) == 0
|
||||
|
||||
resp = api_client.post(api_routes.groups_seeders_labels, json={"locale": "en-US"}, headers=unique_user.token)
|
||||
assert resp.status_code == 200
|
||||
|
||||
# Check that the foods was created
|
||||
labels = database.group_multi_purpose_labels.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
assert len(labels) == CREATED_LABELS
|
||||
|
||||
@@ -7,7 +7,7 @@ from fastapi.testclient import TestClient
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.schema.household.group_shopping_list import ShoppingListItemOut, ShoppingListOut
|
||||
from mealie.schema.recipe.recipe_ingredient import SaveIngredientFood
|
||||
from mealie.schema.recipe.recipe_ingredient import IngredientUnit, SaveIngredientFood
|
||||
from tests import utils
|
||||
from tests.utils import api_routes
|
||||
from tests.utils.factories import random_int, random_string
|
||||
@@ -641,6 +641,96 @@ def test_shopping_list_items_with_zero_quantity(
|
||||
assert len(as_json["listItems"]) == len(normal_items + zero_qty_items) - 1
|
||||
|
||||
|
||||
def test_shopping_list_merge_standard_unit(
|
||||
api_client: TestClient, unique_user: TestUser, shopping_list: ShoppingListOut
|
||||
):
|
||||
unit_1_cup_data = {"name": random_string(), "standardQuantity": 1, "standardUnit": "cup"}
|
||||
unit_2_cup_data = {"name": random_string(), "standardQuantity": 2, "standardUnit": "cup"}
|
||||
unit_1_out = api_client.post(api_routes.units, json=unit_1_cup_data, headers=unique_user.token)
|
||||
unit_2_out = api_client.post(api_routes.units, json=unit_2_cup_data, headers=unique_user.token)
|
||||
|
||||
unit_1 = IngredientUnit.model_validate(unit_1_out.json())
|
||||
unit_2 = IngredientUnit.model_validate(unit_2_out.json())
|
||||
|
||||
list_item_1_data = create_item(shopping_list.id, unit_id=str(unit_1.id), note="mealie-food")
|
||||
list_item_2_data = create_item(shopping_list.id, unit_id=str(unit_2.id), note="mealie-food")
|
||||
response = api_client.post(
|
||||
api_routes.households_shopping_items_create_bulk,
|
||||
json=[list_item_1_data, list_item_2_data],
|
||||
headers=unique_user.token,
|
||||
)
|
||||
|
||||
as_json = utils.assert_deserialize(response, 201)
|
||||
assert len(as_json["createdItems"]) == 1
|
||||
|
||||
item_out = as_json["createdItems"][0]
|
||||
|
||||
# should use larger "2 cup" unit (a la "pint")
|
||||
assert item_out["unitId"] == str(unit_2.id)
|
||||
# calculate quantity by summing base "cup" amount and dividing by 2 (a la pints)
|
||||
assert item_out["quantity"] == (list_item_1_data["quantity"] + (list_item_2_data["quantity"] * 2)) / 2
|
||||
|
||||
|
||||
def test_shopping_list_merge_standard_unit_different_foods(
|
||||
api_client: TestClient, unique_user: TestUser, shopping_list: ShoppingListOut
|
||||
):
|
||||
unit_1_cup_data = {"name": random_string(), "standardQuantity": 1, "standardUnit": "cup"}
|
||||
unit_2_cup_data = {"name": random_string(), "standardQuantity": 2, "standardUnit": "cup"}
|
||||
unit_1_out = api_client.post(api_routes.units, json=unit_1_cup_data, headers=unique_user.token)
|
||||
unit_2_out = api_client.post(api_routes.units, json=unit_2_cup_data, headers=unique_user.token)
|
||||
|
||||
unit_1 = IngredientUnit.model_validate(unit_1_out.json())
|
||||
unit_2 = IngredientUnit.model_validate(unit_2_out.json())
|
||||
|
||||
list_item_1_data = create_item(shopping_list.id, unit_id=str(unit_1.id), note="mealie-food-1")
|
||||
list_item_2_data = create_item(shopping_list.id, unit_id=str(unit_2.id), note="mealie-food-2")
|
||||
response = api_client.post(
|
||||
api_routes.households_shopping_items_create_bulk,
|
||||
json=[list_item_1_data, list_item_2_data],
|
||||
headers=unique_user.token,
|
||||
)
|
||||
|
||||
as_json = utils.assert_deserialize(response, 201)
|
||||
assert len(as_json["createdItems"]) == 2
|
||||
for in_data, out_data in zip(
|
||||
[list_item_1_data, list_item_2_data], [as_json["createdItems"][0], as_json["createdItems"][1]], strict=True
|
||||
):
|
||||
assert in_data["quantity"] == out_data["quantity"]
|
||||
assert out_data["unit"]
|
||||
assert in_data["unit_id"] == out_data["unit"]["id"]
|
||||
assert in_data["note"] == out_data["note"]
|
||||
|
||||
|
||||
def test_shopping_list_merge_standard_unit_incompatible_units(
|
||||
api_client: TestClient, unique_user: TestUser, shopping_list: ShoppingListOut
|
||||
):
|
||||
unit_1_data = {"name": random_string(), "standardQuantity": 1, "standardUnit": "cup"}
|
||||
unit_2_data = {"name": random_string(), "standardQuantity": 2, "standardUnit": "gram"}
|
||||
unit_1_out = api_client.post(api_routes.units, json=unit_1_data, headers=unique_user.token)
|
||||
unit_2_out = api_client.post(api_routes.units, json=unit_2_data, headers=unique_user.token)
|
||||
|
||||
unit_1 = IngredientUnit.model_validate(unit_1_out.json())
|
||||
unit_2 = IngredientUnit.model_validate(unit_2_out.json())
|
||||
|
||||
list_item_1_data = create_item(shopping_list.id, unit_id=str(unit_1.id), note="mealie-food")
|
||||
list_item_2_data = create_item(shopping_list.id, unit_id=str(unit_2.id), note="mealie-food")
|
||||
response = api_client.post(
|
||||
api_routes.households_shopping_items_create_bulk,
|
||||
json=[list_item_1_data, list_item_2_data],
|
||||
headers=unique_user.token,
|
||||
)
|
||||
|
||||
as_json = utils.assert_deserialize(response, 201)
|
||||
assert len(as_json["createdItems"]) == 2
|
||||
for in_data, out_data in zip(
|
||||
[list_item_1_data, list_item_2_data], [as_json["createdItems"][0], as_json["createdItems"][1]], strict=True
|
||||
):
|
||||
assert in_data["quantity"] == out_data["quantity"]
|
||||
assert out_data["unit"]
|
||||
assert in_data["unit_id"] == out_data["unit"]["id"]
|
||||
assert in_data["note"] == out_data["note"]
|
||||
|
||||
|
||||
def test_shopping_list_item_extras(
|
||||
api_client: TestClient, unique_user: TestUser, shopping_list: ShoppingListOut
|
||||
) -> None:
|
||||
|
||||
309
tests/unit_tests/ingredient_parser/test_unit_utils.py
Normal file
309
tests/unit_tests/ingredient_parser/test_unit_utils.py
Normal file
@@ -0,0 +1,309 @@
|
||||
import pint
|
||||
import pytest
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import CreateIngredientUnit
|
||||
from mealie.services.parser_services.parser_utils import UnitConverter, UnitNotFound, merge_quantity_and_unit
|
||||
from tests.utils import random_string
|
||||
|
||||
|
||||
def test_uc_parse_string():
|
||||
uc = UnitConverter()
|
||||
parsed = uc.parse("cup")
|
||||
|
||||
assert isinstance(parsed, pint.Unit)
|
||||
assert (str(parsed)) == "cup"
|
||||
|
||||
|
||||
def test_uc_parse_unit():
|
||||
uc = UnitConverter()
|
||||
parsed = uc.parse(uc.parse("cup"))
|
||||
|
||||
assert isinstance(parsed, pint.Unit)
|
||||
assert (str(parsed)) == "cup"
|
||||
|
||||
|
||||
def test_uc_parse_invalid():
|
||||
uc = UnitConverter()
|
||||
input_str = random_string()
|
||||
parsed = uc.parse(input_str)
|
||||
|
||||
assert not isinstance(parsed, pint.Unit)
|
||||
assert parsed == input_str
|
||||
|
||||
|
||||
def test_uc_parse_invalid_strict():
|
||||
uc = UnitConverter()
|
||||
input_str = random_string()
|
||||
|
||||
with pytest.raises(UnitNotFound):
|
||||
uc.parse(input_str, strict=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pre_parse_1", [True, False])
|
||||
@pytest.mark.parametrize("pre_parse_2", [True, False])
|
||||
def test_can_convert(pre_parse_1: bool, pre_parse_2: bool):
|
||||
unit_1 = "cup"
|
||||
unit_2 = "pint"
|
||||
|
||||
uc = UnitConverter()
|
||||
if pre_parse_1:
|
||||
unit_1 = uc.parse(unit_1)
|
||||
if pre_parse_2:
|
||||
unit_2 = uc.parse(unit_2)
|
||||
|
||||
assert uc.can_convert(unit_1, unit_2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pre_parse_1", [True, False])
|
||||
@pytest.mark.parametrize("pre_parse_2", [True, False])
|
||||
def test_cannot_convert(pre_parse_1: bool, pre_parse_2: bool):
|
||||
unit_1 = "cup"
|
||||
unit_2 = "pound"
|
||||
|
||||
uc = UnitConverter()
|
||||
if pre_parse_1:
|
||||
unit_1 = uc.parse(unit_1)
|
||||
if pre_parse_2:
|
||||
unit_2 = uc.parse(unit_2)
|
||||
|
||||
assert not uc.can_convert(unit_1, unit_2)
|
||||
|
||||
|
||||
def test_cannot_convert_invalid_unit():
|
||||
uc = UnitConverter()
|
||||
assert not uc.can_convert("cup", random_string())
|
||||
assert not uc.can_convert(random_string(), "cup")
|
||||
|
||||
|
||||
def test_can_convert_same_unit():
|
||||
uc = UnitConverter()
|
||||
assert uc.can_convert("cup", "cup")
|
||||
|
||||
|
||||
def test_can_convert_volume_ounce():
|
||||
uc = UnitConverter()
|
||||
assert uc.can_convert("ounce", "cup")
|
||||
assert uc.can_convert("cup", "ounce")
|
||||
|
||||
|
||||
def test_convert_simple():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.convert(1, "cup", "pint")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "pint"
|
||||
assert quantity == 1 / 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pre_parse_1", [True, False])
|
||||
@pytest.mark.parametrize("pre_parse_2", [True, False])
|
||||
def test_convert_pre_parsed(pre_parse_1: bool, pre_parse_2: bool):
|
||||
unit_1 = "cup"
|
||||
unit_2 = "pint"
|
||||
|
||||
uc = UnitConverter()
|
||||
if pre_parse_1:
|
||||
unit_1 = uc.parse(unit_1)
|
||||
if pre_parse_2:
|
||||
unit_2 = uc.parse(unit_2)
|
||||
|
||||
quantity, unit = uc.convert(1, unit_1, unit_2)
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "pint"
|
||||
assert quantity == 1 / 2
|
||||
|
||||
|
||||
def test_convert_weight():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.convert(16, "ounce", "pound")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "pound"
|
||||
assert quantity == 1
|
||||
|
||||
|
||||
def test_convert_zero_quantity():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.convert(0, "cup", "pint")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert quantity == 0
|
||||
|
||||
|
||||
def test_convert_invalid_unit():
|
||||
uc = UnitConverter()
|
||||
|
||||
with pytest.raises(UnitNotFound):
|
||||
uc.convert(1, "pound", random_string())
|
||||
|
||||
|
||||
def test_convert_incompatible_units():
|
||||
uc = UnitConverter()
|
||||
|
||||
with pytest.raises(pint.errors.DimensionalityError):
|
||||
uc.convert(1, "pound", "cup")
|
||||
|
||||
|
||||
def test_convert_volume_ounce():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.convert(8, "ounce", "cup")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "cup"
|
||||
assert quantity == 1
|
||||
|
||||
|
||||
def test_merge_same_unit():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(1, "cup", 2, "cup")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "cup"
|
||||
assert quantity == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pre_parse_1", [True, False])
|
||||
@pytest.mark.parametrize("pre_parse_2", [True, False])
|
||||
def test_merge_compatible_units(pre_parse_1: bool, pre_parse_2: bool):
|
||||
unit_1 = "cup"
|
||||
unit_2 = "pint"
|
||||
|
||||
uc = UnitConverter()
|
||||
if pre_parse_1:
|
||||
unit_1 = uc.parse(unit_1)
|
||||
if pre_parse_2:
|
||||
unit_2 = uc.parse(unit_2)
|
||||
|
||||
quantity, unit = uc.merge(1, unit_1, 1, unit_2)
|
||||
assert isinstance(unit, pint.Unit)
|
||||
# 1 cup + 1 pint = 1 cup + 2 cups = 3 cups
|
||||
assert quantity == 3
|
||||
|
||||
|
||||
def test_merge_weight_units():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(8, "ounce", 8, "ounce")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "ounce"
|
||||
assert quantity == 16
|
||||
|
||||
|
||||
def test_merge_different_weight_units():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(1, "pound", 8, "ounce")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
# 1 pound + 8 ounces = 16 ounces + 8 ounces = 24 ounces
|
||||
assert str(unit) == "pound"
|
||||
assert quantity == 1.5
|
||||
|
||||
|
||||
def test_merge_zero_quantities():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(0, "cup", 1, "cup")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "cup"
|
||||
assert quantity == 1
|
||||
|
||||
|
||||
def test_merge_invalid_unit():
|
||||
uc = UnitConverter()
|
||||
|
||||
with pytest.raises(UnitNotFound):
|
||||
uc.merge(1, "pound", 1, random_string())
|
||||
|
||||
|
||||
def test_merge_incompatible_units():
|
||||
uc = UnitConverter()
|
||||
|
||||
with pytest.raises(pint.errors.DimensionalityError):
|
||||
uc.merge(1, "pound", 1, "cup")
|
||||
|
||||
|
||||
def test_merge_negative_quantity():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(-1, "cup", 2, "cup")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "cup"
|
||||
assert quantity == 1
|
||||
|
||||
|
||||
def test_merge_volume_ounce():
|
||||
uc = UnitConverter()
|
||||
quantity, unit = uc.merge(4, "ounce", 1, "cup")
|
||||
|
||||
assert isinstance(unit, pint.Unit)
|
||||
assert str(unit) == "fluid_ounce" # converted automatically from ounce
|
||||
assert quantity == 12
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_simple():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
|
||||
quantity, unit = merge_quantity_and_unit(1, unit_1, 2, unit_2)
|
||||
|
||||
assert quantity == 3
|
||||
assert unit.name == "mealie_cup"
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_invalid():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_random", standard_quantity=1, standard_unit=random_string())
|
||||
|
||||
with pytest.raises(UnitNotFound):
|
||||
merge_quantity_and_unit(1, unit_1, 1, unit_2)
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_compatible():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_pint", standard_quantity=1, standard_unit="pint")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
|
||||
quantity, unit = merge_quantity_and_unit(1, unit_1, 1, unit_2)
|
||||
|
||||
# 1 pint + 1 cup = 2 pints + 1 cup = 3 cups, converted to pint = 1.5 pint
|
||||
assert quantity == 1.5
|
||||
assert unit.name == "mealie_pint"
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_selects_larger_unit():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_pint", standard_quantity=1, standard_unit="pint")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
|
||||
quantity, unit = merge_quantity_and_unit(2, unit_1, 4, unit_2)
|
||||
|
||||
# 2 pint + 4 cup = 4 cups + 4 cups = 8 cups, should be returned as pint (larger unit)
|
||||
assert quantity == 4
|
||||
assert unit.name == "mealie_pint"
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_selects_smaller_unit():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_pint", standard_quantity=1, standard_unit="pint")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
|
||||
quantity, unit = merge_quantity_and_unit(0.125, unit_1, 0.5, unit_2)
|
||||
|
||||
# 0.125 pint + 0.5 cup = 0.25 cup + 0.5 cup = 0.75 cup, should be returned as cup (smaller for < 1)
|
||||
assert quantity == 0.75
|
||||
assert unit.name == "mealie_cup"
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_missing_standard_data():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup_no_std", standard_quantity=None, standard_unit=None)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
merge_quantity_and_unit(1, unit_1, 1, unit_2)
|
||||
|
||||
|
||||
def test_merge_quantity_and_unit_volume_ounce():
|
||||
unit_1 = CreateIngredientUnit(name="mealie_oz", standard_quantity=1, standard_unit="ounce")
|
||||
unit_2 = CreateIngredientUnit(name="mealie_cup", standard_quantity=1, standard_unit="cup")
|
||||
|
||||
quantity, unit = merge_quantity_and_unit(8, unit_1, 1, unit_2)
|
||||
|
||||
assert quantity == 2
|
||||
assert unit.name == "mealie_cup"
|
||||
@@ -1,11 +1,26 @@
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.repos.all_repositories import AllRepositories, get_repositories
|
||||
from mealie.schema.recipe.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe_ingredient import RecipeIngredient, SaveIngredientUnit
|
||||
from tests.utils.factories import random_string
|
||||
from mealie.schema.user.user import GroupBase
|
||||
from tests.utils.factories import random_int, random_string
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def unique_local_group_id(unfiltered_database: AllRepositories) -> str:
|
||||
return str(unfiltered_database.groups.create(GroupBase(name=random_string())).id)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def unique_db(session: Session, unique_local_group_id: str) -> AllRepositories:
|
||||
return get_repositories(session, group_id=unique_local_group_id)
|
||||
|
||||
|
||||
def test_unit_merger(unique_user: TestUser):
|
||||
database = unique_user.repos
|
||||
recipe: Recipe | None = None
|
||||
@@ -51,3 +66,79 @@ def test_unit_merger(unique_user: TestUser):
|
||||
|
||||
for ingredient in recipe.recipe_ingredient:
|
||||
assert ingredient.unit.id == unit_1.id # type: ignore
|
||||
|
||||
|
||||
@pytest.mark.parametrize("standard_field", ["name", "plural_name", "abbreviation", "plural_abbreviation"])
|
||||
@pytest.mark.parametrize("use_bulk", [True, False])
|
||||
def test_auto_inject_standardization(unique_db: AllRepositories, standard_field: str, use_bulk: bool):
|
||||
unit_in = SaveIngredientUnit(name=random_string(), group_id=unique_db.group_id).model_dump()
|
||||
unit_in[standard_field] = "gallon"
|
||||
|
||||
if use_bulk:
|
||||
out_many = unique_db.ingredient_units.create_many([unit_in])
|
||||
assert len(out_many) == 1
|
||||
unit_out = out_many[0]
|
||||
else:
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_unit == "cup"
|
||||
assert unit_out.standard_quantity == 16
|
||||
|
||||
|
||||
def test_dont_auto_inject_random(unique_db: AllRepositories):
|
||||
unit_in = SaveIngredientUnit(name=random_string(), group_id=unique_db.group_id)
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_quantity is None
|
||||
assert unit_out.standard_unit is None
|
||||
|
||||
|
||||
def test_auto_inject_other_language(unique_db: AllRepositories):
|
||||
# Inject custom unit map
|
||||
GALLON = random_string()
|
||||
unique_db.ingredient_units._standardized_unit_map = {GALLON: "gallon"}
|
||||
|
||||
# Create unit with translated value
|
||||
unit_in = SaveIngredientUnit(name=GALLON, group_id=unique_db.group_id)
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_unit == "cup"
|
||||
assert unit_out.standard_quantity == 16
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["custom-mealie-unit", "gallon"])
|
||||
def test_user_standardization(unique_db: AllRepositories, name: str):
|
||||
unit_in = SaveIngredientUnit(
|
||||
name=name,
|
||||
group_id=unique_db.group_id,
|
||||
standard_quantity=random_int(1, 10),
|
||||
standard_unit=random_string(),
|
||||
)
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_quantity == unit_in.standard_quantity
|
||||
assert unit_out.standard_unit == unit_in.standard_unit
|
||||
|
||||
|
||||
def test_ignore_incomplete_standardization(unique_db: AllRepositories):
|
||||
unit_in = SaveIngredientUnit(
|
||||
name=random_string(),
|
||||
group_id=unique_db.group_id,
|
||||
standard_quantity=random_int(1, 10),
|
||||
standard_unit=None,
|
||||
)
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_quantity is None
|
||||
assert unit_out.standard_unit is None
|
||||
|
||||
unit_in = SaveIngredientUnit(
|
||||
name=random_string(),
|
||||
group_id=unique_db.group_id,
|
||||
standard_quantity=None,
|
||||
standard_unit=random_string(),
|
||||
)
|
||||
unit_out = unique_db.ingredient_units.create(unit_in)
|
||||
|
||||
assert unit_out.standard_quantity is None
|
||||
assert unit_out.standard_unit is None
|
||||
|
||||
@@ -217,6 +217,22 @@ def _b9e516e2d3b3_add_household_to_recipe_last_made_household_to_foods_and_tools
|
||||
assert not tool.households_with_tool
|
||||
|
||||
|
||||
def _a39c7f1826e3_add_unit_standardization_fields(session: Session):
|
||||
groups = session.query(Group).all()
|
||||
|
||||
for group in groups:
|
||||
# test_data.backup_version_1d9a002d7234_1 has a non-anonymized "pint" unit
|
||||
# and has not yet run the standardization migration.
|
||||
pint_units = (
|
||||
session.query(IngredientUnitModel)
|
||||
.filter(IngredientUnitModel.group_id == group.id, IngredientUnitModel.name == "pint")
|
||||
.all()
|
||||
)
|
||||
for unit in pint_units:
|
||||
assert unit.standard_quantity == 2
|
||||
assert unit.standard_unit == "cup"
|
||||
|
||||
|
||||
def test_database_restore_data():
|
||||
"""
|
||||
This tests real user backups to make sure the data is restored correctly. The data has been anonymized, but
|
||||
@@ -227,6 +243,7 @@ def test_database_restore_data():
|
||||
"""
|
||||
|
||||
backup_paths = [
|
||||
test_data.backup_version_1d9a002d7234_1,
|
||||
test_data.backup_version_44e8d670719d_1,
|
||||
test_data.backup_version_44e8d670719d_2,
|
||||
test_data.backup_version_44e8d670719d_3,
|
||||
@@ -245,6 +262,7 @@ def test_database_restore_data():
|
||||
_d7c6efd2de42_migrate_favorites_and_ratings_to_user_ratings,
|
||||
_86054b40fd06_added_query_filter_string_to_cookbook_and_mealplan,
|
||||
_b9e516e2d3b3_add_household_to_recipe_last_made_household_to_foods_and_tools,
|
||||
_a39c7f1826e3_add_unit_standardization_fields,
|
||||
]
|
||||
|
||||
settings = get_app_settings()
|
||||
|
||||
Reference in New Issue
Block a user