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

Refactor/conver to controllers (#923)

* add dependency injection for get_repositories

* convert events api to controller

* update generic typing

* add abstract controllers

* update test naming

* migrate admin services to controllers

* add additional admin route tests

* remove print

* add public shared dependencies

* add types

* fix typo

* add static variables for recipe json keys

* add coverage gutters config

* update controller routers

* add generic success response

* add category/tag/tool tests

* add token refresh test

* add coverage utilities

* covert comments to controller

* add todo

* add helper properties

* delete old service

* update test notes

* add unit test for pretty_stats

* remove dead code from post_webhooks

* update group routes to use controllers

* add additional group test coverage

* abstract common permission checks

* convert ingredient parser to controller

* update recipe crud to use controller

* remove dead-code

* add class lifespan tracker for debugging

* convert bulk export to controller

* migrate tools router to controller

* update recipe share to controller

* move customer router to _base

* ignore prints in flake8

* convert units and foods to new controllers

* migrate user routes to controllers

* centralize error handling

* fix invalid ref

* reorder fields

* update routers to share common handling

* update tests

* remove prints

* fix cookbooks delete

* fix cookbook get

* add controller for mealplanner

* cover report routes to controller

* remove __future__ imports

* remove dead code

* remove all base_http children and remove dead code
This commit is contained in:
Hayden 2022-01-13 13:06:52 -09:00 committed by GitHub
parent 5823a32daf
commit c4540f1395
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
164 changed files with 3111 additions and 3213 deletions

View file

@ -0,0 +1,52 @@
from fastapi.testclient import TestClient
from mealie.core.config import get_app_settings
from mealie.core.settings.static import APP_VERSION
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/admin/about"
statistics = f"{base}/statistics"
check = f"{base}/check"
def test_admin_about_get_app_info(api_client: TestClient, admin_user: TestUser):
response = api_client.get(Routes.base, headers=admin_user.token)
as_dict = response.json()
settings = get_app_settings()
assert as_dict["version"] == APP_VERSION
assert as_dict["demoStatus"] == settings.IS_DEMO
assert as_dict["apiPort"] == settings.API_PORT
assert as_dict["apiDocs"] == settings.API_DOCS
assert as_dict["dbType"] == settings.DB_ENGINE
# assert as_dict["dbUrl"] == settings.DB_URL_PUBLIC
assert as_dict["defaultGroup"] == settings.DEFAULT_GROUP
def test_admin_about_get_app_statistics(api_client: TestClient, admin_user: TestUser):
response = api_client.get(Routes.statistics, headers=admin_user.token)
as_dict = response.json()
# Smoke Test - Test the endpoint returns something thats a number
assert as_dict["totalRecipes"] >= 0
assert as_dict["uncategorizedRecipes"] >= 0
assert as_dict["untaggedRecipes"] >= 0
assert as_dict["totalUsers"] >= 0
assert as_dict["totalGroups"] >= 0
def test_admin_about_check_app_config(api_client: TestClient, admin_user: TestUser):
response = api_client.get(Routes.check, headers=admin_user.token)
as_dict = response.json()
# Smoke Test - Test the endpoint returns something thats a the expected shape
assert as_dict["emailReady"] in [True, False]
assert as_dict["ldapReady"] in [True, False]
assert as_dict["baseUrlSet"] in [True, False]
assert as_dict["isUpToDate"] in [True, False]

View file

@ -0,0 +1,24 @@
from fastapi.testclient import TestClient
from mealie.services.server_tasks.background_executory import BackgroundExecutor
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/admin/server-tasks"
def test_admin_server_tasks_test_and_get(api_client: TestClient, admin_user: TestUser):
# Bootstrap Timer
BackgroundExecutor.sleep_time = 0.1
response = api_client.post(Routes.base, headers=admin_user.token)
assert response.status_code == 201
response = api_client.get(Routes.base, headers=admin_user.token)
as_dict = response.json()
assert len(as_dict) == 1
# Reset Timer
BackgroundExecutor.sleep_time = 60

View file

@ -0,0 +1,107 @@
from dataclasses import dataclass
import pytest
from fastapi.testclient import TestClient
from mealie.schema.static import recipe_keys
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/categories"
recipes = "/api/recipes"
def item(item_id: int) -> str:
return f"{Routes.base}/{item_id}"
def recipe(recipe_id: int) -> str:
return f"{Routes.recipes}/{recipe_id}"
@dataclass
class TestRecipeCategory:
id: int
name: str
slug: str
recipes: list
@pytest.fixture(scope="function")
def category(api_client: TestClient, unique_user: TestUser) -> TestRecipeCategory:
data = {"name": random_string(10)}
response = api_client.post(Routes.base, json=data, headers=unique_user.token)
assert response.status_code == 201
as_json = response.json()
yield TestRecipeCategory(
id=as_json["id"],
name=data["name"],
slug=as_json["slug"],
recipes=[],
)
try:
response = api_client.delete(Routes.item(response.json()["slug"]), headers=unique_user.token)
except Exception:
pass
def test_create_category(api_client: TestClient, unique_user: TestUser):
data = {"name": random_string(10)}
response = api_client.post(Routes.base, json=data, headers=unique_user.token)
assert response.status_code == 201
def test_read_category(api_client: TestClient, category: TestRecipeCategory, unique_user: TestUser):
response = api_client.get(Routes.item(category.slug), headers=unique_user.token)
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == category.id
assert as_json["name"] == category.name
def test_update_category(api_client: TestClient, category: TestRecipeCategory, unique_user: TestUser):
update_data = {
"id": category.id,
"name": random_string(10),
"slug": category.slug,
}
response = api_client.put(Routes.item(category.slug), json=update_data, headers=unique_user.token)
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == category.id
assert as_json["name"] == update_data["name"]
def test_delete_category(api_client: TestClient, category: TestRecipeCategory, unique_user: TestUser):
response = api_client.delete(Routes.item(category.slug), headers=unique_user.token)
assert response.status_code == 200
def test_recipe_category_association(api_client: TestClient, category: TestRecipeCategory, unique_user: TestUser):
# Setup Recipe
recipe_data = {"name": random_string(10)}
response = api_client.post(Routes.recipes, json=recipe_data, headers=unique_user.token)
slug = response.json()
assert response.status_code == 201
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
as_json[recipe_keys.recipe_category] = [{"id": category.id, "name": category.name, "slug": category.slug}]
# Update Recipe
response = api_client.put(Routes.recipe(slug), json=as_json, headers=unique_user.token)
assert response.status_code == 200
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
assert as_json[recipe_keys.recipe_category][0]["slug"] == category.slug

View file

@ -0,0 +1,106 @@
from dataclasses import dataclass
import pytest
from fastapi.testclient import TestClient
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/tags"
recipes = "/api/recipes"
def item(item_id: int) -> str:
return f"{Routes.base}/{item_id}"
def recipe(recipe_id: int) -> str:
return f"{Routes.recipes}/{recipe_id}"
@dataclass
class TestRecipeTag:
id: int
name: str
slug: str
recipes: list
@pytest.fixture(scope="function")
def tag(api_client: TestClient, unique_user: TestUser) -> TestRecipeTag:
data = {"name": random_string(10)}
response = api_client.post(Routes.base, json=data, headers=unique_user.token)
assert response.status_code == 201
as_json = response.json()
yield TestRecipeTag(
id=as_json["id"],
name=data["name"],
slug=as_json["slug"],
recipes=[],
)
try:
response = api_client.delete(Routes.item(response.json()["slug"]), headers=unique_user.token)
except Exception:
pass
def test_create_tag(api_client: TestClient, unique_user: TestUser):
data = {"name": random_string(10)}
response = api_client.post(Routes.base, json=data, headers=unique_user.token)
assert response.status_code == 201
def test_read_tag(api_client: TestClient, tag: TestRecipeTag, unique_user: TestUser):
response = api_client.get(Routes.item(tag.slug), headers=unique_user.token)
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == tag.id
assert as_json["name"] == tag.name
def test_update_tag(api_client: TestClient, tag: TestRecipeTag, unique_user: TestUser):
update_data = {
"id": tag.id,
"name": random_string(10),
"slug": tag.slug,
}
response = api_client.put(Routes.item(tag.slug), json=update_data, headers=unique_user.token)
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == tag.id
assert as_json["name"] == update_data["name"]
def test_delete_tag(api_client: TestClient, tag: TestRecipeTag, unique_user: TestUser):
response = api_client.delete(Routes.item(tag.slug), headers=unique_user.token)
assert response.status_code == 200
def test_recipe_tag_association(api_client: TestClient, tag: TestRecipeTag, unique_user: TestUser):
# Setup Recipe
recipe_data = {"name": random_string(10)}
response = api_client.post(Routes.recipes, json=recipe_data, headers=unique_user.token)
slug = response.json()
assert response.status_code == 201
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
as_json["tags"] = [{"id": tag.id, "name": tag.name, "slug": tag.slug}]
# Update Recipe
response = api_client.put(Routes.recipe(slug), json=as_json, headers=unique_user.token)
assert response.status_code == 200
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
assert as_json["tags"][0]["slug"] == tag.slug

View file

@ -63,7 +63,6 @@ def test_read_tool(api_client: TestClient, tool: TestRecipeTool, unique_user: Te
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == tool.id
assert as_json["name"] == tool.name
@ -80,7 +79,6 @@ def test_update_tool(api_client: TestClient, tool: TestRecipeTool, unique_user:
assert response.status_code == 200
as_json = response.json()
assert as_json["id"] == tool.id
assert as_json["name"] == update_data["name"]
@ -93,28 +91,20 @@ def test_delete_tool(api_client: TestClient, tool: TestRecipeTool, unique_user:
def test_recipe_tool_association(api_client: TestClient, tool: TestRecipeTool, unique_user: TestUser):
# Setup Recipe
recipe_data = {"name": random_string(10)}
response = api_client.post(Routes.recipes, json=recipe_data, headers=unique_user.token)
slug = response.json()
assert response.status_code == 201
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
as_json["tools"] = [{"id": tool.id, "name": tool.name, "slug": tool.slug}]
# Update Recipe
response = api_client.put(Routes.recipe(slug), json=as_json, headers=unique_user.token)
assert response.status_code == 200
# Get Recipe Data
response = api_client.get(Routes.recipe(slug), headers=unique_user.token)
as_json = response.json()
assert as_json["tools"][0]["id"] == tool.id

View file

@ -1,8 +1,14 @@
import random
from dataclasses import dataclass
from uuid import UUID
import pytest
from fastapi.testclient import TestClient
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.cookbook.cookbook import ReadCookBook, SaveCookBook
from tests.utils.assertion_helpers import assert_ignore_keys
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
@ -14,27 +20,56 @@ class Routes:
def get_page_data(group_id: UUID):
name_and_slug = random_string(10)
return {
"name": "My New Page",
"slug": "my-new-page",
"name": name_and_slug,
"slug": name_and_slug,
"description": "",
"position": 0,
"categories": [],
"group_id": group_id,
"group_id": str(group_id),
}
@dataclass
class TestCookbook:
id: int
slug: str
name: str
data: dict
@pytest.fixture(scope="function")
def cookbooks(database: AllRepositories, unique_user: TestUser) -> list[TestCookbook]:
data: list[ReadCookBook] = []
yield_data: list[TestCookbook] = []
for _ in range(3):
cb = database.cookbooks.create(SaveCookBook(**get_page_data(unique_user.group_id)))
data.append(cb)
yield_data.append(TestCookbook(id=cb.id, slug=cb.slug, name=cb.name, data=cb.dict()))
yield yield_data
for cb in yield_data:
try:
database.cookbooks.delete(cb.id)
except Exception:
pass
def test_create_cookbook(api_client: TestClient, unique_user: TestUser):
page_data = get_page_data(unique_user.group_id)
response = api_client.post(Routes.base, json=page_data, headers=unique_user.token)
assert response.status_code == 201
def test_read_cookbook(api_client: TestClient, unique_user: TestUser):
page_data = get_page_data(unique_user.group_id)
def test_read_cookbook(api_client: TestClient, unique_user: TestUser, cookbooks: list[TestCookbook]):
sample = random.choice(cookbooks)
response = api_client.get(Routes.item(1), headers=unique_user.token)
assert_ignore_keys(response.json(), page_data)
response = api_client.get(Routes.item(sample.id), headers=unique_user.token)
assert response.status_code == 200
assert_ignore_keys(response.json(), sample.data)
def test_update_cookbook(api_client: TestClient, unique_user: TestUser):
@ -44,14 +79,36 @@ def test_update_cookbook(api_client: TestClient, unique_user: TestUser):
page_data["name"] = "My New Name"
response = api_client.put(Routes.item(1), json=page_data, headers=unique_user.token)
assert response.status_code == 200
def test_delete_cookbook(api_client: TestClient, unique_user: TestUser):
response = api_client.delete(Routes.item(1), headers=unique_user.token)
def test_update_cookbooks_many(api_client: TestClient, unique_user: TestUser, cookbooks: list[TestCookbook]):
pages = [x.data for x in cookbooks]
reverse_order = sorted(pages, key=lambda x: x["position"], reverse=True)
for x, page in enumerate(reverse_order):
page["position"] = x
page["group_id"] = str(unique_user.group_id)
response = api_client.put(Routes.base, json=reverse_order, headers=unique_user.token)
assert response.status_code == 200
response = api_client.get(Routes.base, headers=unique_user.token)
assert response.status_code == 200
known_ids = [x.id for x in cookbooks]
server_ids = [x["id"] for x in response.json()]
for know in known_ids: # Hacky check, because other tests don't cleanup after themselves :(
assert know in server_ids
def test_delete_cookbook(api_client: TestClient, unique_user: TestUser, cookbooks: list[TestCookbook]):
sample = random.choice(cookbooks)
response = api_client.delete(Routes.item(sample.id), headers=unique_user.token)
assert response.status_code == 200
response = api_client.get(Routes.item(1), headers=unique_user.token)
response = api_client.get(Routes.item(sample.slug), headers=unique_user.token)
assert response.status_code == 404

View file

@ -0,0 +1,38 @@
from fastapi.testclient import TestClient
from mealie.repos.all_repositories import AllRepositories
from tests.utils.assertion_helpers import assert_ignore_keys
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/groups/categories"
@staticmethod
def item(item_id: int | str) -> str:
return f"{Routes.base}/{item_id}"
def test_group_mealplan_set_preferences(api_client: TestClient, unique_user: TestUser, database: AllRepositories):
# Create Categories
categories = [{"name": x} for x in ["Breakfast", "Lunch", "Dinner"]]
created = []
for category in categories:
create = database.categories.create(category)
created.append(create.dict())
# Set Category Preferences
response = api_client.put(Routes.base, json=created, headers=unique_user.token)
assert response.status_code == 200
# Get Category Preferences
response = api_client.get(Routes.base, headers=unique_user.token)
assert response.status_code == 200
as_dict = response.json()
assert len(as_dict) == len(categories)
for api_data, expected in zip(as_dict, created):
assert_ignore_keys(api_data, expected, ["id", "recipes"])

View file

@ -0,0 +1,100 @@
from uuid import uuid4
from fastapi.testclient import TestClient
from mealie.repos.repository_factory import AllRepositories
from tests.utils.factories import random_bool
from tests.utils.fixture_schemas import TestUser
class Routes:
self = "/api/groups/self"
memebers = "/api/groups/members"
permissions = "/api/groups/permissions"
def get_permissions_payload(user_id: str, can_manage=None) -> dict:
return {
"user_id": user_id,
"can_manage": random_bool() if can_manage is None else can_manage,
"can_invite": random_bool(),
"can_organize": random_bool(),
}
def test_get_group_members(api_client: TestClient, user_tuple: list[TestUser]):
usr_1, usr_2 = user_tuple
response = api_client.get(Routes.memebers, headers=usr_1.token)
assert response.status_code == 200
members = response.json()
assert len(members) >= 2
all_ids = [x["id"] for x in members]
assert str(usr_1.user_id) in all_ids
assert str(usr_2.user_id) in all_ids
def test_set_memeber_permissions(api_client: TestClient, user_tuple: list[TestUser], database: AllRepositories):
usr_1, usr_2 = user_tuple
# Set Acting User
acting_user = database.users.get_one(usr_1.user_id)
acting_user.can_manage = True
database.users.update(acting_user.id, acting_user)
payload = get_permissions_payload(str(usr_2.user_id))
# Test
response = api_client.put(Routes.permissions, json=payload, headers=usr_1.token)
assert response.status_code == 200
def test_set_memeber_permissions_unauthorized(api_client: TestClient, unique_user: TestUser, database: AllRepositories):
# Setup
user = database.users.get_one(unique_user.user_id)
user.can_manage = False
database.users.update(user.id, user)
payload = get_permissions_payload(str(user.id))
payload = {
"user_id": str(user.id),
"can_manage": True,
"can_invite": True,
"can_organize": True,
}
# Test
response = api_client.put(Routes.permissions, json=payload, headers=unique_user.token)
assert response.status_code == 403
def test_set_memeber_permissions_other_group(
api_client: TestClient,
unique_user: TestUser,
g2_user: TestUser,
database: AllRepositories,
):
user = database.users.get_one(unique_user.user_id)
user.can_manage = True
database.users.update(user.id, user)
payload = get_permissions_payload(str(g2_user.user_id))
response = api_client.put(Routes.permissions, json=payload, headers=unique_user.token)
assert response.status_code == 403
def test_set_memeber_permissions_no_user(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
):
user = database.users.get_one(unique_user.user_id)
user.can_manage = True
database.users.update(user.id, user)
payload = get_permissions_payload(str(uuid4()))
response = api_client.put(Routes.permissions, json=payload, headers=unique_user.token)
assert response.status_code == 404

View file

@ -12,7 +12,6 @@ class Routes:
def test_get_preferences(api_client: TestClient, unique_user: TestUser) -> None:
response = api_client.get(Routes.preferences, headers=unique_user.token)
assert response.status_code == 200
preferences = response.json()

View file

@ -1,15 +0,0 @@
# from fastapi.testclient import TestClient
# from tests.utils.fixture_schemas import TestUser
# class Routes:
# base = "/api/groups/manage/data" # Not sure if this is a good url?!?!?
# def test_recipe_export(api_client: TestClient, unique_user: TestUser) -> None:
# assert False
# def test_recipe_import(api_client: TestClient, unique_user: TestUser) -> None:
# assert False

View file

@ -0,0 +1,160 @@
from pathlib import Path
import pytest
import sqlalchemy
from fastapi.testclient import TestClient
from mealie.core.dependencies.dependencies import validate_file_token
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.recipe.recipe_bulk_actions import ExportTypes
from mealie.schema.recipe.recipe_category import TagIn
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
class Routes:
create_recipes = "/api/recipes"
bulk_tag = "api/recipes/bulk-actions/tag"
bulk_categorize = "api/recipes/bulk-actions/categorize"
bulk_delete = "api/recipes/bulk-actions/delete"
bulk_export = "api/recipes/bulk-actions/export"
bulk_export_download = bulk_export + "/download"
bulk_export_purge = bulk_export + "/purge"
@pytest.fixture(scope="function")
def ten_slugs(api_client: TestClient, unique_user: TestUser, database: AllRepositories) -> list[str]:
slugs = []
for _ in range(10):
payload = {"name": random_string(length=20)}
response = api_client.post(Routes.create_recipes, json=payload, headers=unique_user.token)
assert response.status_code == 201
response_data = response.json()
slugs.append(response_data)
yield slugs
for slug in slugs:
try:
database.recipes.delete(slug)
except sqlalchemy.exc.NoResultFound:
pass
def test_bulk_tag_recipes(
api_client: TestClient, unique_user: TestUser, database: AllRepositories, ten_slugs: list[str]
):
# Setup Tags
tags = []
for _ in range(3):
tag_name = random_string()
tag = database.tags.create(TagIn(name=tag_name))
tags.append(tag.dict())
payload = {"recipes": ten_slugs, "tags": tags}
response = api_client.post(Routes.bulk_tag, json=payload, headers=unique_user.token)
assert response.status_code == 200
# Validate Recipes are Tagged
for slug in ten_slugs:
recipe = database.recipes.get_one(slug)
for tag in recipe.tags:
assert tag.slug in [x["slug"] for x in tags]
def test_bulk_categorize_recipes(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
ten_slugs: list[str],
):
# Setup Tags
categories = []
for _ in range(3):
cat_name = random_string()
cat = database.tags.create(TagIn(name=cat_name))
categories.append(cat.dict())
payload = {"recipes": ten_slugs, "categories": categories}
response = api_client.post(Routes.bulk_categorize, json=payload, headers=unique_user.token)
assert response.status_code == 200
# Validate Recipes are Categorized
for slug in ten_slugs:
recipe = database.recipes.get_one(slug)
for cat in recipe.recipe_category:
assert cat.slug in [x["slug"] for x in categories]
def test_bulk_delete_recipes(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
ten_slugs: list[str],
):
payload = {"recipes": ten_slugs}
response = api_client.post(Routes.bulk_delete, json=payload, headers=unique_user.token)
assert response.status_code == 200
# Validate Recipes are Tagged
for slug in ten_slugs:
recipe = database.recipes.get_one(slug)
assert recipe is None
def test_bulk_export_recipes(api_client: TestClient, unique_user: TestUser, ten_slugs: list[str]):
payload = {
"recipes": ten_slugs,
"export_type": ExportTypes.JSON.value,
}
response = api_client.post(Routes.bulk_export, json=payload, headers=unique_user.token)
assert response.status_code == 202
# Get All Exports Available
response = api_client.get(Routes.bulk_export, headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert len(response_data) == 1
export_path = response_data[0]["path"]
# Get Export Token
response = api_client.get(f"{Routes.bulk_export_download}?path={export_path}", headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert validate_file_token(response_data["fileToken"]) == Path(export_path)
# Use Export Token to donwload export
response = api_client.get("/api/utils/download?token=" + response_data["fileToken"])
assert response.status_code == 200
# Smoke Test to check that a file was downloaded
assert response.headers["Content-Type"] == "application/octet-stream"
assert len(response.content) > 0
# Purge Export
response = api_client.delete(Routes.bulk_export_purge, headers=unique_user.token)
assert response.status_code == 200
# Validate Export was purged
response = api_client.get(Routes.bulk_export, headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert len(response_data) == 0

View file

@ -152,3 +152,17 @@ def test_delete(api_client: TestClient, api_routes: AppRoutes, recipe_data: Reci
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
response = api_client.delete(recipe_url, headers=unique_user.token)
assert response.status_code == 200
def test_recipe_crud_404(api_client: TestClient, api_routes: AppRoutes, unique_user: TestUser):
response = api_client.put(api_routes.recipes_recipe_slug("test"), json={"test": "stest"}, headers=unique_user.token)
assert response.status_code == 404
response = api_client.get(api_routes.recipes_recipe_slug("test"), headers=unique_user.token)
assert response.status_code == 404
response = api_client.delete(api_routes.recipes_recipe_slug("test"), headers=unique_user.token)
assert response.status_code == 404
response = api_client.patch(api_routes.recipes_create_url, json={"test": "stest"}, headers=unique_user.token)
assert response.status_code == 404

View file

@ -0,0 +1,66 @@
import pytest
import sqlalchemy
from fastapi.testclient import TestClient
from mealie.repos.repository_factory import AllRepositories
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
class Routes:
create_recipes = "/api/recipes"
def base(item_id: int) -> str:
return f"api/users/{item_id}/favorites"
def toggle(item_id: int, slug: str) -> str:
return f"{Routes.base(item_id)}/{slug}"
@pytest.fixture(scope="function")
def ten_slugs(api_client: TestClient, unique_user: TestUser, database: AllRepositories) -> list[str]:
slugs = []
for _ in range(10):
payload = {"name": random_string(length=20)}
response = api_client.post(Routes.create_recipes, json=payload, headers=unique_user.token)
assert response.status_code == 201
response_data = response.json()
slugs.append(response_data)
yield slugs
for slug in slugs:
try:
database.recipes.delete(slug)
except sqlalchemy.exc.NoResultFound:
pass
def test_recipe_favorites(api_client: TestClient, unique_user: TestUser, ten_slugs: list[str]):
# Check that the user has no favorites
response = api_client.get(Routes.base(unique_user.user_id), headers=unique_user.token)
assert response.status_code == 200
assert response.json()["favoriteRecipes"] == []
# Add a few recipes to the user's favorites
for slug in ten_slugs:
response = api_client.post(Routes.toggle(unique_user.user_id, slug), headers=unique_user.token)
assert response.status_code == 200
# Check that the user has the recipes in their favorites
response = api_client.get(Routes.base(unique_user.user_id), headers=unique_user.token)
assert response.status_code == 200
assert len(response.json()["favoriteRecipes"]) == 10
# Remove a few recipes from the user's favorites
for slug in ten_slugs[:5]:
response = api_client.delete(Routes.toggle(unique_user.user_id, slug), headers=unique_user.token)
assert response.status_code == 200
# Check that the user has the recipes in their favorites
response = api_client.get(Routes.base(unique_user.user_id), headers=unique_user.token)
assert response.status_code == 200
assert len(response.json()["favoriteRecipes"]) == 5

View file

@ -0,0 +1,47 @@
import pytest
from fastapi.testclient import TestClient
from mealie.schema.recipe.recipe_ingredient import RegisteredParser
from tests.unit_tests.test_ingredient_parser import TestIngredient, crf_exists, test_ingredients
from tests.utils.fixture_schemas import TestUser
class Routes:
ingredient = "/api/parser/ingredient"
ingredients = "/api/parser/ingredients"
def assert_ingredient(api_response: dict, test_ingredient: TestIngredient):
assert api_response["ingredient"]["quantity"] == test_ingredient.quantity
assert api_response["ingredient"]["unit"]["name"] == test_ingredient.unit
assert api_response["ingredient"]["food"]["name"] == test_ingredient.food
assert api_response["ingredient"]["note"] == test_ingredient.comments
@pytest.mark.skipif(not crf_exists(), reason="CRF++ not installed")
@pytest.mark.parametrize("test_ingredient", test_ingredients)
def test_recipe_ingredient_parser_nlp(api_client: TestClient, test_ingredient: TestIngredient, unique_user: TestUser):
payload = {"parser": RegisteredParser.nlp, "ingredient": test_ingredient.input}
response = api_client.post(Routes.ingredient, json=payload, headers=unique_user.token)
assert response.status_code == 200
assert_ingredient(response.json(), test_ingredient)
@pytest.mark.skipif(not crf_exists(), reason="CRF++ not installed")
def test_recipe_ingredients_parser_nlp(api_client: TestClient, unique_user: TestUser):
payload = {"parser": RegisteredParser.nlp, "ingredients": [x.input for x in test_ingredients]}
response = api_client.post(Routes.ingredients, json=payload, headers=unique_user.token)
assert response.status_code == 200
for api_ingredient, test_ingredient in zip(response.json(), test_ingredients):
assert_ingredient(api_ingredient, test_ingredient)
@pytest.mark.skip("TODO: Implement")
def test_recipe_ingredient_parser_brute(api_client: TestClient):
pass
@pytest.mark.skip("TODO: Implement")
def test_recipe_ingredients_parser_brute(api_client: TestClient):
pass

View file

@ -0,0 +1,125 @@
import pytest
import sqlalchemy
from fastapi.testclient import TestClient
from mealie.repos.repository_factory import AllRepositories
from mealie.schema.recipe.recipe_share_token import RecipeShareTokenSave
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/shared/recipes"
create_recipes = "/api/recipes"
@staticmethod
def item(item_id: str):
return f"{Routes.base}/{item_id}"
@pytest.fixture(scope="function")
def slug(api_client: TestClient, unique_user: TestUser, database: AllRepositories) -> str:
payload = {"name": random_string(length=20)}
response = api_client.post(Routes.create_recipes, json=payload, headers=unique_user.token)
assert response.status_code == 201
response_data = response.json()
yield response_data
try:
database.recipes.delete(response_data)
except sqlalchemy.exc.NoResultFound:
pass
def test_recipe_share_tokens_get_all(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
slug: str,
):
# Create 5 Tokens
recipe = database.recipes.get_one(slug)
tokens = []
for _ in range(5):
token = database.recipe_share_tokens.create(
RecipeShareTokenSave(recipe_id=recipe.id, group_id=unique_user.group_id)
)
tokens.append(token)
# Get All Tokens
response = api_client.get(Routes.base, headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert len(response_data) == 5
def test_recipe_share_tokens_get_all_with_id(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
slug: str,
):
# Create 5 Tokens
recipe = database.recipes.get_one(slug)
tokens = []
for _ in range(3):
token = database.recipe_share_tokens.create(
RecipeShareTokenSave(recipe_id=recipe.id, group_id=unique_user.group_id)
)
tokens.append(token)
response = api_client.get(Routes.base + "?recipe_id=" + str(recipe.id), headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert len(response_data) == 3
def test_recipe_share_tokens_create_and_get_one(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
slug: str,
):
recipe = database.recipes.get_one(slug)
payload = {
"recipe_id": recipe.id,
}
response = api_client.post(Routes.base, json=payload, headers=unique_user.token)
assert response.status_code == 201
response = api_client.get(Routes.item(response.json()["id"]), json=payload, headers=unique_user.token)
assert response.status_code == 200
response_data = response.json()
assert response_data["recipe"]["id"] == recipe.id
def test_recipe_share_tokens_delete_one(
api_client: TestClient,
unique_user: TestUser,
database: AllRepositories,
slug: str,
):
# Create Token
recipe = database.recipes.get_one(slug)
token = database.recipe_share_tokens.create(
RecipeShareTokenSave(recipe_id=recipe.id, group_id=unique_user.group_id)
)
# Delete Token
response = api_client.delete(Routes.item(token.id), headers=unique_user.token)
assert response.status_code == 200
# Get Token
token = database.recipe_share_tokens.get_one(token.id)
assert token is None

View file

@ -3,6 +3,7 @@ import json
from fastapi.testclient import TestClient
from tests.utils.app_routes import AppRoutes
from tests.utils.fixture_schemas import TestUser
def test_failed_login(api_client: TestClient, api_routes: AppRoutes):
@ -23,3 +24,9 @@ def test_superuser_login(api_client: TestClient, api_routes: AppRoutes, admin_to
assert response.status_code == 200
return {"Authorization": f"Bearer {new_token}"}
def test_user_token_refresh(api_client: TestClient, api_routes: AppRoutes, admin_user: TestUser):
response = api_client.post(api_routes.auth_refresh, headers=admin_user.token)
response = api_client.get(api_routes.users_self, headers=admin_user.token)
assert response.status_code == 200