diff --git a/docs/docs/changelog/v0.5.0.md b/docs/docs/changelog/v0.5.0.md index 8d36d651f901..424aa68a4cea 100644 --- a/docs/docs/changelog/v0.5.0.md +++ b/docs/docs/changelog/v0.5.0.md @@ -116,6 +116,7 @@ - All images are now converted to .webp for better compression ### Behind the Scenes +- The database layer has been added for future recipe scaling. - Black and Flake8 now run as CI/CD checks - New debian based docker image - Unified Sidebar Components diff --git a/makefile b/makefile index 3c2dab99a930..df8d8524c81b 100644 --- a/makefile +++ b/makefile @@ -56,8 +56,8 @@ lint: ## ๐Ÿงบ Check style with flake8 coverage: ## โ˜‚๏ธ Check code coverage quickly with the default Python poetry run pytest - coverage report -m - coverage html + poetry run coverage report -m + poetry run coverage html $(BROWSER) htmlcov/index.html setup: ## ๐Ÿ— Setup Development Instance diff --git a/mealie/core/security.py b/mealie/core/security.py index a7a2d847c740..ea19c488ba57 100644 --- a/mealie/core/security.py +++ b/mealie/core/security.py @@ -34,7 +34,6 @@ def authenticate_user(session, email: str, password: str) -> UserInDB: if not user: return False - print(user) if not verify_password(password, user.password): return False return user diff --git a/mealie/db/database.py b/mealie/db/database.py index 07c76dde6c17..5cf374022fa3 100644 --- a/mealie/db/database.py +++ b/mealie/db/database.py @@ -19,8 +19,7 @@ from mealie.schema.comments import CommentOut from mealie.schema.event_notifications import EventNotificationIn from mealie.schema.events import Event as EventSchema from mealie.schema.meal import MealPlanOut -from mealie.schema.recipe import (Recipe, RecipeIngredientFood, - RecipeIngredientUnit) +from mealie.schema.recipe import Recipe, RecipeIngredientFood, RecipeIngredientUnit from mealie.schema.settings import CustomPageOut from mealie.schema.settings import SiteSettings as SiteSettingsSchema from mealie.schema.shopping_list import ShoppingListOut diff --git a/mealie/routes/deps.py b/mealie/routes/deps.py index 3d591834b7ab..333f9cac443d 100644 --- a/mealie/routes/deps.py +++ b/mealie/routes/deps.py @@ -85,7 +85,7 @@ def validate_long_live_token(session: Session, client_token: str, id: int) -> Us return token.user -async def validate_file_token(token: Optional[str] = None) -> Path: +def validate_file_token(token: Optional[str] = None) -> Path: credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="could not validate file token", diff --git a/mealie/services/scraper/cleaner.py b/mealie/services/scraper/cleaner.py index 401d12ca298b..78c73f4096b6 100644 --- a/mealie/services/scraper/cleaner.py +++ b/mealie/services/scraper/cleaner.py @@ -38,14 +38,14 @@ def clean(recipe_data: dict, url=None) -> dict: def clean_string(text: str) -> str: if text == "" or text is None: return "" - else: - cleaned_text = html.unescape(text) - cleaned_text = re.sub("<[^<]+?>", "", cleaned_text) - cleaned_text = re.sub(" +", " ", cleaned_text) - cleaned_text = re.sub("

", "\n", cleaned_text) - cleaned_text = re.sub(r"\n\s*\n", "\n\n", cleaned_text) - cleaned_text = cleaned_text.replace("\xa0", " ").replace("\t", " ").strip() - return cleaned_text + + cleaned_text = html.unescape(text) + cleaned_text = re.sub("<[^<]+?>", "", cleaned_text) + cleaned_text = re.sub(" +", " ", cleaned_text) + cleaned_text = re.sub("

", "\n", cleaned_text) + cleaned_text = re.sub(r"\n\s*\n", "\n\n", cleaned_text) + cleaned_text = cleaned_text.replace("\xa0", " ").replace("\t", " ").strip() + return cleaned_text def category(category: str): @@ -82,6 +82,10 @@ def instructions(instructions) -> List[dict]: if not instructions: return [] + # Dictionary (Keys: step number strings, Values: the instructions) + if isinstance(instructions, dict): + instructions = list(instructions.values()) + if isinstance(instructions, list) and isinstance(instructions[0], list): instructions = instructions[0] diff --git a/tests/conftest.py b/tests/conftest.py index 81301118885f..cace40945f98 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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() diff --git a/tests/integration_tests/recipe_tests/test_recipe_assets.py b/tests/integration_tests/recipe_tests/test_recipe_assets.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/integration_tests/test_recipe_routes.py b/tests/integration_tests/recipe_tests/test_recipe_crud.py similarity index 91% rename from tests/integration_tests/test_recipe_routes.py rename to tests/integration_tests/recipe_tests/test_recipe_crud.py index 9dccd699c00d..0cbaa774008f 100644 --- a/tests/integration_tests/test_recipe_routes.py +++ b/tests/integration_tests/recipe_tests/test_recipe_crud.py @@ -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 diff --git a/tests/integration_tests/test_meal_routes.py b/tests/integration_tests/test_meal_routes.py index 5a457c445651..a98f15910c12 100644 --- a/tests/integration_tests/test_meal_routes.py +++ b/tests/integration_tests/test_meal_routes.py @@ -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 diff --git a/tests/integration_tests/test_settings_routes.py b/tests/integration_tests/test_settings_routes.py index 0b68c588563d..f0b9df16ed46 100644 --- a/tests/integration_tests/test_settings_routes.py +++ b/tests/integration_tests/test_settings_routes.py @@ -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 diff --git a/tests/integration_tests/test_theme_routes.py b/tests/integration_tests/test_theme_routes.py new file mode 100644 index 000000000000..043e7a69af9a --- /dev/null +++ b/tests/integration_tests/test_theme_routes.py @@ -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 diff --git a/tests/integration_tests/test_user_routes.py b/tests/integration_tests/test_user_routes.py index b11b9f4ea801..b0cd7e4fda96 100644 --- a/tests/integration_tests/test_user_routes.py +++ b/tests/integration_tests/test_user_routes.py @@ -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) diff --git a/tests/unit_tests/test_recipe_parser.py b/tests/unit_tests/test_recipe_parser.py index 878f4b25c5c8..a6438ecf9378 100644 --- a/tests/unit_tests/test_recipe_parser.py +++ b/tests/unit_tests/test_recipe_parser.py @@ -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) diff --git a/tests/unit_tests/test_security.py b/tests/unit_tests/test_security.py new file mode 100644 index 000000000000..f5b71d77e026 --- /dev/null +++ b/tests/unit_tests/test_security.py @@ -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) diff --git a/tests/utils/helpers.py b/tests/utils/helpers.py new file mode 100644 index 000000000000..3a50ed95e07d --- /dev/null +++ b/tests/utils/helpers.py @@ -0,0 +1,3 @@ +class MatchAny: + def __eq__(self, _: object) -> bool: + return True diff --git a/tests/utils/recipe_data.py b/tests/utils/recipe_data.py index 32a472d7cc31..4cdf402dccc2 100644 --- a/tests/utils/recipe_data.py +++ b/tests/utils/recipe_data.py @@ -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, ), ]