1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-02 20:15:24 +02:00

fix: Make Mealie Timezone-Aware (#3847)

Co-authored-by: boc-the-git <3479092+boc-the-git@users.noreply.github.com>
This commit is contained in:
Michael Genson 2024-07-08 16:12:20 -05:00 committed by GitHub
parent 17f9eef551
commit d5f7a883df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 250 additions and 176 deletions

View file

@ -1,7 +1,7 @@
import random
import time
from collections import defaultdict
from datetime import date, datetime, timedelta
from datetime import datetime, timedelta, timezone
from random import randint
from urllib.parse import parse_qsl, urlsplit
@ -233,7 +233,7 @@ def test_pagination_filter_null(database: AllRepositories, unique_user: TestUser
user_id=unique_user.user_id,
group_id=unique_user.group_id,
name=random_string(),
last_made=datetime.now(),
last_made=datetime.now(timezone.utc),
)
)
@ -619,7 +619,7 @@ def test_pagination_filter_datetimes(
def test_pagination_order_by_multiple(
database: AllRepositories, unique_user: TestUser, order_direction: OrderDirection
):
current_time = datetime.now()
current_time = datetime.now(timezone.utc)
alphabet = ["a", "b", "c", "d", "e"]
abbreviations = alphabet.copy()
@ -681,7 +681,7 @@ def test_pagination_order_by_multiple_directions(
order_by_str: str,
order_direction: OrderDirection,
):
current_time = datetime.now()
current_time = datetime.now(timezone.utc)
alphabet = ["a", "b", "c", "d", "e"]
abbreviations = alphabet.copy()
@ -729,7 +729,7 @@ def test_pagination_order_by_multiple_directions(
def test_pagination_order_by_nested_model(
database: AllRepositories, unique_user: TestUser, order_direction: OrderDirection
):
current_time = datetime.now()
current_time = datetime.now(timezone.utc)
alphabet = ["a", "b", "c", "d", "e"]
labels = database.group_multi_purpose_labels.create_many(
@ -759,7 +759,7 @@ def test_pagination_order_by_nested_model(
def test_pagination_order_by_doesnt_filter(database: AllRepositories, unique_user: TestUser):
current_time = datetime.now()
current_time = datetime.now(timezone.utc)
label = database.group_multi_purpose_labels.create(
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
@ -805,7 +805,7 @@ def test_pagination_order_by_nulls(
null_position: OrderByNullPosition,
order_direction: OrderDirection,
):
current_time = datetime.now()
current_time = datetime.now(timezone.utc)
label = database.group_multi_purpose_labels.create(
MultiPurposeLabelSave(name=random_string(), group_id=unique_user.group_id)
@ -909,10 +909,11 @@ def test_pagination_shopping_list_items_with_labels(database: AllRepositories, u
def test_pagination_filter_dates(api_client: TestClient, unique_user: TestUser):
yesterday = date.today() - timedelta(days=1)
today = date.today()
tomorrow = date.today() + timedelta(days=1)
day_after_tomorrow = date.today() + timedelta(days=2)
today = datetime.now(timezone.utc).date()
yesterday = today - timedelta(days=1)
tomorrow = today + timedelta(days=1)
day_after_tomorrow = today + timedelta(days=2)
mealplan_today = CreatePlanEntry(date=today, entry_type="breakfast", title=random_string(), text=random_string())
mealplan_tomorrow = CreatePlanEntry(

View file

@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from typing import cast
from uuid import UUID
@ -298,12 +298,12 @@ def test_recipe_repo_pagination_by_categories(database: AllRepositories, unique_
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for i in range(5):
pagination_query.pagination_seed = str(datetime.now())
pagination_query.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(database.recipes.page_all(pagination_query, categories=[category_slug]).items)
assert not all(i == random_ordered[0] for i in random_ordered)
@ -391,12 +391,12 @@ def test_recipe_repo_pagination_by_tags(database: AllRepositories, unique_user:
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for i in range(5):
pagination_query.pagination_seed = str(datetime.now())
pagination_query.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(database.recipes.page_all(pagination_query, tags=[tag_slug]).items)
assert len(random_ordered[0]) == 15
assert not all(i == random_ordered[0] for i in random_ordered)
@ -487,12 +487,12 @@ def test_recipe_repo_pagination_by_tools(database: AllRepositories, unique_user:
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for i in range(5):
pagination_query.pagination_seed = str(datetime.now())
pagination_query.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(database.recipes.page_all(pagination_query, tools=[tool_id]).items)
assert len(random_ordered[0]) == 15
assert not all(i == random_ordered[0] for i in random_ordered)
@ -571,12 +571,12 @@ def test_recipe_repo_pagination_by_foods(database: AllRepositories, unique_user:
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for i in range(5):
pagination_query.pagination_seed = str(datetime.now())
pagination_query.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(database.recipes.page_all(pagination_query, foods=[food_id]).items)
assert len(random_ordered[0]) == 15
assert not all(i == random_ordered[0] for i in random_ordered)
@ -651,12 +651,12 @@ def test_random_order_recipe_search(
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for _ in range(5):
pagination.pagination_seed = str(datetime.now())
pagination.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(repo.page_all(pagination, search="soup").items)
assert not all(i == random_ordered[0] for i in random_ordered)

View file

@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
import pytest
@ -125,11 +125,11 @@ def test_random_order_search(
page=1,
per_page=-1,
order_by="random",
pagination_seed=str(datetime.now()),
pagination_seed=str(datetime.now(timezone.utc)),
order_direction=OrderDirection.asc,
)
random_ordered = []
for _ in range(5):
pagination.pagination_seed = str(datetime.now())
pagination.pagination_seed = str(datetime.now(timezone.utc))
random_ordered.append(repo.page_all(pagination, search="unit").items)
assert not all(i == random_ordered[0] for i in random_ordered)

View file

@ -9,7 +9,7 @@ from sqlalchemy.orm import Session
import tests.data as test_data
from mealie.core.config import get_app_settings
from mealie.db.db_setup import session_context
from mealie.db.models._model_utils import GUID
from mealie.db.models._model_utils.guid import GUID
from mealie.db.models.group import Group
from mealie.db.models.group.shopping_list import ShoppingList
from mealie.db.models.labels import MultiPurposeLabel

View file

@ -1,4 +1,4 @@
from datetime import date, datetime, timedelta
from datetime import datetime, timedelta, timezone
from fastapi.testclient import TestClient
from pydantic import UUID4
@ -34,8 +34,10 @@ def test_new_mealplan_event(api_client: TestClient, unique_user: TestUser):
response_json = response.json()
initial_event_count = len(response_json["items"])
new_plan = CreatePlanEntry(date=date.today(), entry_type="dinner", recipe_id=recipe_id).model_dump(by_alias=True)
new_plan["date"] = date.today().isoformat()
new_plan = CreatePlanEntry(
date=datetime.now(timezone.utc).date(), entry_type="dinner", recipe_id=recipe_id
).model_dump(by_alias=True)
new_plan["date"] = datetime.now(timezone.utc).date().isoformat()
new_plan["recipeId"] = str(recipe_id)
response = api_client.post(api_routes.groups_mealplans, json=new_plan, headers=unique_user.token)
@ -63,7 +65,7 @@ def test_new_mealplan_event(api_client: TestClient, unique_user: TestUser):
response = api_client.get(api_routes.recipes_slug(recipe_name), headers=unique_user.token)
new_recipe_data: dict = response.json()
recipe = RecipeSummary.model_validate(new_recipe_data)
assert recipe.last_made.date() == date.today() # type: ignore
assert recipe.last_made.date() == datetime.now(timezone.utc).date() # type: ignore
# make sure nothing else was updated
for data in [original_recipe_data, new_recipe_data]:
@ -99,8 +101,10 @@ def test_new_mealplan_event_duplicates(api_client: TestClient, unique_user: Test
response_json = response.json()
initial_event_count = len(response_json["items"])
new_plan = CreatePlanEntry(date=date.today(), entry_type="dinner", recipe_id=recipe_id).model_dump(by_alias=True)
new_plan["date"] = date.today().isoformat()
new_plan = CreatePlanEntry(
date=datetime.now(timezone.utc).date(), entry_type="dinner", recipe_id=recipe_id
).model_dump(by_alias=True)
new_plan["date"] = datetime.now(timezone.utc).date().isoformat()
new_plan["recipeId"] = str(recipe_id)
response = api_client.post(api_routes.groups_mealplans, json=new_plan, headers=unique_user.token)
@ -143,10 +147,10 @@ def test_new_mealplan_events_with_multiple_recipes(api_client: TestClient, uniqu
for recipe in recipes:
mealplan_count_by_recipe_id[recipe.id] = 0 # type: ignore
for _ in range(random_int(1, 5)):
new_plan = CreatePlanEntry(date=date.today(), entry_type="dinner", recipe_id=str(recipe.id)).model_dump(
by_alias=True
)
new_plan["date"] = date.today().isoformat()
new_plan = CreatePlanEntry(
date=datetime.now(timezone.utc).date(), entry_type="dinner", recipe_id=str(recipe.id)
).model_dump(by_alias=True)
new_plan["date"] = datetime.now(timezone.utc).date().isoformat()
new_plan["recipeId"] = str(recipe.id)
response = api_client.post(api_routes.groups_mealplans, json=new_plan, headers=unique_user.token)
@ -196,15 +200,17 @@ def test_preserve_future_made_date(api_client: TestClient, unique_user: TestUser
recipe = RecipeSummary.model_validate(response.json())
recipe_id = str(recipe.id)
future_dt = datetime.now() + timedelta(days=random_int(1, 10))
future_dt = datetime.now(timezone.utc) + timedelta(days=random_int(1, 10))
recipe.last_made = future_dt
response = api_client.put(
api_routes.recipes_slug(recipe.slug), json=utils.jsonify(recipe), headers=unique_user.token
)
assert response.status_code == 200
new_plan = CreatePlanEntry(date=date.today(), entry_type="dinner", recipe_id=recipe_id).model_dump(by_alias=True)
new_plan["date"] = date.today().isoformat()
new_plan = CreatePlanEntry(
date=datetime.now(timezone.utc).date(), entry_type="dinner", recipe_id=recipe_id
).model_dump(by_alias=True)
new_plan["date"] = datetime.now(timezone.utc).date().isoformat()
new_plan["recipeId"] = str(recipe_id)
response = api_client.post(api_routes.groups_mealplans, json=new_plan, headers=unique_user.token)

View file

@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.group.group_shopping_list import ShoppingListItemCreate, ShoppingListItemOut, ShoppingListSave
@ -40,7 +40,7 @@ def test_cleanup(database: AllRepositories, unique_user: TestUser):
for item in unchecked_items + checked_items:
assert item in shopping_list.list_items
checked_items.sort(key=lambda x: x.update_at or datetime.now(), reverse=True)
checked_items.sort(key=lambda x: x.update_at or datetime.now(timezone.utc), reverse=True)
expected_kept_items = unchecked_items + checked_items[:MAX_CHECKED_ITEMS]
expected_deleted_items = checked_items[MAX_CHECKED_ITEMS:]

View file

@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from pydantic import UUID4
@ -23,7 +23,7 @@ def webhook_factory(
name=name or random_string(),
url=url or random_string(),
webhook_type=webhook_type,
scheduled_time=scheduled_time.time() if scheduled_time else datetime.now().time(),
scheduled_time=scheduled_time.time() if scheduled_time else datetime.now(timezone.utc).time(),
group_id=group_id,
)
@ -35,7 +35,7 @@ def test_get_scheduled_webhooks_filter_query(database: AllRepositories, unique_u
expected: list[SaveWebhook] = []
start = datetime.now()
start = datetime.now(timezone.utc)
for _ in range(5):
new_item = webhook_factory(group_id=unique_user.group_id, enabled=random_bool())
@ -52,7 +52,7 @@ def test_get_scheduled_webhooks_filter_query(database: AllRepositories, unique_u
expected.append(new_item)
event_bus_listener = WebhookEventListener(unique_user.group_id) # type: ignore
results = event_bus_listener.get_scheduled_webhooks(start, datetime.now() + timedelta(minutes=5))
results = event_bus_listener.get_scheduled_webhooks(start, datetime.now(timezone.utc) + timedelta(minutes=5))
assert len(results) == len(expected)

View file

@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from mealie.repos.repository_factory import AllRepositories
from mealie.services.user_services.user_service import UserService
@ -59,7 +59,7 @@ def test_lock_unlocker_user(database: AllRepositories, unique_user: TestUser) ->
assert not unlocked_user.is_locked
# Sanity check that the is_locked property is working
user.locked_at = datetime.now() - timedelta(days=2)
user.locked_at = datetime.now(timezone.utc) - timedelta(days=2)
assert not user.is_locked
@ -85,7 +85,7 @@ def test_reset_locked_users(database: AllRepositories, unique_user: TestUser) ->
assert user.login_attemps == 5
# Test that the locked user is unlocked by reset
user.locked_at = datetime.now() - timedelta(days=2)
user.locked_at = datetime.now(timezone.utc) - timedelta(days=2)
database.users.update(user.id, user)
unlocked = user_service.reset_locked_users()
user = database.users.get_one(unique_user.user_id)

View file

@ -1,4 +1,4 @@
from datetime import date
from datetime import datetime, timezone
from uuid import uuid4
import pytest
@ -7,7 +7,7 @@ from mealie.schema.meal_plan.new_meal import CreatePlanEntry
def test_create_plan_with_title():
entry = CreatePlanEntry(date=date.today(), title="Test Title")
entry = CreatePlanEntry(date=datetime.now(timezone.utc).date(), title="Test Title")
assert entry.title == "Test Title"
assert entry.recipe_id is None
@ -15,7 +15,7 @@ def test_create_plan_with_title():
def test_create_plan_with_slug():
uuid = uuid4()
entry = CreatePlanEntry(date=date.today(), recipe_id=uuid)
entry = CreatePlanEntry(date=datetime.now(timezone.utc).date(), recipe_id=uuid)
assert entry.recipe_id == uuid
assert entry.title == ""
@ -23,4 +23,4 @@ def test_create_plan_with_slug():
def test_slug_or_title_validation():
with pytest.raises(ValueError):
CreatePlanEntry(date=date.today(), slug="", title="")
CreatePlanEntry(date=datetime.now(timezone.utc).date(), slug="", title="")