mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-02 20:15:24 +02:00
feat: add support for API extras on shopping lists, shopping list items, and food data (#1619)
* added api extras to other tables genericized api extras model from recipes added extras column to ingredient foods added extras column to shopping lists added extras column to shopping list items * updated alembic version test * made mypy happy * added TODO on test that does nothing * added extras tests for lists, items, and foods * added docs for new extras * modified alembic versions to eliminate branching
This commit is contained in:
parent
db70a210a2
commit
8271c3001e
12 changed files with 300 additions and 33 deletions
|
@ -1,6 +1,7 @@
|
|||
import random
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import UUID4
|
||||
|
||||
|
@ -14,10 +15,12 @@ class Routes:
|
|||
shopping = "/api/groups/shopping"
|
||||
items = shopping + "/items"
|
||||
|
||||
def item(item_id: str) -> str:
|
||||
@staticmethod
|
||||
def item(item_id: str | UUID4) -> str:
|
||||
return f"{Routes.items}/{item_id}"
|
||||
|
||||
def shopping_list(list_id: str) -> str:
|
||||
@staticmethod
|
||||
def shopping_list(list_id: str | UUID4) -> str:
|
||||
return f"{Routes.shopping}/lists/{list_id}"
|
||||
|
||||
|
||||
|
@ -162,9 +165,9 @@ def test_shopping_list_items_update_many_reorder(
|
|||
response = api_client.get(Routes.shopping_list(list_with_items.id), headers=unique_user.token)
|
||||
response_list = utils.assert_derserialize(response, 200)
|
||||
|
||||
for i, item in enumerate(response_list["listItems"]):
|
||||
assert item["position"] == i
|
||||
assert item["id"] == str(list_items[i].id)
|
||||
for i, item_data in enumerate(response_list["listItems"]):
|
||||
assert item_data["position"] == i
|
||||
assert item_data["id"] == str(list_items[i].id)
|
||||
|
||||
|
||||
def test_shopping_list_items_update_many_consolidates_common_items(
|
||||
|
@ -194,6 +197,7 @@ def test_shopping_list_items_update_many_consolidates_common_items(
|
|||
assert response_list["listItems"][0]["note"] == master_note
|
||||
|
||||
|
||||
@pytest.mark.skip("TODO: Implement")
|
||||
def test_shopping_list_items_update_many_remove_recipe_with_other_items(
|
||||
api_client: TestClient,
|
||||
unique_user: TestUser,
|
||||
|
@ -201,3 +205,38 @@ def test_shopping_list_items_update_many_remove_recipe_with_other_items(
|
|||
) -> None:
|
||||
# list_items = list_with_items.list_items
|
||||
pass
|
||||
|
||||
|
||||
def test_shopping_list_item_extras(
|
||||
api_client: TestClient, unique_user: TestUser, shopping_list: ShoppingListOut
|
||||
) -> None:
|
||||
key_str_1 = random_string()
|
||||
val_str_1 = random_string()
|
||||
|
||||
key_str_2 = random_string()
|
||||
val_str_2 = random_string()
|
||||
|
||||
# create an item with extras
|
||||
new_item_data = create_item(shopping_list.id)
|
||||
new_item_data["extras"] = {key_str_1: val_str_1}
|
||||
|
||||
response = api_client.post(Routes.items, json=new_item_data, headers=unique_user.token)
|
||||
item_as_json = utils.assert_derserialize(response, 201)
|
||||
|
||||
# make sure the extra persists
|
||||
extras = item_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
|
||||
# add more extras to the item
|
||||
item_as_json["extras"][key_str_2] = val_str_2
|
||||
|
||||
response = api_client.put(Routes.item(item_as_json["id"]), json=item_as_json, headers=unique_user.token)
|
||||
item_as_json = utils.assert_derserialize(response, 200)
|
||||
|
||||
# make sure both the new extra and original extra persist
|
||||
extras = item_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert key_str_2 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
assert extras[key_str_2] == val_str_2
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import random
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.schema.group.group_shopping_list import ShoppingListOut
|
||||
from mealie.schema.recipe.recipe import Recipe
|
||||
|
@ -12,17 +13,19 @@ from tests.utils.fixture_schemas import TestUser
|
|||
class Routes:
|
||||
base = "/api/groups/shopping/lists"
|
||||
|
||||
def item(item_id: str) -> str:
|
||||
@staticmethod
|
||||
def item(item_id: str | UUID4) -> str:
|
||||
return f"{Routes.base}/{item_id}"
|
||||
|
||||
def add_recipe(item_id: str, recipe_id: str) -> str:
|
||||
@staticmethod
|
||||
def add_recipe(item_id: str | UUID4, recipe_id: str | UUID4) -> str:
|
||||
return f"{Routes.item(item_id)}/recipe/{recipe_id}"
|
||||
|
||||
|
||||
def test_shopping_lists_get_all(api_client: TestClient, unique_user: TestUser, shopping_lists: list[ShoppingListOut]):
|
||||
all_lists = api_client.get(Routes.base, headers=unique_user.token)
|
||||
assert all_lists.status_code == 200
|
||||
all_lists = all_lists.json()["items"]
|
||||
response = api_client.get(Routes.base, headers=unique_user.token)
|
||||
assert response.status_code == 200
|
||||
all_lists = response.json()["items"]
|
||||
|
||||
assert len(all_lists) == len(shopping_lists)
|
||||
|
||||
|
@ -199,3 +202,39 @@ def test_shopping_lists_remove_recipe_multiple_quantity(
|
|||
refs = as_json["recipeReferences"]
|
||||
assert len(refs) == 1
|
||||
assert refs[0]["recipeId"] == str(recipe.id)
|
||||
|
||||
|
||||
def test_shopping_list_extras(
|
||||
api_client: TestClient,
|
||||
unique_user: TestUser,
|
||||
):
|
||||
key_str_1 = random_string()
|
||||
val_str_1 = random_string()
|
||||
|
||||
key_str_2 = random_string()
|
||||
val_str_2 = random_string()
|
||||
|
||||
# create a list with extras
|
||||
new_list_data: dict = {"name": random_string()}
|
||||
new_list_data["extras"] = {key_str_1: val_str_1}
|
||||
|
||||
response = api_client.post(Routes.base, json=new_list_data, headers=unique_user.token)
|
||||
list_as_json = utils.assert_derserialize(response, 201)
|
||||
|
||||
# make sure the extra persists
|
||||
extras = list_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
|
||||
# add more extras to the list
|
||||
list_as_json["extras"][key_str_2] = val_str_2
|
||||
|
||||
response = api_client.put(Routes.item(list_as_json["id"]), json=list_as_json, headers=unique_user.token)
|
||||
list_as_json = utils.assert_derserialize(response, 200)
|
||||
|
||||
# make sure both the new extra and original extra persist
|
||||
extras = list_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert key_str_2 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
assert extras[key_str_2] == val_str_2
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import CreateIngredientFood
|
||||
from tests import utils
|
||||
from tests.utils.factories import random_string
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
@ -9,12 +12,13 @@ from tests.utils.fixture_schemas import TestUser
|
|||
class Routes:
|
||||
base = "/api/foods"
|
||||
|
||||
@staticmethod
|
||||
def item(item_id: int) -> str:
|
||||
return f"{Routes.base}/{item_id}"
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def food(api_client: TestClient, unique_user: TestUser) -> dict:
|
||||
def food(api_client: TestClient, unique_user: TestUser) -> Generator[dict, None, None]:
|
||||
data = CreateIngredientFood(
|
||||
name=random_string(10),
|
||||
description=random_string(10),
|
||||
|
@ -74,3 +78,39 @@ def test_delete_food(api_client: TestClient, food: dict, unique_user: TestUser):
|
|||
|
||||
response = api_client.get(Routes.item(id), headers=unique_user.token)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_food_extras(
|
||||
api_client: TestClient,
|
||||
unique_user: TestUser,
|
||||
):
|
||||
key_str_1 = random_string()
|
||||
val_str_1 = random_string()
|
||||
|
||||
key_str_2 = random_string()
|
||||
val_str_2 = random_string()
|
||||
|
||||
# create a food with extras
|
||||
new_food_data: dict = {"name": random_string()}
|
||||
new_food_data["extras"] = {key_str_1: val_str_1}
|
||||
|
||||
response = api_client.post(Routes.base, json=new_food_data, headers=unique_user.token)
|
||||
food_as_json = utils.assert_derserialize(response, 201)
|
||||
|
||||
# make sure the extra persists
|
||||
extras = food_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
|
||||
# add more extras to the food
|
||||
food_as_json["extras"][key_str_2] = val_str_2
|
||||
|
||||
response = api_client.put(Routes.item(food_as_json["id"]), json=food_as_json, headers=unique_user.token)
|
||||
food_as_json = utils.assert_derserialize(response, 200)
|
||||
|
||||
# make sure both the new extra and original extra persist
|
||||
extras = food_as_json["extras"]
|
||||
assert key_str_1 in extras
|
||||
assert key_str_2 in extras
|
||||
assert extras[key_str_1] == val_str_1
|
||||
assert extras[key_str_2] == val_str_2
|
||||
|
|
|
@ -4,7 +4,7 @@ from mealie.core.config import get_app_settings
|
|||
from mealie.services.backups_v2.alchemy_exporter import AlchemyExporter
|
||||
|
||||
ALEMBIC_VERSIONS = [
|
||||
{"version_num": "089bfa50d0ed"},
|
||||
{"version_num": "44e8d670719d"},
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue