diff --git a/mealie/schema/cookbook/cookbook.py b/mealie/schema/cookbook/cookbook.py index 00ef3805e96..d7500566179 100644 --- a/mealie/schema/cookbook/cookbook.py +++ b/mealie/schema/cookbook/cookbook.py @@ -1,6 +1,7 @@ from typing import Annotated from pydantic import UUID4, ConfigDict, Field, field_validator +from slugify import slugify from sqlalchemy.orm import joinedload from sqlalchemy.orm.interfaces import LoaderOption @@ -29,6 +30,18 @@ class CreateCookBook(MealieModel): def validate_public(public: bool | None) -> bool: return False if public is None else public + @field_validator("name") + def validate_name(name: str) -> str: + name = name.strip() + + # we calculate the slug later leveraging the database, + # but we still need to validate the name can be slugified + possible_slug = slugify(name) + if not (name and possible_slug): + raise ValueError("Name cannot be empty") + + return name + class SaveCookBook(CreateCookBook): group_id: UUID4 diff --git a/tests/integration_tests/user_household_tests/test_group_cookbooks.py b/tests/integration_tests/user_household_tests/test_group_cookbooks.py index 11c2b64b1e2..7fafe11fff1 100644 --- a/tests/integration_tests/user_household_tests/test_group_cookbooks.py +++ b/tests/integration_tests/user_household_tests/test_group_cookbooks.py @@ -62,6 +62,22 @@ def test_create_cookbook(api_client: TestClient, unique_user: TestUser): assert response.status_code == 201 +@pytest.mark.parametrize("name_input", ["", " ", "@"]) +def test_create_cookbook_bad_name(api_client: TestClient, unique_user: TestUser, name_input: str): + data = { + "name": name_input, + "slug": name_input, + "description": "", + "position": 0, + "categories": [], + "group_id": str(unique_user.group_id), + "household_id": str(unique_user.household_id), + } + + response = api_client.post(api_routes.households_cookbooks, json=data, headers=unique_user.token) + assert response.status_code == 422 + + def test_read_cookbook(api_client: TestClient, unique_user: TestUser, cookbooks: list[TestCookbook]): sample = random.choice(cookbooks) response = api_client.get(api_routes.households_cookbooks_item_id(sample.id), headers=unique_user.token)