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

Improve Test Coverage (#511)

* add recipe scaling notes

* test theme rename

* fix coverage call to use poetry

* remove print

* remove async

* consolidate test case data

* fix mealplan tests

* remove redundant else

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-06-13 13:09:44 -08:00 committed by GitHub
parent c325a49fc2
commit 2dc9c8e843
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 249 additions and 230 deletions

View file

@ -11,7 +11,7 @@ from pytest import fixture
from tests.app_routes import AppRoutes
from tests.test_config import TEST_DATA
from tests.utils.recipe_data import build_recipe_store, get_raw_no_image, get_raw_recipe
from tests.utils.recipe_data import get_raw_no_image, get_raw_recipe, get_recipe_test_cases
main()
@ -71,4 +71,4 @@ def raw_recipe_no_image():
@fixture(scope="session")
def recipe_store():
return build_recipe_store()
return get_recipe_test_cases()

View file

@ -4,16 +4,17 @@ import pytest
from fastapi.testclient import TestClient
from slugify import slugify
from tests.app_routes import AppRoutes
from tests.utils.recipe_data import RecipeTestData, build_recipe_store
from tests.utils.recipe_data import RecipeSiteTestCase, get_recipe_test_cases
recipe_test_data = build_recipe_store()
recipe_test_data = get_recipe_test_cases()
@pytest.mark.parametrize("recipe_data", recipe_test_data)
def test_create_by_url(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeTestData, token):
def test_create_by_url(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeSiteTestCase, token):
api_client.delete(api_routes.recipes_recipe_slug(recipe_data.expected_slug), headers=token)
response = api_client.post(api_routes.recipes_create_url, json={"url": recipe_data.url}, headers=token)
assert response.status_code == 201
assert json.loads(response.text) == recipe_data.expected_slug
@ -35,7 +36,7 @@ def test_create_no_image(api_client: TestClient, api_routes: AppRoutes, token, r
@pytest.mark.parametrize("recipe_data", recipe_test_data)
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeTestData, token):
def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeSiteTestCase, token):
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
response = api_client.get(recipe_url, headers=token)
assert response.status_code == 200
@ -68,7 +69,7 @@ def test_read_update(api_client: TestClient, api_routes: AppRoutes, recipe_data:
@pytest.mark.parametrize("recipe_data", recipe_test_data)
def test_rename(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeTestData, token):
def test_rename(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeSiteTestCase, token):
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
response = api_client.get(recipe_url, headers=token)
assert response.status_code == 200
@ -87,7 +88,7 @@ def test_rename(api_client: TestClient, api_routes: AppRoutes, recipe_data: Reci
@pytest.mark.parametrize("recipe_data", recipe_test_data)
def test_delete(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeTestData, token):
def test_delete(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeSiteTestCase, token):
recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug)
response = api_client.delete(recipe_url, headers=token)
assert response.status_code == 200

View file

@ -1,104 +1,102 @@
# import json
import json
# import pytest
# from fastapi.testclient import TestClient
# from tests.app_routes import AppRoutes
# from tests.utils.recipe_data import RecipeTestData
import pytest
from fastapi.testclient import TestClient
from tests.app_routes import AppRoutes
from tests.utils.recipe_data import RecipeSiteTestCase
# def get_meal_plan_template(first=None, second=None):
# return {
# "group": "Home",
# "startDate": "2021-01-18",
# "endDate": "2021-01-19",
# "meals": [
# {
# "slug": first,
# "date": "2021-1-17",
# },
# {
# "slug": second,
# "date": "2021-1-18",
# },
# ],
# }
def get_meal_plan_template(first=None, second=None):
return {
"group": "Home",
"startDate": "2021-01-18",
"endDate": "2021-01-19",
"planDays": [
{
"date": "2021-1-18",
"meals": [{"slug": first, "name": "", "description": ""}],
},
{
"date": "2021-1-19",
"meals": [{"slug": second, "name": "", "description": ""}],
},
],
}
# @pytest.fixture(scope="session")
# def slug_1(api_client: TestClient, api_routes: AppRoutes, token, recipe_store: list[RecipeTestData]):
# # Slug 1
# slug_1 = api_client.post(api_routes.recipes_create_url, json={"url": recipe_store[0].url}, headers=token)
# slug_1 = json.loads(slug_1.content)
@pytest.fixture(scope="session")
def slug_1(api_client: TestClient, api_routes: AppRoutes, token, recipe_store: list[RecipeSiteTestCase]):
slug_1 = api_client.post(api_routes.recipes_create_url, json={"url": recipe_store[0].url}, headers=token)
slug_1 = json.loads(slug_1.content)
# yield slug_1
yield slug_1
# api_client.delete(api_routes.recipes_recipe_slug(slug_1))
api_client.delete(api_routes.recipes_recipe_slug(slug_1))
# @pytest.fixture(scope="session")
# def slug_2(api_client: TestClient, api_routes: AppRoutes, token, recipe_store: list[RecipeTestData]):
# # Slug 2
# slug_2 = api_client.post(api_routes.recipes_create_url, json={"url": recipe_store[1].url}, headers=token)
# slug_2 = json.loads(slug_2.content)
@pytest.fixture(scope="session")
def slug_2(api_client: TestClient, api_routes: AppRoutes, token, recipe_store: list[RecipeSiteTestCase]):
slug_2 = api_client.post(api_routes.recipes_create_url, json={"url": recipe_store[1].url}, headers=token)
slug_2 = json.loads(slug_2.content)
# yield slug_2
yield slug_2
# api_client.delete(api_routes.recipes_recipe_slug(slug_2))
api_client.delete(api_routes.recipes_recipe_slug(slug_2))
# def test_create_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
# meal_plan = get_meal_plan_template(slug_1, slug_2)
def test_create_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
meal_plan = get_meal_plan_template(slug_1, slug_2)
# response = api_client.post(api_routes.meal_plans_create, json=meal_plan, headers=token)
# assert response.status_code == 201
response = api_client.post(api_routes.meal_plans_create, json=meal_plan, headers=token)
assert response.status_code == 201
# def test_read_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
# response = api_client.get(api_routes.meal_plans_all, headers=token)
def test_read_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
response = api_client.get(api_routes.meal_plans_all, headers=token)
# assert response.status_code == 200
assert response.status_code == 200
# meal_plan = get_meal_plan_template(slug_1, slug_2)
meal_plan_template = get_meal_plan_template(slug_1, slug_2)
# new_meal_plan = json.loads(response.text)
# meals = new_meal_plan[0]["meals"]
created_meal_plan = json.loads(response.text)
meals = created_meal_plan[0]["planDays"]
# assert meals[0]["slug"] == meal_plan["meals"][0]["slug"]
# assert meals[1]["slug"] == meal_plan["meals"][1]["slug"]
assert meals[0]["meals"][0]["slug"] == meal_plan_template["planDays"][0]["meals"][0]["slug"]
assert meals[1]["meals"][0]["slug"] == meal_plan_template["planDays"][1]["meals"][0]["slug"]
# def test_update_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
def test_update_mealplan(api_client: TestClient, api_routes: AppRoutes, slug_1, slug_2, token):
# response = api_client.get(api_routes.meal_plans_all, headers=token)
response = api_client.get(api_routes.meal_plans_all, headers=token)
# existing_mealplan = json.loads(response.text)
# existing_mealplan = existing_mealplan[0]
existing_mealplan = json.loads(response.text)
existing_mealplan = existing_mealplan[0]
# # Swap
# plan_uid = existing_mealplan.get("uid")
# existing_mealplan["meals"][0]["slug"] = slug_2
# existing_mealplan["meals"][1]["slug"] = slug_1
# Swap
plan_uid = existing_mealplan.get("uid")
existing_mealplan["planDays"][0]["meals"][0]["slug"] = slug_2
existing_mealplan["planDays"][1]["meals"][0]["slug"] = slug_1
# response = api_client.put(api_routes.meal_plans_plan_id(plan_uid), json=existing_mealplan, headers=token)
response = api_client.put(api_routes.meal_plans_plan_id(plan_uid), json=existing_mealplan, headers=token)
# assert response.status_code == 200
assert response.status_code == 200
# response = api_client.get(api_routes.meal_plans_all, headers=token)
# existing_mealplan = json.loads(response.text)
# existing_mealplan = existing_mealplan[0]
response = api_client.get(api_routes.meal_plans_all, headers=token)
existing_mealplan = json.loads(response.text)
existing_mealplan = existing_mealplan[0]
# assert existing_mealplan["meals"][0]["slug"] == slug_2
# assert existing_mealplan["meals"][1]["slug"] == slug_1
assert existing_mealplan["planDays"][0]["meals"][0]["slug"] == slug_2
assert existing_mealplan["planDays"][1]["meals"][0]["slug"] == slug_1
# def test_delete_mealplan(api_client: TestClient, api_routes: AppRoutes, token):
# response = api_client.get(api_routes.meal_plans_all, headers=token)
def test_delete_mealplan(api_client: TestClient, api_routes: AppRoutes, token):
response = api_client.get(api_routes.meal_plans_all, headers=token)
# assert response.status_code == 200
# existing_mealplan = json.loads(response.text)
# existing_mealplan = existing_mealplan[0]
assert response.status_code == 200
existing_mealplan = json.loads(response.text)
existing_mealplan = existing_mealplan[0]
# plan_uid = existing_mealplan.get("uid")
# response = api_client.delete(api_routes.meal_plans_plan_id(plan_uid), headers=token)
plan_uid = existing_mealplan.get("uid")
response = api_client.delete(api_routes.meal_plans_plan_id(plan_uid), headers=token)
# assert response.status_code == 200
assert response.status_code == 200

View file

@ -3,7 +3,6 @@ import json
import pytest
from fastapi.testclient import TestClient
from mealie.schema.settings import SiteSettings
from mealie.schema.theme import SiteTheme
from tests.app_routes import AppRoutes
@ -12,28 +11,6 @@ def default_settings():
return SiteSettings().dict(by_alias=True)
@pytest.fixture(scope="session")
def default_theme():
return SiteTheme(id=1).dict()
@pytest.fixture(scope="session")
def new_theme():
return {
"id": 3,
"name": "myTestTheme",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#43A047",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
}
def test_default_settings(api_client: TestClient, api_routes: AppRoutes, default_settings):
response = api_client.get(api_routes.site_settings)
@ -52,59 +29,3 @@ def test_update_settings(api_client: TestClient, api_routes: AppRoutes, default_
response = api_client.get(api_routes.site_settings)
assert json.loads(response.content) == default_settings
def test_default_theme(api_client: TestClient, api_routes: AppRoutes, default_theme):
response = api_client.get(api_routes.themes_id(1))
assert response.status_code == 200
assert json.loads(response.content) == default_theme
def test_create_theme(api_client: TestClient, api_routes: AppRoutes, new_theme, token):
response = api_client.post(api_routes.themes_create, json=new_theme, headers=token)
assert response.status_code == 201
response = api_client.get(api_routes.themes_id(new_theme.get("id")), headers=token)
assert response.status_code == 200
assert json.loads(response.content) == new_theme
def test_read_all_themes(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme):
response = api_client.get(api_routes.themes)
assert response.status_code == 200
response_dict = json.loads(response.content)
assert default_theme in response_dict
assert new_theme in response_dict
def test_read_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme):
for theme in [default_theme, new_theme]:
response = api_client.get(api_routes.themes_id(theme.get("id")))
assert response.status_code == 200
assert json.loads(response.content) == theme
def test_update_theme(api_client: TestClient, api_routes: AppRoutes, token, default_theme, new_theme):
theme_colors = {
"primary": "#E12345",
"accent": "#012345",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF4432",
}
new_theme["colors"] = theme_colors
response = api_client.put(api_routes.themes_id(new_theme.get("id")), json=new_theme, headers=token)
assert response.status_code == 200
response = api_client.get(api_routes.themes_id(new_theme.get("id")))
assert json.loads(response.content) == new_theme
def test_delete_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme, token):
for theme in [default_theme, new_theme]:
response = api_client.delete(api_routes.themes_id(theme.get("id")), headers=token)
assert response.status_code == 200

View file

@ -0,0 +1,85 @@
import json
import pytest
from fastapi.testclient import TestClient
from mealie.schema.theme import SiteTheme
from tests.app_routes import AppRoutes
@pytest.fixture(scope="session")
def default_theme():
return SiteTheme(id=1).dict()
@pytest.fixture(scope="session")
def new_theme():
return {
"id": 3,
"name": "myTestTheme",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#43A047",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
}
def test_default_theme(api_client: TestClient, api_routes: AppRoutes, default_theme):
response = api_client.get(api_routes.themes_id(1))
assert response.status_code == 200
assert json.loads(response.content) == default_theme
def test_create_theme(api_client: TestClient, api_routes: AppRoutes, new_theme, token):
response = api_client.post(api_routes.themes_create, json=new_theme, headers=token)
assert response.status_code == 201
response = api_client.get(api_routes.themes_id(new_theme.get("id")), headers=token)
assert response.status_code == 200
assert json.loads(response.content) == new_theme
def test_read_all_themes(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme):
response = api_client.get(api_routes.themes)
assert response.status_code == 200
response_dict = json.loads(response.content)
assert default_theme in response_dict
assert new_theme in response_dict
def test_read_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme):
for theme in [default_theme, new_theme]:
response = api_client.get(api_routes.themes_id(theme.get("id")))
assert response.status_code == 200
assert json.loads(response.content) == theme
def test_update_theme(api_client: TestClient, api_routes: AppRoutes, token, new_theme):
theme_colors = {
"primary": "#E12345",
"accent": "#012345",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF4432",
}
new_theme["colors"] = theme_colors
new_theme["name"] = "New Theme Name"
response = api_client.put(api_routes.themes_id(new_theme.get("id")), json=new_theme, headers=token)
assert response.status_code == 200
response = api_client.get(api_routes.themes_id(new_theme.get("id")))
assert json.loads(response.content) == new_theme
def test_delete_theme(api_client: TestClient, api_routes: AppRoutes, default_theme, new_theme, token):
for theme in [default_theme, new_theme]:
response = api_client.delete(api_routes.themes_id(theme.get("id")), headers=token)
assert response.status_code == 200

View file

@ -34,6 +34,13 @@ def new_user():
)
def test_failed_login(api_client: TestClient, api_routes: AppRoutes):
form_data = {"username": "changeme@email.com", "password": "WRONG_PASSWORD"}
response = api_client.post(api_routes.auth_token, form_data)
assert response.status_code == 401
def test_superuser_login(api_client: TestClient, api_routes: AppRoutes, token):
form_data = {"username": "changeme@email.com", "password": "MyPassword"}
response = api_client.post(api_routes.auth_token, form_data)

View file

@ -1,57 +1,17 @@
from dataclasses import dataclass
import pytest
from mealie.services.scraper import scraper
from tests.utils.recipe_data import RecipeSiteTestCase, get_recipe_test_cases
test_cases = get_recipe_test_cases()
"""
These tests are skipped by default and only really used when troubleshooting the parser
directly. If you are working on improve the parser you can add test cases to the `get_recipe_test_cases` function
and then use this test case by removing the `@pytest.mark.skip` and than testing your results.
"""
@dataclass
class RecipeSiteTestCase:
url: str
expected_slug: str
num_ingredients: int
num_steps: int
test_cases = [
RecipeSiteTestCase(
url="https://www.seriouseats.com/taiwanese-three-cup-chicken-san-bei-gi-recipe",
expected_slug="taiwanese-three-cup-chicken-san-bei-ji-recipe",
num_ingredients=10,
num_steps=3,
),
RecipeSiteTestCase(
url="https://www.rezeptwelt.de/backen-herzhaft-rezepte/schinken-kaese-waffeln-ohne-viel-schnickschnack/4j0bkiig-94d4d-106529-cfcd2-is97x2ml",
expected_slug="schinken-kase-waffeln-ohne-viel-schnickschnack",
num_ingredients=7,
num_steps=1, # Malformed JSON Data, can't parse steps just get one string
),
RecipeSiteTestCase(
url="https://cookpad.com/us/recipes/5544853-sous-vide-smoked-beef-ribs",
expected_slug="sous-vide-smoked-beef-ribs",
num_ingredients=7,
num_steps=12,
),
RecipeSiteTestCase(
url="https://www.greatbritishchefs.com/recipes/jam-roly-poly-recipe",
expected_slug="jam-roly-poly-with-custard",
num_ingredients=13,
num_steps=9,
),
RecipeSiteTestCase(
url="https://recipes.anovaculinary.com/recipe/sous-vide-shrimp",
expected_slug="sous-vide-shrimp",
num_ingredients=5,
num_steps=0,
),
RecipeSiteTestCase(
url="https://www.bonappetit.com/recipe/detroit-style-pepperoni-pizza",
expected_slug="detroit-style-pepperoni-pizza",
num_ingredients=8,
num_steps=5,
),
]
@pytest.mark.skip
@pytest.mark.parametrize("recipe_test_data", test_cases)
def test_recipe_parser(recipe_test_data: RecipeSiteTestCase):
recipe = scraper.create_from_url(recipe_test_data.url)

View file

@ -0,0 +1,11 @@
from pathlib import Path
from mealie.core import security
from mealie.routes.deps import validate_file_token
def test_create_file_token():
file_path = Path(__file__).parent
file_token = security.create_file_token(file_path)
assert file_path == validate_file_token(file_token)

3
tests/utils/helpers.py Normal file
View file

@ -0,0 +1,3 @@
class MatchAny:
def __eq__(self, _: object) -> bool:
return True

View file

@ -2,20 +2,50 @@ from dataclasses import dataclass
@dataclass
class RecipeTestData:
class RecipeSiteTestCase:
url: str
expected_slug: str
num_ingredients: int
num_steps: int
def build_recipe_store():
def get_recipe_test_cases():
return [
RecipeTestData(
url="https://www.bonappetit.com/recipe/spinach-thepla-and-vaghareli-dahi",
expected_slug="thepla-recipe-with-vaghareli-dahi",
RecipeSiteTestCase(
url="https://www.seriouseats.com/taiwanese-three-cup-chicken-san-bei-gi-recipe",
expected_slug="taiwanese-three-cup-chicken-san-bei-ji-recipe",
num_ingredients=10,
num_steps=3,
),
RecipeTestData(
url="https://www.bonappetit.com/recipe/classic-coleslaw",
expected_slug="traditional-coleslaw-recipe",
RecipeSiteTestCase(
url="https://www.rezeptwelt.de/backen-herzhaft-rezepte/schinken-kaese-waffeln-ohne-viel-schnickschnack/4j0bkiig-94d4d-106529-cfcd2-is97x2ml",
expected_slug="schinken-kase-waffeln-ohne-viel-schnickschnack",
num_ingredients=7,
num_steps=1, # Malformed JSON Data, can't parse steps just get one string
),
RecipeSiteTestCase(
url="https://cookpad.com/us/recipes/5544853-sous-vide-smoked-beef-ribs",
expected_slug="sous-vide-smoked-beef-ribs",
num_ingredients=7,
num_steps=12,
),
RecipeSiteTestCase(
url="https://www.greatbritishchefs.com/recipes/jam-roly-poly-recipe",
expected_slug="jam-roly-poly-with-custard",
num_ingredients=13,
num_steps=9,
),
RecipeSiteTestCase(
url="https://recipes.anovaculinary.com/recipe/sous-vide-shrimp",
expected_slug="sous-vide-shrimp",
num_ingredients=5,
num_steps=0,
),
RecipeSiteTestCase(
url="https://www.bonappetit.com/recipe/detroit-style-pepperoni-pizza",
expected_slug="detroit-style-pepperoni-pizza",
num_ingredients=8,
num_steps=5,
),
]