mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04:00 
			
		
		
		
	* fix #1144 * fix type checks * refactor test routes package * fix #1208 * unify test routes into module
This commit is contained in:
		| @@ -1,9 +1,9 @@ | ||||
| preserve_hierarchy: false | ||||
| files: | ||||
|   - source: /frontend/src/locales/messages/en-US.json | ||||
|     translation: /frontend/src/locales/messages/%locale%.json | ||||
|   - source: /frontend/src/locales/dateTimeFormats/en-US.json | ||||
|     translation: /frontend/src/locales/dateTimeFormats/%locale%.json | ||||
|   - source: /frontend/lang/messages/en-US.json | ||||
|     translation: /frontend/lang/messages/%locale%.json | ||||
|   - source: /frontend/lang/dateTimeFormats/en-US.json | ||||
|     translation: /frontend/lang/dateTimeFormats/%locale%.json | ||||
|  | ||||
|   # Backend General Messages | ||||
|   - source: /mealie/lang/messages/en-US.json | ||||
|   | ||||
| @@ -149,8 +149,8 @@ class RepositoryGeneric(Generic[T, D]): | ||||
|         if match_key is None: | ||||
|             match_key = self.primary_key | ||||
|  | ||||
|         filter = self._filter_builder(**{match_key: match_value}) | ||||
|         return self.session.query(self.sql_model).filter_by(**filter).one() | ||||
|         fltr = self._filter_builder(**{match_key: match_value}) | ||||
|         return self.session.query(self.sql_model).filter_by(**fltr).one() | ||||
|  | ||||
|     def get_one(self, value: str | int | UUID4, key: str = None, any_case=False, override_schema=None) -> T | None: | ||||
|         key = key or self.primary_key | ||||
| @@ -210,7 +210,7 @@ class RepositoryGeneric(Generic[T, D]): | ||||
|  | ||||
|         return [eff_schema.from_orm(x) for x in result] | ||||
|  | ||||
|     def create(self, document: T | BaseModel) -> T: | ||||
|     def create(self, document: T | BaseModel | dict) -> T: | ||||
|         """Creates a new database entry for the given SQL Alchemy Model. | ||||
|  | ||||
|         Args: | ||||
| @@ -266,8 +266,10 @@ class RepositoryGeneric(Generic[T, D]): | ||||
|  | ||||
|         return self.update(match_value, entry_as_dict) | ||||
|  | ||||
|     def delete(self, primary_key_value) -> D: | ||||
|         result = self.session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one() | ||||
|     def delete(self, value, match_key: str | None = None) -> T: | ||||
|         match_key = match_key or self.primary_key | ||||
|  | ||||
|         result = self.session.query(self.sql_model).filter_by(**{match_key: value}).one() | ||||
|         results_as_model = self.schema.from_orm(result) | ||||
|  | ||||
|         try: | ||||
| @@ -313,7 +315,7 @@ class RepositoryGeneric(Generic[T, D]): | ||||
|                 for x in self.session.query(self.sql_model).filter(attribute_name == attr_match).all()  # noqa: 711 | ||||
|             ] | ||||
|  | ||||
|     def create_many(self, documents: list[T]) -> list[T]: | ||||
|     def create_many(self, documents: list[T | dict]) -> list[T]: | ||||
|         new_documents = [] | ||||
|         for document in documents: | ||||
|             document = document if isinstance(document, dict) else document.dict() | ||||
|   | ||||
| @@ -2,6 +2,8 @@ import random | ||||
| import shutil | ||||
| from typing import Optional | ||||
|  | ||||
| from pydantic import UUID4 | ||||
|  | ||||
| from mealie.assets import users as users_assets | ||||
| from mealie.schema.user.user import PrivateUser, User | ||||
|  | ||||
| @@ -30,11 +32,11 @@ class RepositoryUsers(RepositoryGeneric[PrivateUser, User]): | ||||
|  | ||||
|         return new_user | ||||
|  | ||||
|     def delete(self, id: str) -> User: | ||||
|         entry = super().delete(id) | ||||
|     def delete(self, value: str | UUID4, match_key: str | None = None) -> User: | ||||
|         entry = super().delete(value, match_key) | ||||
|         # Delete the user's directory | ||||
|         shutil.rmtree(PrivateUser.get_directory(id)) | ||||
|         return entry | ||||
|         shutil.rmtree(PrivateUser.get_directory(value)) | ||||
|         return entry  # type: ignore | ||||
|  | ||||
|     def get_by_username(self, username: str, limit=1) -> Optional[User]: | ||||
|         dbuser = self.session.query(User).filter(User.username == username).one_or_none() | ||||
|   | ||||
| @@ -128,7 +128,7 @@ class PrivateUser(UserOut): | ||||
|         orm_mode = True | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_directory(user_id: UUID4) -> Path: | ||||
|     def get_directory(user_id: UUID4 | str) -> Path: | ||||
|         user_dir = get_app_dirs().USER_DIR / str(user_id) | ||||
|         user_dir.mkdir(parents=True, exist_ok=True) | ||||
|         return user_dir | ||||
|   | ||||
| @@ -178,7 +178,7 @@ class RecipeService(BaseService): | ||||
|     def delete_one(self, slug) -> Recipe: | ||||
|         recipe = self._get_recipe(slug) | ||||
|         self.can_update(recipe) | ||||
|         data = self.repos.recipes.delete(slug) | ||||
|         data = self.repos.recipes.delete(recipe.id, "id") | ||||
|         self.delete_assets(data) | ||||
|         return data | ||||
|  | ||||
|   | ||||
| @@ -27,7 +27,7 @@ def generate_create_data() -> dict: | ||||
| def test_init_superuser(api_client: TestClient, admin_user: TestUser): | ||||
|     settings = get_app_settings() | ||||
|  | ||||
|     response = api_client.get(routes.RoutesAdminUsers.item(admin_user.user_id), headers=admin_user.token) | ||||
|     response = api_client.get(routes.admin.AdminUsers.item(admin_user.user_id), headers=admin_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     admin_data = response.json() | ||||
| @@ -41,13 +41,13 @@ def test_init_superuser(api_client: TestClient, admin_user: TestUser): | ||||
|  | ||||
| def test_create_user(api_client: TestClient, api_routes: AppRoutes, admin_token): | ||||
|     create_data = generate_create_data() | ||||
|     response = api_client.post(routes.RoutesAdminUsers.base, json=create_data, headers=admin_token) | ||||
|     response = api_client.post(routes.admin.AdminUsers.base, json=create_data, headers=admin_token) | ||||
|     assert response.status_code == 201 | ||||
|  | ||||
|     form_data = {"username": create_data["email"], "password": create_data["password"]} | ||||
|     header = utils.login(form_data=form_data, api_client=api_client, api_routes=api_routes) | ||||
|  | ||||
|     response = api_client.get(routes.RoutesUsers.self, headers=header) | ||||
|     response = api_client.get(routes.user.Users.self, headers=header) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     user_data = response.json() | ||||
| @@ -60,14 +60,14 @@ def test_create_user(api_client: TestClient, api_routes: AppRoutes, admin_token) | ||||
|  | ||||
| def test_create_user_as_non_admin(api_client: TestClient, user_token): | ||||
|     create_data = generate_create_data() | ||||
|     response = api_client.post(routes.RoutesAdminUsers.base, json=create_data, headers=user_token) | ||||
|     response = api_client.post(routes.admin.AdminUsers.base, json=create_data, headers=user_token) | ||||
|     assert response.status_code == 403 | ||||
|  | ||||
|  | ||||
| def test_update_user(api_client: TestClient, admin_user: TestUser): | ||||
|     # Create a new user | ||||
|     create_data = generate_create_data() | ||||
|     response = api_client.post(routes.RoutesAdminUsers.base, json=create_data, headers=admin_user.token) | ||||
|     response = api_client.post(routes.admin.AdminUsers.base, json=create_data, headers=admin_user.token) | ||||
|     assert response.status_code == 201 | ||||
|     update_data = response.json() | ||||
|  | ||||
| @@ -76,7 +76,7 @@ def test_update_user(api_client: TestClient, admin_user: TestUser): | ||||
|     update_data["email"] = random_email() | ||||
|  | ||||
|     response = api_client.put( | ||||
|         routes.RoutesAdminUsers.item(update_data["id"]), headers=admin_user.token, json=update_data | ||||
|         routes.admin.AdminUsers.item(update_data["id"]), headers=admin_user.token, json=update_data | ||||
|     ) | ||||
|  | ||||
|     assert response.status_code == 200 | ||||
| @@ -93,21 +93,21 @@ def test_update_other_user_as_not_admin(api_client: TestClient, unique_user: Tes | ||||
|         "admin": True, | ||||
|     } | ||||
|     response = api_client.put( | ||||
|         routes.RoutesAdminUsers.item(g2_user.user_id), headers=unique_user.token, json=update_data | ||||
|         routes.admin.AdminUsers.item(g2_user.user_id), headers=unique_user.token, json=update_data | ||||
|     ) | ||||
|  | ||||
|     assert response.status_code == 403 | ||||
|  | ||||
|  | ||||
| def test_self_demote_admin(api_client: TestClient, admin_user: TestUser): | ||||
|     response = api_client.get(routes.RoutesUsers.self, headers=admin_user.token) | ||||
|     response = api_client.get(routes.user.Users.self, headers=admin_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     user_data = response.json() | ||||
|     user_data["admin"] = False | ||||
|  | ||||
|     response = api_client.put( | ||||
|         routes.RoutesAdminUsers.item(admin_user.user_id), headers=admin_user.token, json=user_data | ||||
|         routes.admin.AdminUsers.item(admin_user.user_id), headers=admin_user.token, json=user_data | ||||
|     ) | ||||
|  | ||||
|     assert response.status_code == 403 | ||||
| @@ -122,12 +122,12 @@ def test_self_promote_admin(api_client: TestClient, unique_user: TestUser): | ||||
|         "admin": True, | ||||
|     } | ||||
|     response = api_client.put( | ||||
|         routes.RoutesAdminUsers.item(unique_user.user_id), headers=unique_user.token, json=update_data | ||||
|         routes.admin.AdminUsers.item(unique_user.user_id), headers=unique_user.token, json=update_data | ||||
|     ) | ||||
|  | ||||
|     assert response.status_code == 403 | ||||
|  | ||||
|  | ||||
| def test_delete_user(api_client: TestClient, admin_token, unique_user: TestUser): | ||||
|     response = api_client.delete(routes.RoutesAdminUsers.item(unique_user.user_id), headers=admin_token) | ||||
|     response = api_client.delete(routes.admin.AdminUsers.item(unique_user.user_id), headers=admin_token) | ||||
|     assert response.status_code == 200 | ||||
|   | ||||
| @@ -14,9 +14,9 @@ test_ids = [ | ||||
| ] | ||||
|  | ||||
| organizer_routes = [ | ||||
|     (routes.RoutesCategory), | ||||
|     (routes.RoutesTags), | ||||
|     (routes.RoutesTools), | ||||
|     (routes.organizers.Categories), | ||||
|     (routes.organizers.Tags), | ||||
|     (routes.organizers.Tools), | ||||
| ] | ||||
|  | ||||
|  | ||||
| @@ -43,9 +43,9 @@ def test_organizers_create_read(api_client: TestClient, unique_user: TestUser, r | ||||
|  | ||||
|  | ||||
| update_data = [ | ||||
|     (routes.RoutesCategory, {"name": random_string(10)}), | ||||
|     (routes.RoutesTags, {"name": random_string(10)}), | ||||
|     (routes.RoutesTools, {"name": random_string(10), "onHand": random_bool()}), | ||||
|     (routes.organizers.Categories, {"name": random_string(10)}), | ||||
|     (routes.organizers.Tags, {"name": random_string(10)}), | ||||
|     (routes.organizers.Tools, {"name": random_string(10), "onHand": random_bool()}), | ||||
| ] | ||||
|  | ||||
|  | ||||
| @@ -101,9 +101,9 @@ def test_organizer_delete( | ||||
|  | ||||
|  | ||||
| association_data = [ | ||||
|     (routes.RoutesCategory, recipe_keys.recipe_category), | ||||
|     (routes.RoutesTags, "tags"), | ||||
|     (routes.RoutesTools, "tools"), | ||||
|     (routes.organizers.Categories, recipe_keys.recipe_category), | ||||
|     (routes.organizers.Tags, "tags"), | ||||
|     (routes.organizers.Tools, "tools"), | ||||
| ] | ||||
|  | ||||
|  | ||||
| @@ -123,28 +123,28 @@ def test_organizer_association( | ||||
|  | ||||
|     # Setup Recipe | ||||
|     recipe_data = {"name": random_string(10)} | ||||
|     response = api_client.post(routes.RoutesRecipe.base, json=recipe_data, headers=unique_user.token) | ||||
|     response = api_client.post(routes.recipes.Recipe.base, json=recipe_data, headers=unique_user.token) | ||||
|     slug = response.json() | ||||
|     assert response.status_code == 201 | ||||
|  | ||||
|     # Get Recipe Data | ||||
|     response = api_client.get(routes.RoutesRecipe.item(slug), headers=unique_user.token) | ||||
|     response = api_client.get(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     as_json = response.json() | ||||
|     as_json[recipe_key] = [ | ||||
|         {"id": item["id"], "group_id": unique_user.group_id, "name": item["name"], "slug": item["slug"]} | ||||
|     ] | ||||
|  | ||||
|     # Update Recipe | ||||
|     response = api_client.put(routes.RoutesRecipe.item(slug), json=as_json, headers=unique_user.token) | ||||
|     response = api_client.put(routes.recipes.Recipe.item(slug), json=as_json, headers=unique_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     # Get Recipe Data | ||||
|     response = api_client.get(routes.RoutesRecipe.item(slug), headers=unique_user.token) | ||||
|     response = api_client.get(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     as_json = response.json() | ||||
|     assert as_json[recipe_key][0]["slug"] == item["slug"] | ||||
|  | ||||
|     # Cleanup | ||||
|     response = api_client.delete(routes.RoutesRecipe.item(slug), headers=unique_user.token) | ||||
|     response = api_client.delete(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     response = api_client.delete(route.item(item["id"]), headers=unique_user.token) | ||||
| @@ -155,7 +155,7 @@ def test_organizer_association( | ||||
| def test_organizer_get_by_slug( | ||||
|     api_client: TestClient, | ||||
|     unique_user: TestUser, | ||||
|     route: routes.RoutesOrganizerBase, | ||||
|     route: routes.organizers.RoutesOrganizerBase, | ||||
|     recipe_key: str, | ||||
| ): | ||||
|     # Create Organizer | ||||
| @@ -170,20 +170,20 @@ def test_organizer_get_by_slug( | ||||
|     for _ in range(10): | ||||
|         # Setup Recipe | ||||
|         recipe_data = {"name": random_string(10)} | ||||
|         response = api_client.post(routes.RoutesRecipe.base, json=recipe_data, headers=unique_user.token) | ||||
|         response = api_client.post(routes.recipes.Recipe.base, json=recipe_data, headers=unique_user.token) | ||||
|         assert response.status_code == 201 | ||||
|         slug = response.json() | ||||
|         recipe_slugs.append(slug) | ||||
|  | ||||
|     # Associate 10 Recipes to Organizer | ||||
|     for slug in recipe_slugs: | ||||
|         response = api_client.get(routes.RoutesRecipe.item(slug), headers=unique_user.token) | ||||
|         response = api_client.get(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|         as_json = response.json() | ||||
|         as_json[recipe_key] = [ | ||||
|             {"id": item["id"], "group_id": unique_user.group_id, "name": item["name"], "slug": item["slug"]} | ||||
|         ] | ||||
|  | ||||
|         response = api_client.put(routes.RoutesRecipe.item(slug), json=as_json, headers=unique_user.token) | ||||
|         response = api_client.put(routes.recipes.Recipe.item(slug), json=as_json, headers=unique_user.token) | ||||
|         assert response.status_code == 200 | ||||
|  | ||||
|     # Get Organizer by Slug | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| from fastapi.testclient import TestClient | ||||
|  | ||||
| from mealie.repos.repository_factory import AllRepositories | ||||
| from tests.utils import routes | ||||
| from tests.utils.fixture_schemas import TestUser | ||||
| from tests.utils.routes import RoutesSeeders | ||||
|  | ||||
|  | ||||
| def test_seed_invalid_locale(api_client: TestClient, unique_user: TestUser): | ||||
|     for route in [RoutesSeeders.foods, RoutesSeeders.labels, RoutesSeeders.units]: | ||||
|     for route in [routes.seeders.Seeders.foods, routes.seeders.Seeders.labels, routes.seeders.Seeders.units]: | ||||
|         resp = api_client.post(route, json={"locale": "invalid"}, headers=unique_user.token) | ||||
|         assert resp.status_code == 422 | ||||
|  | ||||
| @@ -18,7 +18,7 @@ def test_seed_foods(api_client: TestClient, unique_user: TestUser, database: All | ||||
|     foods = database.ingredient_foods.by_group(unique_user.group_id).get_all() | ||||
|     assert len(foods) == 0 | ||||
|  | ||||
|     resp = api_client.post(RoutesSeeders.foods, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     resp = api_client.post(routes.seeders.Seeders.foods, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     assert resp.status_code == 200 | ||||
|  | ||||
|     # Check that the foods was created | ||||
| @@ -33,7 +33,7 @@ def test_seed_units(api_client: TestClient, unique_user: TestUser, database: All | ||||
|     units = database.ingredient_units.by_group(unique_user.group_id).get_all() | ||||
|     assert len(units) == 0 | ||||
|  | ||||
|     resp = api_client.post(RoutesSeeders.units, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     resp = api_client.post(routes.seeders.Seeders.units, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     assert resp.status_code == 200 | ||||
|  | ||||
|     # Check that the foods was created | ||||
| @@ -48,7 +48,7 @@ def test_seed_labels(api_client: TestClient, unique_user: TestUser, database: Al | ||||
|     labels = database.group_multi_purpose_labels.by_group(unique_user.group_id).get_all() | ||||
|     assert len(labels) == 0 | ||||
|  | ||||
|     resp = api_client.post(RoutesSeeders.labels, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     resp = api_client.post(routes.seeders.Seeders.labels, json={"locale": "en-US"}, headers=unique_user.token) | ||||
|     assert resp.status_code == 200 | ||||
|  | ||||
|     # Check that the foods was created | ||||
|   | ||||
| @@ -14,6 +14,7 @@ from mealie.schema.recipe.recipe import RecipeCategory | ||||
| from mealie.services.recipe.recipe_data_service import RecipeDataService | ||||
| from mealie.services.scraper.scraper_strategies import RecipeScraperOpenGraph | ||||
| from tests import data, utils | ||||
| from tests.utils import routes | ||||
| from tests.utils.app_routes import AppRoutes | ||||
| from tests.utils.factories import random_string | ||||
| from tests.utils.fixture_schemas import TestUser | ||||
| @@ -157,12 +158,11 @@ def test_create_by_url_with_tags( | ||||
| @pytest.mark.parametrize("recipe_data", recipe_test_data) | ||||
| def test_read_update( | ||||
|     api_client: TestClient, | ||||
|     api_routes: AppRoutes, | ||||
|     recipe_data: RecipeSiteTestCase, | ||||
|     unique_user: TestUser, | ||||
|     recipe_categories: list[RecipeCategory], | ||||
| ): | ||||
|     recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug) | ||||
|     recipe_url = routes.recipes.Recipe.item(recipe_data.expected_slug) | ||||
|     response = api_client.get(recipe_url, headers=unique_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
| @@ -196,8 +196,8 @@ def test_read_update( | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("recipe_data", recipe_test_data) | ||||
| def test_rename(api_client: TestClient, api_routes: AppRoutes, recipe_data: RecipeSiteTestCase, unique_user: TestUser): | ||||
|     recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug) | ||||
| def test_rename(api_client: TestClient, recipe_data: RecipeSiteTestCase, unique_user: TestUser): | ||||
|     recipe_url = routes.recipes.Recipe.item(recipe_data.expected_slug) | ||||
|     response = api_client.get(recipe_url, headers=unique_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
| @@ -215,44 +215,66 @@ 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: RecipeSiteTestCase, unique_user: TestUser): | ||||
|     recipe_url = api_routes.recipes_recipe_slug(recipe_data.expected_slug) | ||||
|     response = api_client.delete(recipe_url, headers=unique_user.token) | ||||
| def test_delete(api_client: TestClient, recipe_data: RecipeSiteTestCase, unique_user: TestUser): | ||||
|     response = api_client.delete(routes.recipes.Recipe.item(recipe_data.expected_slug), 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) | ||||
|     response = api_client.put(routes.recipes.Recipe.item("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) | ||||
|     response = api_client.get(routes.recipes.Recipe.item("test"), headers=unique_user.token) | ||||
|     assert response.status_code == 404 | ||||
|  | ||||
|     response = api_client.delete(api_routes.recipes_recipe_slug("test"), headers=unique_user.token) | ||||
|     response = api_client.delete(routes.recipes.Recipe.item("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 | ||||
|  | ||||
|  | ||||
| def test_create_recipe_same_name(api_client: TestClient, api_routes: AppRoutes, unique_user: TestUser): | ||||
| def test_create_recipe_same_name(api_client: TestClient, unique_user: TestUser): | ||||
|     slug = random_string(10) | ||||
|  | ||||
|     response = api_client.post("/api/recipes", json={"name": slug}, headers=unique_user.token) | ||||
|     response = api_client.post(routes.recipes.Recipe.base, json={"name": slug}, headers=unique_user.token) | ||||
|     assert response.status_code == 201 | ||||
|     assert json.loads(response.text) == slug | ||||
|  | ||||
|     response = api_client.post("/api/recipes", json={"name": slug}, headers=unique_user.token) | ||||
|     response = api_client.post(routes.recipes.Recipe.base, json={"name": slug}, headers=unique_user.token) | ||||
|     assert response.status_code == 201 | ||||
|     assert json.loads(response.text) == f"{slug}-1" | ||||
|  | ||||
|  | ||||
| def test_create_recipe_too_many_time(api_client: TestClient, api_routes: AppRoutes, unique_user: TestUser): | ||||
| def test_create_recipe_too_many_time(api_client: TestClient, unique_user: TestUser): | ||||
|     slug = random_string(10) | ||||
|  | ||||
|     for _ in range(10): | ||||
|         response = api_client.post("/api/recipes", json={"name": slug}, headers=unique_user.token) | ||||
|         response = api_client.post(routes.recipes.Recipe.base, json={"name": slug}, headers=unique_user.token) | ||||
|         assert response.status_code == 201 | ||||
|  | ||||
|     response = api_client.post("/api/recipes", json={"name": slug}, headers=unique_user.token) | ||||
|     response = api_client.post(routes.recipes.Recipe.base, json={"name": slug}, headers=unique_user.token) | ||||
|     assert response.status_code == 400 | ||||
|  | ||||
|  | ||||
| def test_delete_recipe_same_name(api_client: TestClient, unique_user: utils.TestUser, g2_user: utils.TestUser): | ||||
|     slug = random_string(10) | ||||
|  | ||||
|     # Create recipe for both users | ||||
|     for user in (unique_user, g2_user): | ||||
|         response = api_client.post(routes.recipes.Recipe.base, json={"name": slug}, headers=user.token) | ||||
|         assert response.status_code == 201 | ||||
|         assert json.loads(response.text) == slug | ||||
|  | ||||
|     # Delete recipe for user 1 | ||||
|     response = api_client.delete(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     # Ensure recipe for user 2 still exists | ||||
|     response = api_client.get(routes.recipes.Recipe.item(slug), headers=g2_user.token) | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     # Make sure recipe for user 1 doesn't exist | ||||
|     response = api_client.get(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     response = api_client.get(routes.recipes.Recipe.item(slug), headers=unique_user.token) | ||||
|     assert response.status_code == 404 | ||||
|   | ||||
| @@ -26,7 +26,7 @@ def test_associate_ingredient_with_step(api_client: TestClient, unique_user: Tes | ||||
|         steps[idx] = [str(ingredient.reference_id) for ingredient in ingredients] | ||||
|  | ||||
|     response = api_client.put( | ||||
|         routes.RoutesRecipe.item(recipe.slug), | ||||
|         routes.recipes.Recipe.item(recipe.slug), | ||||
|         json=jsonify(recipe.dict()), | ||||
|         headers=unique_user.token, | ||||
|     ) | ||||
| @@ -35,14 +35,14 @@ def test_associate_ingredient_with_step(api_client: TestClient, unique_user: Tes | ||||
|  | ||||
|     # Get Recipe and check that the ingredient is associated with the step | ||||
|  | ||||
|     response = api_client.get(routes.RoutesRecipe.item(recipe.slug), headers=unique_user.token) | ||||
|     response = api_client.get(routes.recipes.Recipe.item(recipe.slug), headers=unique_user.token) | ||||
|  | ||||
|     assert response.status_code == 200 | ||||
|  | ||||
|     recipe = json.loads(response.text) | ||||
|     data: dict = json.loads(response.text) | ||||
|  | ||||
|     for idx, step in enumerate(recipe.get("recipeInstructions")): | ||||
|         all_refs = [ref["referenceId"] for ref in step.get("ingredientReferences")] | ||||
|     for idx, stp in enumerate(data.get("recipeInstructions")): | ||||
|         all_refs = [ref["referenceId"] for ref in stp.get("ingredientReferences")] | ||||
|  | ||||
|         assert len(all_refs) == 2 | ||||
|  | ||||
|   | ||||
| @@ -10,8 +10,8 @@ from tests.utils import routes | ||||
| class CategoryTestCase(ABCMultiTenantTestCase): | ||||
|     items: list[RecipeCategory] | ||||
|  | ||||
|     def seed_action(self, group_id: str) -> set[int]: | ||||
|         category_ids: set[int] = set() | ||||
|     def seed_action(self, group_id: str) -> set[str]: | ||||
|         category_ids: set[str] = set() | ||||
|         for _ in range(10): | ||||
|             category = self.database.categories.create( | ||||
|                 CategorySave( | ||||
| @@ -26,8 +26,8 @@ class CategoryTestCase(ABCMultiTenantTestCase): | ||||
|         return category_ids | ||||
|  | ||||
|     def seed_multi(self, group1_id: str, group2_id: str) -> tuple[set[str], set[str]]: | ||||
|         g1_item_ids = set() | ||||
|         g2_item_ids = set() | ||||
|         g1_item_ids: set[str] = set() | ||||
|         g2_item_ids: set[str] = set() | ||||
|  | ||||
|         for group_id, item_ids in [(group1_id, g1_item_ids), (group2_id, g2_item_ids)]: | ||||
|             for _ in range(10): | ||||
| @@ -44,7 +44,7 @@ class CategoryTestCase(ABCMultiTenantTestCase): | ||||
|         return g1_item_ids, g2_item_ids | ||||
|  | ||||
|     def get_all(self, token: str) -> Response: | ||||
|         return self.client.get(routes.RoutesCategory.base, headers=token) | ||||
|         return self.client.get(routes.organizers.Categories.base, headers=token) | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         for item in self.items: | ||||
|   | ||||
| @@ -43,7 +43,7 @@ class FoodsTestCase(ABCMultiTenantTestCase): | ||||
|         return g1_item_ids, g2_item_ids | ||||
|  | ||||
|     def get_all(self, token: str) -> Response: | ||||
|         return self.client.get(routes.RoutesFoods.base, headers=token) | ||||
|         return self.client.get(routes.recipes.Foods.base, headers=token) | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         for item in self.items: | ||||
|   | ||||
| @@ -10,8 +10,8 @@ from tests.utils import routes | ||||
| class TagsTestCase(ABCMultiTenantTestCase): | ||||
|     items: list[RecipeTag] | ||||
|  | ||||
|     def seed_action(self, group_id: str) -> set[int]: | ||||
|         tag_ids: set[int] = set() | ||||
|     def seed_action(self, group_id: str) -> set[str]: | ||||
|         tag_ids: set[str] = set() | ||||
|         for _ in range(10): | ||||
|             tag = self.database.tags.create( | ||||
|                 TagSave( | ||||
| @@ -26,8 +26,8 @@ class TagsTestCase(ABCMultiTenantTestCase): | ||||
|         return tag_ids | ||||
|  | ||||
|     def seed_multi(self, group1_id: str, group2_id: str) -> tuple[set[str], set[str]]: | ||||
|         g1_item_ids = set() | ||||
|         g2_item_ids = set() | ||||
|         g1_item_ids: set[str] = set() | ||||
|         g2_item_ids: set[str] = set() | ||||
|  | ||||
|         for group_id, item_ids in [(group1_id, g1_item_ids), (group2_id, g2_item_ids)]: | ||||
|             for _ in range(10): | ||||
| @@ -44,7 +44,7 @@ class TagsTestCase(ABCMultiTenantTestCase): | ||||
|         return g1_item_ids, g2_item_ids | ||||
|  | ||||
|     def get_all(self, token: str) -> Response: | ||||
|         return self.client.get(routes.RoutesTags.base, headers=token) | ||||
|         return self.client.get(routes.organizers.Tags.base, headers=token) | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         for item in self.items: | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class ToolsTestCase(ABCMultiTenantTestCase): | ||||
|         return g1_item_ids, g2_item_ids | ||||
|  | ||||
|     def get_all(self, token: str) -> Response: | ||||
|         return self.client.get(routes.RoutesTools.base, headers=token) | ||||
|         return self.client.get(routes.organizers.Tools.base, headers=token) | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         for item in self.items: | ||||
|   | ||||
| @@ -9,8 +9,8 @@ from tests.utils import routes | ||||
| class UnitsTestCase(ABCMultiTenantTestCase): | ||||
|     items: list[IngredientUnit] | ||||
|  | ||||
|     def seed_action(self, group_id: str) -> set[int]: | ||||
|         unit_ids: set[int] = set() | ||||
|     def seed_action(self, group_id: str) -> set[str]: | ||||
|         unit_ids: set[str] = set() | ||||
|         for _ in range(10): | ||||
|             unit = self.database.ingredient_units.create( | ||||
|                 SaveIngredientUnit( | ||||
| @@ -25,8 +25,8 @@ class UnitsTestCase(ABCMultiTenantTestCase): | ||||
|         return unit_ids | ||||
|  | ||||
|     def seed_multi(self, group1_id: str, group2_id: str) -> tuple[set[str], set[str]]: | ||||
|         g1_item_ids = set() | ||||
|         g2_item_ids = set() | ||||
|         g1_item_ids: set[str] = set() | ||||
|         g2_item_ids: set[str] = set() | ||||
|  | ||||
|         for group_id, item_ids in [(group1_id, g1_item_ids), (group2_id, g2_item_ids)]: | ||||
|             for _ in range(10): | ||||
| @@ -43,7 +43,7 @@ class UnitsTestCase(ABCMultiTenantTestCase): | ||||
|         return g1_item_ids, g2_item_ids | ||||
|  | ||||
|     def get_all(self, token: str) -> Response: | ||||
|         return self.client.get(routes.RoutesUnits.base, headers=token) | ||||
|         return self.client.get(routes.recipes.Units.base, headers=token) | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         for item in self.items: | ||||
|   | ||||
| @@ -1,60 +0,0 @@ | ||||
| from pydantic import UUID4 | ||||
|  | ||||
|  | ||||
| class RoutesBase: | ||||
|     prefix = "/api" | ||||
|     base = f"{prefix}/" | ||||
|  | ||||
|     def __init__(self) -> None: | ||||
|         raise Exception("This class is not meant to be instantiated.") | ||||
|  | ||||
|     @classmethod | ||||
|     def item(cls, item_id: int | str | UUID4) -> str: | ||||
|         return f"{cls.base}/{item_id}" | ||||
|  | ||||
|  | ||||
| class RoutesFoods(RoutesBase): | ||||
|     base = "/api/foods" | ||||
|  | ||||
|  | ||||
| class RoutesUnits(RoutesBase): | ||||
|     base = "/api/units" | ||||
|  | ||||
|  | ||||
| class RoutesOrganizerBase(RoutesBase): | ||||
|     @classmethod | ||||
|     def slug(cls, slug: str) -> str: | ||||
|         return f"{cls.base}/slug/{slug}" | ||||
|  | ||||
|  | ||||
| class RoutesTools(RoutesOrganizerBase): | ||||
|     base = "/api/organizers/tools" | ||||
|  | ||||
|  | ||||
| class RoutesTags(RoutesOrganizerBase): | ||||
|     base = "/api/organizers/tags" | ||||
|  | ||||
|  | ||||
| class RoutesCategory(RoutesOrganizerBase): | ||||
|     base = "/api/organizers/categories" | ||||
|  | ||||
|  | ||||
| class RoutesRecipe(RoutesBase): | ||||
|     base = "/api/recipes" | ||||
|  | ||||
|  | ||||
| class RoutesAdminUsers(RoutesBase): | ||||
|     base = "/api/admin/users" | ||||
|  | ||||
|  | ||||
| class RoutesUsers(RoutesBase): | ||||
|     base = "/api/users" | ||||
|     self = f"{base}/self" | ||||
|  | ||||
|  | ||||
| class RoutesSeeders(RoutesBase): | ||||
|     base = "/api/groups/seeders" | ||||
|  | ||||
|     foods = f"{base}/foods" | ||||
|     units = f"{base}/units" | ||||
|     labels = f"{base}/labels" | ||||
							
								
								
									
										6
									
								
								tests/utils/routes/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/utils/routes/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| from . import routes_admin as admin | ||||
| from . import routes_organizers as organizers | ||||
| from . import routes_recipes as recipes | ||||
| from . import routes_seeders as seeders | ||||
| from . import routes_user as user | ||||
| from ._base import RoutesBase | ||||
							
								
								
									
										17
									
								
								tests/utils/routes/_base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/utils/routes/_base.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| from pydantic import UUID4 | ||||
|  | ||||
|  | ||||
| def v1(route: str) -> str: | ||||
|     return f"/api{route}" | ||||
|  | ||||
|  | ||||
| class RoutesBase: | ||||
|     prefix = "/api" | ||||
|     base = f"{prefix}/" | ||||
|  | ||||
|     def __init__(self) -> None: | ||||
|         raise NotImplementedError("This class is not meant to be instantiated.") | ||||
|  | ||||
|     @classmethod | ||||
|     def item(cls, item_id: int | str | UUID4) -> str: | ||||
|         return f"{cls.base}/{item_id}" | ||||
							
								
								
									
										5
									
								
								tests/utils/routes/routes_admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/utils/routes/routes_admin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| from ._base import RoutesBase, v1 | ||||
|  | ||||
|  | ||||
| class AdminUsers(RoutesBase): | ||||
|     base = v1("/admin/users") | ||||
							
								
								
									
										19
									
								
								tests/utils/routes/routes_organizers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/utils/routes/routes_organizers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| from ._base import RoutesBase, v1 | ||||
|  | ||||
|  | ||||
| class RoutesOrganizerBase(RoutesBase): | ||||
|     @classmethod | ||||
|     def slug(cls, slug: str) -> str: | ||||
|         return f"{cls.base}/slug/{slug}" | ||||
|  | ||||
|  | ||||
| class Tools(RoutesOrganizerBase): | ||||
|     base = v1("/organizers/tools") | ||||
|  | ||||
|  | ||||
| class Tags(RoutesOrganizerBase): | ||||
|     base = v1("/organizers/tags") | ||||
|  | ||||
|  | ||||
| class Categories(RoutesOrganizerBase): | ||||
|     base = v1("/organizers/categories") | ||||
							
								
								
									
										13
									
								
								tests/utils/routes/routes_recipes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/utils/routes/routes_recipes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| from ._base import RoutesBase, v1 | ||||
|  | ||||
|  | ||||
| class Foods(RoutesBase): | ||||
|     base = v1("/foods") | ||||
|  | ||||
|  | ||||
| class Units(RoutesBase): | ||||
|     base = v1("/units") | ||||
|  | ||||
|  | ||||
| class Recipe(RoutesBase): | ||||
|     base = v1("/recipes") | ||||
							
								
								
									
										9
									
								
								tests/utils/routes/routes_seeders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/utils/routes/routes_seeders.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| from ._base import RoutesBase, v1 | ||||
|  | ||||
|  | ||||
| class Seeders(RoutesBase): | ||||
|     base = v1("/groups/seeders") | ||||
|  | ||||
|     foods = f"{base}/foods" | ||||
|     units = f"{base}/units" | ||||
|     labels = f"{base}/labels" | ||||
							
								
								
									
										6
									
								
								tests/utils/routes/routes_user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/utils/routes/routes_user.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| from ._base import RoutesBase, v1 | ||||
|  | ||||
|  | ||||
| class Users(RoutesBase): | ||||
|     base = v1("/users") | ||||
|     self = f"{base}/self" | ||||
		Reference in New Issue
	
	Block a user