mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-03 12:35:22 +02:00
feat: Add Households to Mealie (#3970)
This commit is contained in:
parent
0c29cef17d
commit
eb170cc7e5
315 changed files with 6975 additions and 3577 deletions
26
tests/fixtures/fixture_admin.py
vendored
26
tests/fixtures/fixture_admin.py
vendored
|
@ -1,7 +1,11 @@
|
|||
from uuid import UUID
|
||||
|
||||
from pytest import fixture
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from tests import utils
|
||||
from tests.utils import api_routes
|
||||
|
||||
|
@ -14,8 +18,8 @@ def admin_token(api_client: TestClient):
|
|||
return utils.login(form_data, api_client)
|
||||
|
||||
|
||||
@fixture(scope="session")
|
||||
def admin_user(api_client: TestClient):
|
||||
@fixture(scope="module")
|
||||
def admin_user(session: Session, api_client: TestClient):
|
||||
settings = get_app_settings()
|
||||
|
||||
form_data = {"username": settings._DEFAULT_EMAIL, "password": settings._DEFAULT_PASSWORD}
|
||||
|
@ -26,17 +30,27 @@ def admin_user(api_client: TestClient):
|
|||
assert token is not None
|
||||
|
||||
assert user_data.get("admin") is True
|
||||
assert user_data.get("groupId") is not None
|
||||
assert user_data.get("id") is not None
|
||||
assert (user_id := user_data.get("id")) is not None
|
||||
assert (group_id := user_data.get("groupId")) is not None
|
||||
assert (household_id := user_data.get("householdId")) is not None
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
try:
|
||||
yield utils.TestUser(
|
||||
_group_id=user_data.get("groupId"),
|
||||
user_id=user_data.get("id"),
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
user_id=user_id,
|
||||
password=settings._DEFAULT_PASSWORD,
|
||||
username=user_data.get("username"),
|
||||
email=user_data.get("email"),
|
||||
token=token,
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
finally:
|
||||
# TODO: Delete User after test
|
||||
|
|
19
tests/fixtures/fixture_database.py
vendored
19
tests/fixtures/fixture_database.py
vendored
|
@ -1,14 +1,21 @@
|
|||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from mealie.db.db_setup import SessionLocal
|
||||
from mealie.repos.all_repositories import AllRepositories, get_repositories
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def database() -> AllRepositories:
|
||||
@pytest.fixture(scope="module")
|
||||
def session() -> Generator[sessionmaker[Session], None, None]:
|
||||
try:
|
||||
db = SessionLocal()
|
||||
yield get_repositories(db)
|
||||
|
||||
sess = SessionLocal()
|
||||
yield sess
|
||||
finally:
|
||||
db.close()
|
||||
sess.close()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def unfiltered_database(session: Session) -> Generator[AllRepositories, None, None]:
|
||||
yield get_repositories(session, group_id=None, household_id=None)
|
||||
|
|
7
tests/fixtures/fixture_multitenant.py
vendored
7
tests/fixtures/fixture_multitenant.py
vendored
|
@ -2,6 +2,7 @@ from dataclasses import dataclass
|
|||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from tests import utils
|
||||
from tests.fixtures.fixture_users import build_unique_user
|
||||
|
@ -15,8 +16,8 @@ class MultiTenant:
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def multitenants(api_client: TestClient) -> MultiTenant:
|
||||
def multitenants(session: Session, api_client: TestClient) -> MultiTenant:
|
||||
yield MultiTenant(
|
||||
user_one=build_unique_user(random_string(12), api_client),
|
||||
user_two=build_unique_user(random_string(12), api_client),
|
||||
user_one=build_unique_user(session, random_string(12), api_client),
|
||||
user_two=build_unique_user(session, random_string(12), api_client),
|
||||
)
|
||||
|
|
10
tests/fixtures/fixture_recipe.py
vendored
10
tests/fixtures/fixture_recipe.py
vendored
|
@ -4,7 +4,6 @@ from collections.abc import Generator
|
|||
import sqlalchemy
|
||||
from pytest import fixture
|
||||
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.schema.recipe.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe_category import CategoryOut, CategorySave
|
||||
from mealie.schema.recipe.recipe_ingredient import RecipeIngredient
|
||||
|
@ -30,7 +29,8 @@ def recipe_store():
|
|||
|
||||
|
||||
@fixture(scope="function")
|
||||
def recipe_ingredient_only(database: AllRepositories, unique_user: TestUser):
|
||||
def recipe_ingredient_only(unique_user: TestUser):
|
||||
database = unique_user.repos
|
||||
# Create a recipe
|
||||
recipe = Recipe(
|
||||
user_id=unique_user.user_id,
|
||||
|
@ -55,7 +55,8 @@ def recipe_ingredient_only(database: AllRepositories, unique_user: TestUser):
|
|||
|
||||
|
||||
@fixture(scope="function")
|
||||
def recipe_categories(database: AllRepositories, unique_user: TestUser) -> Generator[list[CategoryOut], None, None]:
|
||||
def recipe_categories(unique_user: TestUser) -> Generator[list[CategoryOut], None, None]:
|
||||
database = unique_user.repos
|
||||
models: list[CategoryOut] = []
|
||||
for _ in range(3):
|
||||
category = CategorySave(
|
||||
|
@ -73,7 +74,8 @@ def recipe_categories(database: AllRepositories, unique_user: TestUser) -> Gener
|
|||
|
||||
|
||||
@fixture(scope="function")
|
||||
def random_recipe(database: AllRepositories, unique_user: TestUser) -> Generator[Recipe, None, None]:
|
||||
def random_recipe(unique_user: TestUser) -> Generator[Recipe, None, None]:
|
||||
database = unique_user.repos
|
||||
recipe = Recipe(
|
||||
user_id=unique_user.user_id,
|
||||
group_id=unique_user.group_id,
|
||||
|
|
30
tests/fixtures/fixture_shopping_lists.py
vendored
30
tests/fixtures/fixture_shopping_lists.py
vendored
|
@ -2,8 +2,7 @@ import pytest
|
|||
import sqlalchemy
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.schema.group.group_shopping_list import ShoppingListItemCreate, ShoppingListOut, ShoppingListSave
|
||||
from mealie.schema.household.group_shopping_list import ShoppingListItemCreate, ShoppingListOut, ShoppingListSave
|
||||
from tests.utils.factories import random_string
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
@ -24,12 +23,17 @@ def create_item(list_id: UUID4) -> dict:
|
|||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def shopping_lists(database: AllRepositories, unique_user: TestUser):
|
||||
def shopping_lists(unique_user: TestUser):
|
||||
database = unique_user.repos
|
||||
models: list[ShoppingListOut] = []
|
||||
|
||||
for _ in range(3):
|
||||
model = database.group_shopping_lists.create(
|
||||
ShoppingListSave(name=random_string(10), group_id=unique_user.group_id, user_id=unique_user.user_id),
|
||||
ShoppingListSave(
|
||||
name=random_string(10),
|
||||
group_id=unique_user.group_id,
|
||||
user_id=unique_user.user_id,
|
||||
),
|
||||
)
|
||||
|
||||
models.append(model)
|
||||
|
@ -44,9 +48,14 @@ def shopping_lists(database: AllRepositories, unique_user: TestUser):
|
|||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def shopping_list(database: AllRepositories, unique_user: TestUser):
|
||||
def shopping_list(unique_user: TestUser):
|
||||
database = unique_user.repos
|
||||
model = database.group_shopping_lists.create(
|
||||
ShoppingListSave(name=random_string(10), group_id=unique_user.group_id, user_id=unique_user.user_id),
|
||||
ShoppingListSave(
|
||||
name=random_string(10),
|
||||
group_id=unique_user.group_id,
|
||||
user_id=unique_user.user_id,
|
||||
),
|
||||
)
|
||||
|
||||
yield model
|
||||
|
@ -58,9 +67,14 @@ def shopping_list(database: AllRepositories, unique_user: TestUser):
|
|||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def list_with_items(database: AllRepositories, unique_user: TestUser):
|
||||
def list_with_items(unique_user: TestUser):
|
||||
database = unique_user.repos
|
||||
list_model = database.group_shopping_lists.create(
|
||||
ShoppingListSave(name=random_string(10), group_id=unique_user.group_id, user_id=unique_user.user_id),
|
||||
ShoppingListSave(
|
||||
name=random_string(10),
|
||||
group_id=unique_user.group_id,
|
||||
user_id=unique_user.user_id,
|
||||
),
|
||||
)
|
||||
|
||||
for _ in range(10):
|
||||
|
|
149
tests/fixtures/fixture_users.py
vendored
149
tests/fixtures/fixture_users.py
vendored
|
@ -1,7 +1,9 @@
|
|||
import json
|
||||
from collections.abc import Generator
|
||||
from uuid import UUID
|
||||
|
||||
from pytest import fixture
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from mealie.db.db_setup import session_context
|
||||
|
@ -12,7 +14,7 @@ from tests.utils import api_routes
|
|||
from tests.utils.factories import random_string
|
||||
|
||||
|
||||
def build_unique_user(group: str, api_client: TestClient) -> utils.TestUser:
|
||||
def build_unique_user(session: Session, group: str, api_client: TestClient) -> utils.TestUser:
|
||||
group = group or random_string(12)
|
||||
|
||||
registration = utils.user_registration_factory()
|
||||
|
@ -26,19 +28,98 @@ def build_unique_user(group: str, api_client: TestClient) -> utils.TestUser:
|
|||
user_data = api_client.get(api_routes.users_self, headers=token).json()
|
||||
assert token is not None
|
||||
|
||||
user_id = user_data.get("id")
|
||||
group_id = user_data.get("groupId")
|
||||
household_id = user_data.get("householdId")
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
return utils.TestUser(
|
||||
_group_id=user_data.get("groupId"),
|
||||
user_id=user_data.get("id"),
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
user_id=user_id,
|
||||
email=user_data.get("email"),
|
||||
username=user_data.get("username"),
|
||||
password=registration.password,
|
||||
token=token,
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
|
||||
|
||||
@fixture(scope="module")
|
||||
def g2_user(admin_token, api_client: TestClient):
|
||||
def h2_user(session: Session, admin_token, api_client: TestClient, unique_user: utils.TestUser):
|
||||
"""Another user in the same group as `unique_user`, but in a different household"""
|
||||
group = api_client.get(api_routes.groups_self, headers=unique_user.token).json()
|
||||
household_name = random_string(12)
|
||||
api_client.post(
|
||||
api_routes.admin_households,
|
||||
json={
|
||||
"name": household_name,
|
||||
"groupId": group["id"],
|
||||
},
|
||||
headers=admin_token,
|
||||
)
|
||||
|
||||
user_data = {
|
||||
"fullName": utils.random_string(),
|
||||
"username": utils.random_string(),
|
||||
"email": utils.random_email(),
|
||||
"password": "useruser",
|
||||
"group": group["name"],
|
||||
"household": household_name,
|
||||
"admin": False,
|
||||
"tokens": [],
|
||||
}
|
||||
response = api_client.post(api_routes.users, json=user_data, headers=admin_token)
|
||||
assert response.status_code == 201
|
||||
|
||||
# Log in as this user
|
||||
form_data = {"username": user_data["email"], "password": "useruser"}
|
||||
token = utils.login(form_data, api_client)
|
||||
|
||||
self_response = api_client.get(api_routes.users_self, headers=token)
|
||||
assert self_response.status_code == 200
|
||||
|
||||
data = json.loads(self_response.text)
|
||||
user_id = data["id"]
|
||||
household_id = data["householdId"]
|
||||
group_id = data["groupId"]
|
||||
assert user_id
|
||||
assert group_id
|
||||
assert household_id
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
try:
|
||||
yield utils.TestUser(
|
||||
user_id=user_id,
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
token=token,
|
||||
email=user_data["email"],
|
||||
username=user_data["username"],
|
||||
password=user_data["password"],
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
finally:
|
||||
# TODO: Delete User after test
|
||||
pass
|
||||
|
||||
|
||||
@fixture(scope="module")
|
||||
def g2_user(session: Session, admin_token, api_client: TestClient):
|
||||
group = random_string(12)
|
||||
|
||||
# Create the user
|
||||
create_data = {
|
||||
"fullName": utils.random_string(),
|
||||
|
@ -46,11 +127,12 @@ def g2_user(admin_token, api_client: TestClient):
|
|||
"email": utils.random_email(),
|
||||
"password": "useruser",
|
||||
"group": group,
|
||||
"household": "Family",
|
||||
"admin": False,
|
||||
"tokens": [],
|
||||
}
|
||||
|
||||
response = api_client.post(api_routes.admin_groups, json={"name": group}, headers=admin_token)
|
||||
api_client.post(api_routes.admin_groups, json={"name": group}, headers=admin_token)
|
||||
response = api_client.post(api_routes.users, json=create_data, headers=admin_token)
|
||||
|
||||
assert response.status_code == 201
|
||||
|
@ -66,15 +148,25 @@ def g2_user(admin_token, api_client: TestClient):
|
|||
|
||||
user_id = json.loads(self_response.text).get("id")
|
||||
group_id = json.loads(self_response.text).get("groupId")
|
||||
household_id = json.loads(self_response.text).get("householdId")
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
try:
|
||||
yield utils.TestUser(
|
||||
user_id=user_id,
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
token=token,
|
||||
email=create_data["email"], # type: ignore
|
||||
username=create_data.get("username"), # type: ignore
|
||||
password=create_data.get("password"), # type: ignore
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
finally:
|
||||
# TODO: Delete User after test
|
||||
|
@ -82,7 +174,7 @@ def g2_user(admin_token, api_client: TestClient):
|
|||
|
||||
|
||||
@fixture(scope="module")
|
||||
def unique_user(api_client: TestClient):
|
||||
def unique_user(session: Session, api_client: TestClient):
|
||||
registration = utils.user_registration_factory()
|
||||
response = api_client.post("/api/users/register", json=registration.model_dump(by_alias=True))
|
||||
assert response.status_code == 201
|
||||
|
@ -94,14 +186,27 @@ def unique_user(api_client: TestClient):
|
|||
user_data = api_client.get(api_routes.users_self, headers=token).json()
|
||||
assert token is not None
|
||||
|
||||
assert (user_id := user_data.get("id")) is not None
|
||||
assert (group_id := user_data.get("groupId")) is not None
|
||||
assert (household_id := user_data.get("householdId")) is not None
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
try:
|
||||
yield utils.TestUser(
|
||||
_group_id=user_data.get("groupId"),
|
||||
user_id=user_data.get("id"),
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
user_id=user_id,
|
||||
email=user_data.get("email"),
|
||||
username=user_data.get("username"),
|
||||
password=registration.password,
|
||||
token=token,
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
finally:
|
||||
# TODO: Delete User after test
|
||||
|
@ -109,8 +214,9 @@ def unique_user(api_client: TestClient):
|
|||
|
||||
|
||||
@fixture(scope="module")
|
||||
def user_tuple(admin_token, api_client: TestClient) -> Generator[list[utils.TestUser], None, None]:
|
||||
def user_tuple(session: Session, admin_token, api_client: TestClient) -> Generator[list[utils.TestUser], None, None]:
|
||||
group_name = utils.random_string()
|
||||
|
||||
# Create the user
|
||||
create_data_1 = {
|
||||
"fullName": utils.random_string(),
|
||||
|
@ -118,6 +224,7 @@ def user_tuple(admin_token, api_client: TestClient) -> Generator[list[utils.Test
|
|||
"email": utils.random_email(),
|
||||
"password": "useruser",
|
||||
"group": group_name,
|
||||
"household": "Family",
|
||||
"admin": False,
|
||||
"tokens": [],
|
||||
}
|
||||
|
@ -128,6 +235,7 @@ def user_tuple(admin_token, api_client: TestClient) -> Generator[list[utils.Test
|
|||
"email": utils.random_email(),
|
||||
"password": "useruser",
|
||||
"group": group_name,
|
||||
"household": "Family",
|
||||
"admin": False,
|
||||
"tokens": [],
|
||||
}
|
||||
|
@ -147,14 +255,27 @@ def user_tuple(admin_token, api_client: TestClient) -> Generator[list[utils.Test
|
|||
assert response.status_code == 200
|
||||
user_data = json.loads(response.text)
|
||||
|
||||
user_id = user_data.get("id")
|
||||
group_id = user_data.get("groupId")
|
||||
household_id = user_data.get("householdId")
|
||||
|
||||
if not isinstance(user_id, UUID):
|
||||
user_id = UUID(user_id)
|
||||
if not isinstance(group_id, UUID):
|
||||
group_id = UUID(group_id)
|
||||
if not isinstance(household_id, UUID):
|
||||
household_id = UUID(household_id)
|
||||
|
||||
users_out.append(
|
||||
utils.TestUser(
|
||||
_group_id=user_data.get("groupId"),
|
||||
user_id=user_data.get("id"),
|
||||
_group_id=group_id,
|
||||
_household_id=household_id,
|
||||
user_id=user_id,
|
||||
username=user_data.get("username"),
|
||||
email=user_data.get("email"),
|
||||
password="useruser",
|
||||
token=token,
|
||||
repos=get_repositories(session, group_id=group_id, household_id=household_id),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -164,7 +285,7 @@ def user_tuple(admin_token, api_client: TestClient) -> Generator[list[utils.Test
|
|||
pass
|
||||
|
||||
|
||||
@fixture(scope="session")
|
||||
@fixture(scope="module")
|
||||
def user_token(admin_token, api_client: TestClient):
|
||||
# Create the user
|
||||
create_data = {
|
||||
|
@ -191,7 +312,7 @@ def ldap_user():
|
|||
# Create an LDAP user directly instead of using TestClient since we don't have
|
||||
# a LDAP service set up
|
||||
with session_context() as session:
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
user = db.users.create(
|
||||
{
|
||||
"username": utils.random_string(10),
|
||||
|
@ -204,5 +325,5 @@ def ldap_user():
|
|||
)
|
||||
yield user
|
||||
with session_context() as session:
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
db.users.delete(user.id)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue