1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-05 21:45:25 +02:00

feat: Move "on hand" and "last made" to household (#4616)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2025-01-13 10:19:49 -06:00 committed by GitHub
parent e565b919df
commit e9892aba89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1618 additions and 400 deletions

View file

@ -1,6 +1,13 @@
from datetime import datetime, timezone
from uuid import UUID
from dateutil.parser import parse as parse_dt
from fastapi.testclient import TestClient
from mealie.db.models.household import HouseholdToRecipe
from mealie.schema.recipe.recipe import Recipe
from tests.utils import api_routes
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
@ -18,3 +25,61 @@ def test_get_household_members(api_client: TestClient, user_tuple: list[TestUser
assert str(usr_1.user_id) in all_ids
assert str(usr_2.user_id) in all_ids
assert str(h2_user.user_id) not in all_ids
def test_get_household_recipe_default(api_client: TestClient, unique_user: TestUser):
recipe = unique_user.repos.recipes.create(
Recipe(
user_id=unique_user.user_id,
group_id=UUID(unique_user.group_id),
name=random_string(),
)
)
response = api_client.get(api_routes.households_self_recipes_recipe_slug(recipe.slug), headers=unique_user.token)
assert response.status_code == 200
assert response.json()["recipeId"] == str(recipe.id)
assert response.json()["lastMade"] is None
def test_get_household_recipe(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
dt_now = datetime.now(tz=timezone.utc)
recipe = unique_user.repos.recipes.create(
Recipe(
user_id=unique_user.user_id,
group_id=UUID(unique_user.group_id),
name=random_string(),
)
)
session = unique_user.repos.session
session.add(
HouseholdToRecipe(
session=session,
household_id=UUID(unique_user.household_id),
recipe_id=recipe.id,
last_made=dt_now,
)
)
session.commit()
response = api_client.get(api_routes.households_self_recipes_recipe_slug(recipe.slug), headers=unique_user.token)
assert response.status_code == 200
data = response.json()
assert data["recipeId"] == str(recipe.id)
assert data["lastMade"]
assert parse_dt(data["lastMade"]) == dt_now
response = api_client.get(api_routes.households_self_recipes_recipe_slug(recipe.slug), headers=h2_user.token)
assert response.status_code == 200
h2_data = response.json()
assert h2_data["recipeId"] == str(recipe.id)
assert h2_data["lastMade"] is None
def test_get_household_recipe_invalid_recipe(api_client: TestClient, unique_user: TestUser):
response = api_client.get(
api_routes.households_self_recipes_recipe_slug(random_string()), headers=unique_user.token
)
assert response.status_code == 404

View file

@ -1,6 +1,7 @@
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
import pytest
from dateutil.parser import parse as parse_dt
from fastapi.testclient import TestClient
from mealie.schema.cookbook.cookbook import SaveCookBook
@ -233,28 +234,50 @@ def test_user_can_update_last_made_on_other_household(
assert response.status_code == 201
h2_recipe = h2_user.repos.recipes.get_one(response.json())
assert h2_recipe and h2_recipe.id
h2_recipe_id = h2_recipe.id
h2_recipe_slug = h2_recipe.slug
response = api_client.get(api_routes.recipes_slug(h2_recipe_slug), headers=unique_user.token)
assert response.status_code == 200
recipe = response.json()
assert recipe["id"] == str(h2_recipe_id)
old_last_made = recipe["lastMade"]
dt_1 = datetime.now(tz=UTC)
dt_2 = dt_1 + timedelta(days=2)
now = datetime.now(UTC).isoformat().replace("+00:00", "Z")
# set last made for unique_user and make sure it only updates globally and for unique_user
response = api_client.patch(
api_routes.recipes_slug_last_made(h2_recipe_slug), json={"timestamp": now}, headers=unique_user.token
api_routes.recipes_slug_last_made(h2_recipe.slug),
json={"timestamp": dt_2.isoformat()},
headers=unique_user.token,
)
assert response.status_code == 200
# confirm the last made date was updated
response = api_client.get(api_routes.recipes_slug(h2_recipe_slug), headers=unique_user.token)
response = api_client.get(api_routes.households_self_recipes_recipe_slug(h2_recipe_slug), headers=unique_user.token)
assert response.status_code == 200
recipe = response.json()
assert recipe["id"] == str(h2_recipe_id)
new_last_made = recipe["lastMade"]
assert new_last_made == now != old_last_made
assert (last_made_json := response.json()["lastMade"])
assert parse_dt(last_made_json) == dt_2
response = api_client.get(api_routes.households_self_recipes_recipe_slug(h2_recipe_slug), headers=h2_user.token)
assert response.status_code == 200
assert response.json()["lastMade"] is None
recipe = h2_user.repos.recipes.get_one(h2_recipe_slug)
assert recipe
assert recipe.last_made == dt_2
# set last made for h2_user and make sure it only updates globally and for h2_user
response = api_client.patch(
api_routes.recipes_slug_last_made(h2_recipe.slug), json={"timestamp": dt_1.isoformat()}, headers=h2_user.token
)
assert response.status_code == 200
response = api_client.get(api_routes.households_self_recipes_recipe_slug(h2_recipe_slug), headers=h2_user.token)
assert response.status_code == 200
assert (last_made_json := response.json()["lastMade"])
assert parse_dt(last_made_json) == dt_1
response = api_client.get(api_routes.households_self_recipes_recipe_slug(h2_recipe_slug), headers=unique_user.token)
assert response.status_code == 200
assert (last_made_json := response.json()["lastMade"])
assert parse_dt(last_made_json) == dt_2
# this shouldn't have updated since dt_2 is newer than dt_1
recipe = h2_user.repos.recipes.get_one(h2_recipe_slug)
assert recipe
assert recipe.last_made == dt_2
def test_cookbook_recipes_includes_all_households(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):

View file

@ -14,14 +14,32 @@ from tests.utils.fixture_schemas import TestUser
def create_food(user: TestUser, on_hand: bool = False):
if on_hand:
household = user.repos.households.get_by_slug_or_id(user.household_id)
assert household
households = [household.slug]
else:
households = []
return user.repos.ingredient_foods.create(
SaveIngredientFood(id=uuid4(), name=random_string(), group_id=user.group_id, on_hand=on_hand)
SaveIngredientFood(
id=uuid4(), name=random_string(), group_id=user.group_id, households_with_ingredient_food=households
)
)
def create_tool(user: TestUser, on_hand: bool = False):
if on_hand:
household = user.repos.households.get_by_slug_or_id(user.household_id)
assert household
households = [household.slug]
else:
households = []
return user.repos.tools.create(
RecipeToolSave(id=uuid4(), name=random_string(), group_id=user.group_id, on_hand=on_hand)
RecipeToolSave(
id=uuid4(), name=random_string(), group_id=user.group_id, on_hand=on_hand, households_with_tool=households
)
)
@ -568,7 +586,7 @@ def test_include_cross_household_recipes(api_client: TestClient, unique_user: Te
try:
response = api_client.get(
api_routes.recipes_suggestions,
params={"maxMissingFoods": 0, "foods": [str(known_food.id)], "includeCrossHousehold": True},
params={"maxMissingFoods": 0, "foods": [str(known_food.id)]},
headers=h2_user.token,
)
response.raise_for_status()
@ -579,3 +597,61 @@ def test_include_cross_household_recipes(api_client: TestClient, unique_user: Te
finally:
unique_user.repos.recipes.delete(recipe.slug)
h2_user.repos.recipes.delete(other_recipe.slug)
def test_respect_cross_household_on_hand_food(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
on_hand_food = create_food(unique_user, on_hand=True) # only on-hand for unique_user
other_food = create_food(unique_user)
recipe = create_recipe(unique_user, foods=[on_hand_food, other_food])
try:
response = api_client.get(
api_routes.recipes_suggestions,
params={"maxMissingFoods": 0, "foods": [str(other_food.id)]},
headers=unique_user.token,
)
response.raise_for_status()
data = response.json()
assert len(data["items"]) == 1
assert data["items"][0]["recipe"]["id"] == str(recipe.id)
response = api_client.get(
api_routes.recipes_suggestions,
params={"maxMissingFoods": 0, "foods": [str(other_food.id)]},
headers=h2_user.token,
)
response.raise_for_status()
data = response.json()
assert len(data["items"]) == 0
finally:
unique_user.repos.recipes.delete(recipe.slug)
def test_respect_cross_household_on_hand_tool(api_client: TestClient, unique_user: TestUser, h2_user: TestUser):
on_hand_tool = create_tool(unique_user, on_hand=True) # only on-hand for unique_user
other_tool = create_tool(unique_user)
recipe = create_recipe(unique_user, tools=[on_hand_tool, other_tool])
try:
response = api_client.get(
api_routes.recipes_suggestions,
params={"maxMissingTools": 0, "tools": [str(other_tool.id)]},
headers=unique_user.token,
)
response.raise_for_status()
data = response.json()
assert len(data["items"]) == 1
assert data["items"][0]["recipe"]["id"] == str(recipe.id)
response = api_client.get(
api_routes.recipes_suggestions,
params={"maxMissingTools": 0, "tools": [str(other_tool.id)]},
headers=h2_user.token,
)
response.raise_for_status()
data = response.json()
assert len(data["items"]) == 0
finally:
unique_user.repos.recipes.delete(recipe.slug)