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

feat: recipe timeline backend api (#1685)

* added recipe_timeline_events table to db

* added schema and routes for recipe timeline events

* added missing mixin and fixed update schema

* added tests

* adjusted migration revision tree

* updated alembic revision test

* added initial timeline event for new recipes

* added additional tests

* added event bus support

* renamed event_dt to timestamp

* add timeline_events to ignore list

* run code-gen

* use new test routes implementation

* use doc string syntax

* moved event type enum from db to schema

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
Michael Genson 2022-11-01 03:12:26 -05:00 committed by GitHub
parent 714a080ecb
commit 6ee64535df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 639 additions and 6 deletions

View file

@ -0,0 +1,252 @@
import pytest
from fastapi.testclient import TestClient
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut, RecipeTimelineEventPagination
from tests.utils import api_routes
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
@pytest.fixture(scope="function")
def recipes(api_client: TestClient, unique_user: TestUser):
recipes = []
for _ in range(3):
data = {"name": random_string(10)}
response = api_client.post(api_routes.recipes, json=data, headers=unique_user.token)
assert response.status_code == 201
slug = response.json()
response = api_client.get(f"{api_routes.recipes}/{slug}", headers=unique_user.token)
assert response.status_code == 200
recipe = Recipe.parse_obj(response.json())
recipes.append(recipe)
yield recipes
response = api_client.delete(f"{api_routes.recipes}/{slug}", headers=unique_user.token)
def test_create_timeline_event(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
recipe = recipes[0]
new_event = {
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug),
json=new_event,
headers=unique_user.token,
)
assert event_response.status_code == 201
event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert event.recipe_id == recipe.id
assert str(event.user_id) == str(unique_user.user_id)
def test_get_all_timeline_events(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
# create some events
recipe = recipes[0]
events_data = [
{
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
for _ in range(10)
]
events: list[RecipeTimelineEventOut] = []
for event_data in events_data:
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug), json=event_data, headers=unique_user.token
)
events.append(RecipeTimelineEventOut.parse_obj(event_response.json()))
# check that we see them all
params = {"page": 1, "perPage": -1}
events_response = api_client.get(
api_routes.recipes_slug_timeline_events(recipe.slug), params=params, headers=unique_user.token
)
events_pagination = RecipeTimelineEventPagination.parse_obj(events_response.json())
event_ids = [event.id for event in events]
paginated_event_ids = [event.id for event in events_pagination.items]
assert len(event_ids) <= len(paginated_event_ids)
for event_id in event_ids:
assert event_id in paginated_event_ids
def test_get_timeline_event(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
# create an event
recipe = recipes[0]
new_event_data = {
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug),
json=new_event_data,
headers=unique_user.token,
)
new_event = RecipeTimelineEventOut.parse_obj(event_response.json())
# fetch the new event
event_response = api_client.get(
api_routes.recipes_slug_timeline_events_item_id(recipe.slug, new_event.id), headers=unique_user.token
)
assert event_response.status_code == 200
event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert event == new_event
def test_update_timeline_event(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
old_subject = random_string()
new_subject = random_string()
# create an event
recipe = recipes[0]
new_event_data = {
"user_id": unique_user.user_id,
"subject": old_subject,
"event_type": "info",
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug), json=new_event_data, headers=unique_user.token
)
new_event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert new_event.subject == old_subject
# update the event
updated_event_data = {"subject": new_subject}
event_response = api_client.put(
api_routes.recipes_slug_timeline_events_item_id(recipe.slug, new_event.id),
json=updated_event_data,
headers=unique_user.token,
)
assert event_response.status_code == 200
updated_event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert new_event.id == updated_event.id
assert updated_event.subject == new_subject
def test_delete_timeline_event(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
# create an event
recipe = recipes[0]
new_event_data = {
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug), json=new_event_data, headers=unique_user.token
)
new_event = RecipeTimelineEventOut.parse_obj(event_response.json())
# delete the event
event_response = api_client.delete(
api_routes.recipes_slug_timeline_events_item_id(recipe.slug, new_event.id), headers=unique_user.token
)
assert event_response.status_code == 200
deleted_event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert deleted_event.id == new_event.id
# try to get the event
event_response = api_client.get(
api_routes.recipes_slug_timeline_events_item_id(recipe.slug, deleted_event.id), headers=unique_user.token
)
assert event_response.status_code == 404
def test_create_recipe_with_timeline_event(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
# make sure when the recipes fixture was created that all recipes have at least one event
for recipe in recipes:
events_response = api_client.get(
api_routes.recipes_slug_timeline_events(recipe.slug), headers=unique_user.token
)
events_pagination = RecipeTimelineEventPagination.parse_obj(events_response.json())
assert events_pagination.items
def test_invalid_recipe_slug(api_client: TestClient, unique_user: TestUser):
new_event_data = {
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(random_string()), json=new_event_data, headers=unique_user.token
)
assert event_response.status_code == 404
def test_recipe_slug_mismatch(api_client: TestClient, unique_user: TestUser, recipes: list[Recipe]):
# get new recipes
recipe = recipes[0]
invalid_recipe = recipes[1]
# create a new event
new_event_data = {
"user_id": unique_user.user_id,
"subject": random_string(),
"event_type": "info",
"message": random_string(),
}
event_response = api_client.post(
api_routes.recipes_slug_timeline_events(recipe.slug), json=new_event_data, headers=unique_user.token
)
event = RecipeTimelineEventOut.parse_obj(event_response.json())
# try to perform operations on the event using the wrong recipe
event_response = api_client.get(
api_routes.recipes_slug_timeline_events_item_id(invalid_recipe.slug, event.id),
json=new_event_data,
headers=unique_user.token,
)
assert event_response.status_code == 404
event_response = api_client.put(
api_routes.recipes_slug_timeline_events_item_id(invalid_recipe.slug, event.id),
json=new_event_data,
headers=unique_user.token,
)
assert event_response.status_code == 404
event_response = api_client.delete(
api_routes.recipes_slug_timeline_events_item_id(invalid_recipe.slug, event.id),
json=new_event_data,
headers=unique_user.token,
)
assert event_response.status_code == 404
# make sure the event still exists and is unmodified
event_response = api_client.get(
api_routes.recipes_slug_timeline_events_item_id(recipe.slug, event.id),
json=new_event_data,
headers=unique_user.token,
)
assert event_response.status_code == 200
existing_event = RecipeTimelineEventOut.parse_obj(event_response.json())
assert existing_event == event