1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-05 13:35:23 +02:00

feat: mealplan-webhooks (#1403)

* fix type errors on event bus

* webhooks fields required for new implementation

* db migration

* wip: webhook query + tests and stub function

* ignore type checker error

* type and method cleanup

* datetime and time utc validator

* update testing code for utc scheduled time

* fix file cmp function call

* update version_number

* add support for translating "time" objects when restoring backup

* bump recipe-scrapers

* use specific import syntax

* generate frontend types

* utilize names exports

* use utc times

* add task to scheduler

* implement new scheduler functionality

* stub for type annotation

* implement meal-plan data getter

* add experimental banner
This commit is contained in:
Hayden 2022-06-17 13:25:47 -08:00 committed by GitHub
parent b1256f4ad2
commit 5a053cdcd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 428 additions and 93 deletions

View file

@ -1,10 +1,11 @@
import contextlib
from collections.abc import Generator
from pytest import MonkeyPatch, fixture
mp = MonkeyPatch()
mp.setenv("PRODUCTION", "True")
mp.setenv("TESTING", "True")
from pathlib import Path
from fastapi.testclient import TestClient
@ -34,11 +35,9 @@ def api_client():
yield TestClient(app)
try:
with contextlib.suppress(Exception):
settings = config.get_app_settings()
settings.DB_PROVIDER.db_path.unlink() # Handle SQLite Provider
except Exception:
pass
@fixture(scope="session")
@ -52,16 +51,13 @@ def test_image_png():
@fixture(scope="session", autouse=True)
def global_cleanup() -> None:
def global_cleanup() -> Generator[None, None, None]:
"""Purges the .temp directory used for testing"""
yield None
try:
with contextlib.suppress(Exception):
temp_dir = Path(__file__).parent / ".temp"
if temp_dir.exists():
import shutil
shutil.rmtree(temp_dir, ignore_errors=True)
except Exception:
pass

View file

@ -1,71 +1,74 @@
from datetime import datetime, timezone
import pytest
from fastapi.testclient import TestClient
from tests.utils import assert_derserialize, jsonify
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/groups/webhooks"
@staticmethod
def item(item_id: int) -> str:
return f"{Routes.base}/{item_id}"
@pytest.fixture()
def webhook_data():
return {"enabled": True, "name": "Test-Name", "url": "https://my-fake-url.com", "time": "00:00"}
return {
"enabled": True,
"name": "Test-Name",
"url": "https://my-fake-url.com",
"time": "00:00",
"scheduledTime": datetime.now(),
}
def test_create_webhook(api_client: TestClient, unique_user: TestUser, webhook_data):
response = api_client.post(Routes.base, json=webhook_data, headers=unique_user.token)
response = api_client.post(Routes.base, json=jsonify(webhook_data), headers=unique_user.token)
assert response.status_code == 201
def test_read_webhook(api_client: TestClient, unique_user: TestUser, webhook_data):
response = api_client.post(Routes.base, json=webhook_data, headers=unique_user.token)
id = response.json()["id"]
response = api_client.post(Routes.base, json=jsonify(webhook_data), headers=unique_user.token)
item_id = response.json()["id"]
response = api_client.get(Routes.item(id), headers=unique_user.token)
response = api_client.get(Routes.item(item_id), headers=unique_user.token)
webhook = assert_derserialize(response, 200)
webhook = response.json()
assert webhook["id"]
assert webhook["id"] == item_id
assert webhook["name"] == webhook_data["name"]
assert webhook["url"] == webhook_data["url"]
assert webhook["time"] == webhook_data["time"]
assert webhook["scheduledTime"] == str(webhook_data["scheduledTime"].astimezone(timezone.utc).time())
assert webhook["enabled"] == webhook_data["enabled"]
def test_update_webhook(api_client: TestClient, webhook_data, unique_user: TestUser):
response = api_client.post(Routes.base, json=webhook_data, headers=unique_user.token)
id = response.json()["id"]
response = api_client.post(Routes.base, json=jsonify(webhook_data), headers=unique_user.token)
item_dict = assert_derserialize(response, 201)
item_id = item_dict["id"]
webhook_data["name"] = "My New Name"
webhook_data["url"] = "https://my-new-fake-url.com"
webhook_data["time"] = "01:00"
webhook_data["enabled"] = False
response = api_client.put(Routes.item(id), json=webhook_data, headers=unique_user.token)
response = api_client.put(Routes.item(item_id), json=jsonify(webhook_data), headers=unique_user.token)
updated_webhook = assert_derserialize(response, 200)
assert response.status_code == 200
updated_webhook = response.json()
assert updated_webhook["name"] == webhook_data["name"]
assert updated_webhook["url"] == webhook_data["url"]
assert updated_webhook["time"] == webhook_data["time"]
assert updated_webhook["enabled"] == webhook_data["enabled"]
assert response.status_code == 200
def test_delete_webhook(api_client: TestClient, webhook_data, unique_user: TestUser):
response = api_client.post(Routes.base, json=webhook_data, headers=unique_user.token)
id = response.json()["id"]
response = api_client.delete(Routes.item(id), headers=unique_user.token)
response = api_client.post(Routes.base, json=jsonify(webhook_data), headers=unique_user.token)
item_dict = assert_derserialize(response, 201)
item_id = item_dict["id"]
response = api_client.delete(Routes.item(item_id), headers=unique_user.token)
assert response.status_code == 200
response = api_client.get(Routes.item(id), headers=unique_user.token)
response = api_client.get(Routes.item(item_id), headers=unique_user.token)
assert response.status_code == 404

View file

@ -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": "ab0bae02578f"},
{"version_num": "f30cf048c228"},
]

View file

@ -22,7 +22,7 @@ def match_file_tree(path_a: Path, path_b: Path):
assert b_file.exists()
match_file_tree(a_file, b_file)
else:
assert filecmp(path_a, path_b)
assert filecmp.cmp(path_a, path_b)
def test_database_backup():

View file

@ -0,0 +1,65 @@
from datetime import datetime, timedelta
from pydantic import UUID4
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.group.webhook import SaveWebhook, WebhookType
from mealie.services.scheduler.tasks.post_webhooks import get_scheduled_webhooks
from tests.utils import random_string
from tests.utils.factories import random_bool
from tests.utils.fixture_schemas import TestUser
def webhook_factory(
group_id: str | UUID4,
enabled: bool = True,
name: str = "",
url: str = "",
scheduled_time: datetime | None = None,
webhook_type: str = WebhookType.mealplan,
) -> SaveWebhook:
return SaveWebhook(
enabled=enabled,
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(),
group_id=group_id,
)
def test_get_scheduled_webhooks_filter_query(database: AllRepositories, unique_user: TestUser):
"""
get_scheduled_webhooks_test tests the get_scheduled_webhooks function.
"""
expected: list[SaveWebhook] = []
start = datetime.now()
for _ in range(5):
new_item = webhook_factory(group_id=unique_user.group_id, enabled=random_bool())
out_of_range_item = webhook_factory(
group_id=unique_user.group_id,
enabled=random_bool(),
scheduled_time=(start - timedelta(minutes=20)),
)
database.webhooks.create(new_item)
database.webhooks.create(out_of_range_item)
if new_item.enabled:
expected.append(new_item)
results = get_scheduled_webhooks(database.session, start, datetime.now() + timedelta(minutes=5))
assert len(results) == len(expected)
for result in results:
assert result.enabled
for expected_item in expected:
if result.name == expected_item.name: # Names are uniquely generated so we can use this to compare
assert result.enabled == expected_item.enabled
break