Рефакт. Добавил слой сервис, подготовил к кэшированию

develop
Сергей Ванюшкин 2024-02-02 23:38:12 +03:00
parent b223053cf6
commit 0ba422397a
24 changed files with 1127 additions and 723 deletions

BIN
dump.rdb

Binary file not shown.

View File

@ -4,3 +4,4 @@ POSTGRES_USER=testuser
POSTGRES_PASSWORD=test
POSTGRES_DB=fastfood_db
POSTGRES_DB_TEST=testdb
REDIS_DB=redis://localhost

View File

@ -1,5 +1,4 @@
from fastapi import FastAPI, Request
from starlette.responses import JSONResponse
from fastapi import FastAPI
from fastfood.routers.dish import router as dish_router
from fastfood.routers.menu import router as menu_router

View File

@ -8,6 +8,7 @@ class Settings(BaseSettings):
POSTGRES_PASSWORD: str = ""
POSTGRES_USER: str = ""
POSTGRES_DB_TEST: str = ""
REDIS_DB: str = ""
@property
def DATABASE_URL_asyncpg(self):

View File

@ -1,64 +0,0 @@
from uuid import UUID
from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import models, schemas
class DishCrud:
@staticmethod
async def get_dishes(submenu_id: UUID, session: AsyncSession):
async with session:
query = select(models.Dish).where(models.Dish.parent_submenu == submenu_id)
dishes = await session.execute(query)
return dishes.scalars().all()
@staticmethod
async def create_dish_item(
submenu_id: UUID,
dish: schemas.DishBase,
session: AsyncSession,
):
async with session:
new_dish = models.Dish(**dish.model_dump())
new_dish.parent_submenu = submenu_id
session.add(new_dish)
await session.flush()
await session.commit()
return new_dish
@staticmethod
async def get_dish_item(
dish_id: UUID,
session: AsyncSession,
):
async with session:
query = select(models.Dish).where(models.Dish.id == dish_id)
submenu = await session.execute(query)
return submenu.scalars().one_or_none()
@staticmethod
async def update_dish_item(
dish_id: UUID,
dish: schemas.DishBase,
session: AsyncSession,
):
async with session:
query = (
update(models.Dish)
.where(models.Dish.id == dish_id)
.values(**dish.model_dump())
)
await session.execute(query)
await session.commit()
qr = select(models.Dish).where(models.Dish.id == dish_id)
updated_submenu = await session.execute(qr)
return updated_submenu.scalars().one()
@staticmethod
async def delete_dish_item(dish_id: UUID, session: AsyncSession):
async with session:
query = delete(models.Dish).where(models.Dish.id == dish_id)
await session.execute(query)
await session.commit()

View File

@ -1,59 +0,0 @@
import pickle
from typing import Any
import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends
def get_async_redis_pool():
redis_url = "redis://localhost"
return redis.from_url(redis_url, decode_responses=False)
async def get_async_redis_client(
redis_pool: redis.Redis = Depends(get_async_redis_pool),
):
return redis_pool
class AsyncRedisCache:
def __init__(self, redis_pool: redis.Redis = Depends(get_async_redis_pool)) -> None:
self.redis_pool = redis_pool
self.ttl = 1800
async def get(self, key: str) -> Any | None:
data = await self.redis_pool.get(key)
if data is not None:
return pickle.loads(data)
return None
async def set(
self, key: str, value: Any, background_tasks: BackgroundTasks
) -> None:
data = pickle.dumps(value)
background_tasks.add_task(self._set_cache, key, data)
async def _set_cache(self, key: str, data: Any) -> None:
await self.redis_pool.setex(key, self.ttl, data)
async def delete(self, key: str, background_tasks: BackgroundTasks) -> None:
background_tasks.add_task(self._delete_cache, key)
async def _delete_cache(self, key: str) -> None:
await self.redis_pool.delete(key)
async def clear_cache(
self, pattern: str, background_tasks: BackgroundTasks
) -> None:
keys = [key async for key in self.redis_pool.scan_iter(pattern)]
if keys:
background_tasks.add_task(self._clear_keys, keys)
async def _clear_keys(self, keys: list[str]) -> None:
await self.redis_pool.delete(*keys)
async def clear_after_change(
self, menu_id: int | str, background_tasks: BackgroundTasks
) -> None:
await self.clear_cache(f"{str(menu_id)}*", background_tasks)
await self.clear_cache("all*", background_tasks)

View File

@ -1,80 +0,0 @@
from uuid import UUID
from sqlalchemy import delete, distinct, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased
from fastfood import models, schemas
class SubMenuCrud:
@staticmethod
async def get_submenus(menu_id: UUID, session: AsyncSession):
async with session:
query = select(models.SubMenu).where(
models.SubMenu.parent_menu == menu_id,
)
submenus = await session.execute(query)
return submenus
@staticmethod
async def create_submenu_item(
menu_id: UUID,
submenu: schemas.MenuBase,
session: AsyncSession,
):
async with session:
new_submenu = models.SubMenu(**submenu.model_dump())
new_submenu.parent_menu = menu_id
session.add(new_submenu)
await session.commit()
await session.refresh(new_submenu)
return new_submenu
@staticmethod
async def get_submenu_item(
menu_id: UUID,
submenu_id: UUID,
session: AsyncSession,
):
async with session:
s = aliased(models.SubMenu)
d = aliased(models.Dish)
query = (
select(s, func.count(distinct(d.id)))
.join(d, s.id == d.parent_submenu, isouter=True)
.group_by(s.id)
.where(s.id == submenu_id)
)
submenu = await session.execute(query)
submenu = submenu.scalars().one_or_none()
if submenu is None:
return None
return submenu
@staticmethod
async def update_submenu_item(
submenu_id: UUID,
submenu: schemas.MenuBase,
session: AsyncSession,
):
async with session:
query = (
update(models.SubMenu)
.where(models.SubMenu.id == submenu_id)
.values(**submenu.model_dump())
)
await session.execute(query)
await session.commit()
qr = select(models.SubMenu).where(models.SubMenu.id == submenu_id)
updated_submenu = await session.execute(qr)
return updated_submenu
@staticmethod
async def delete_submenu_item(submenu_id: UUID, session: AsyncSession):
async with session:
query = delete(models.SubMenu).where(
models.SubMenu.id == submenu_id,
)
await session.execute(query)
await session.commit()

View File

@ -1,7 +1,8 @@
from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker,
create_async_engine)
import redis.asyncio as redis
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from fastfood.config import settings
@ -16,3 +17,13 @@ async_session_maker = async_sessionmaker(
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
yield session
def get_redis_pool():
return redis.from_url(settings.REDIS_DB, decode_responses=False)
async def get_async_redis_client(
redis_pool: redis.Redis = Depends(get_redis_pool),
):
return redis_pool

View File

@ -1,9 +1,9 @@
from fastfood import models
from fastfood.dbase import async_engine
from .dish import DishCrud
from .menu import MenuCrud
from .submenu import SubMenuCrud
from .dish import DishRepository
from .menu import MenuRepository
from .submenu import SubMenuRepository
async def create_db_and_tables():
@ -12,8 +12,8 @@ async def create_db_and_tables():
await conn.run_sync(models.Base.metadata.create_all)
class Crud(MenuCrud, SubMenuCrud, DishCrud):
class Repository(MenuRepository, SubMenuRepository, DishRepository):
pass
crud = Crud()
ropo = Repository()

View File

@ -0,0 +1,72 @@
from uuid import UUID
from fastapi import Depends
from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import models
from fastfood.dbase import get_async_session
from fastfood.schemas import DishBase
class DishRepository:
def __init__(self, session: AsyncSession = Depends(get_async_session)):
self.db = session
async def get_dishes(self, menu_id: UUID, submenu_id: UUID):
query = select(models.Dish).where(
models.Dish.parent_submenu == submenu_id,
)
dishes = await self.db.execute(query)
return dishes.scalars().all()
async def create_dish_item(
self,
menu_id: UUID,
submenu_id: UUID,
dish_data: DishBase,
):
new_dish = models.Dish(**dish_data.model_dump())
new_dish.parent_submenu = submenu_id
self.db.add(new_dish)
await self.db.flush()
await self.db.commit()
return new_dish
async def get_dish_item(
self,
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
):
query = select(models.Dish).where(models.Dish.id == dish_id)
submenu = await self.db.execute(query)
return submenu.scalars().one_or_none()
async def update_dish_item(
self,
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
dish_data: DishBase,
):
query = (
update(models.Dish)
.where(models.Dish.id == dish_id)
.values(**dish_data.model_dump())
)
await self.db.execute(query)
await self.db.commit()
qr = select(models.Dish).where(models.Dish.id == dish_id)
updated_submenu = await self.db.execute(qr)
return updated_submenu.scalars().one()
async def delete_dish_item(
self,
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
):
query = delete(models.Dish).where(models.Dish.id == dish_id)
await self.db.execute(query)
await self.db.commit()

View File

@ -1,4 +1,3 @@
from typing import Annotated
from uuid import UUID
from fastapi import Depends
@ -10,7 +9,7 @@ from fastfood import models, schemas
from fastfood.dbase import get_async_session
class MenuCrud:
class MenuRepository:
def __init__(self, session: AsyncSession = Depends(get_async_session)):
self.db = session

View File

@ -0,0 +1,24 @@
from typing import Any
import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends
from fastfood.dbase import get_redis_pool
class RedisRepository:
def __init__(
self,
redis_pool: redis.Redis = Depends(get_redis_pool),
) -> None:
self.redis_pool = redis_pool
self.ttl = 1800
async def get(self, key: str) -> Any | None:
return None
async def set(
self, key: str, value: Any, background_tasks: BackgroundTasks
) -> None:
pass

View File

@ -0,0 +1,76 @@
from uuid import UUID
from fastapi import Depends
from sqlalchemy import delete, distinct, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import aliased
from fastfood import models, schemas
from fastfood.dbase import get_async_session
class SubMenuRepository:
def __init__(self, session: AsyncSession = Depends(get_async_session)):
self.db = session
async def get_submenus(self, menu_id: UUID):
query = select(models.SubMenu).where(
models.SubMenu.parent_menu == menu_id,
)
submenus = await self.db.execute(query)
return submenus
async def create_submenu_item(
self,
menu_id: UUID,
submenu: schemas.MenuBase,
):
new_submenu = models.SubMenu(**submenu.model_dump())
new_submenu.parent_menu = menu_id
self.db.add(new_submenu)
await self.db.commit()
await self.db.refresh(new_submenu)
return new_submenu
async def get_submenu_item(
self,
menu_id: UUID,
submenu_id: UUID,
):
s = aliased(models.SubMenu)
d = aliased(models.Dish)
query = (
select(s, func.count(distinct(d.id)).label("dishes_count"))
.join(d, s.id == d.parent_submenu, isouter=True)
.group_by(s.id)
.where(s.id == submenu_id)
)
submenu = await self.db.execute(query)
submenu = submenu.scalars().one_or_none()
if submenu is None:
return None
return submenu
async def update_submenu_item(
self,
menu_id: UUID,
submenu_id: UUID,
submenu_data: schemas.MenuBase,
):
query = (
update(models.SubMenu)
.where(models.SubMenu.id == submenu_id)
.values(**submenu_data.model_dump())
)
await self.db.execute(query)
await self.db.commit()
qr = select(models.SubMenu).where(models.SubMenu.id == submenu_id)
updated_submenu = await self.db.execute(qr)
return updated_submenu
async def delete_submenu_item(self, menu_id: UUID, submenu_id: UUID):
query = delete(models.SubMenu).where(
models.SubMenu.id == submenu_id,
)
await self.db.execute(query)
await self.db.commit()

View File

@ -1,12 +1,9 @@
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from fastfood import schemas
from fastfood.cruds import crud
from fastfood.dbase import get_async_session
from fastfood.service.dish import DishService
from fastfood.utils import price_converter
router = APIRouter(
@ -17,9 +14,12 @@ router = APIRouter(
@router.get("/")
async def get_dishes(
menu_id: UUID, submenu_id: UUID, session: AsyncSession = Depends(get_async_session)
menu_id: UUID,
submenu_id: UUID,
dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.get_dishes(submenu_id=submenu_id, session=session)
result = await dish.read_dishes(menu_id, submenu_id)
return result
@ -27,13 +27,14 @@ async def get_dishes(
async def create_dish(
menu_id: UUID,
submenu_id: UUID,
dish: schemas.DishBase,
session: AsyncSession = Depends(get_async_session),
dish_data: schemas.DishBase,
dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.create_dish_item(
submenu_id=submenu_id,
dish=dish,
session=session,
result = await dish.create_dish(
menu_id,
submenu_id,
dish_data,
)
return price_converter(result)
@ -43,11 +44,13 @@ async def get_dish(
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
session: AsyncSession = Depends(get_async_session),
dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.get_dish_item(
dish_id=dish_id,
session=session,
result = await dish.read_dish(
menu_id,
submenu_id,
dish_id,
)
if not result:
raise HTTPException(status_code=404, detail="dish not found")
@ -59,13 +62,15 @@ async def update_dish(
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
dish: schemas.DishBase,
session: AsyncSession = Depends(get_async_session),
dish_data: schemas.DishBase,
dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.update_dish_item(
dish_id=dish_id,
dish=dish,
session=session,
result = await dish.update_dish(
menu_id,
submenu_id,
dish_id,
dish_data,
)
return price_converter(result)
@ -75,6 +80,7 @@ async def delete_dish(
menu_id: UUID,
submenu_id: UUID,
dish_id: UUID,
session: AsyncSession = Depends(get_async_session),
dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
await crud.delete_dish_item(dish_id=dish_id, session=session)
await dish.del_dish(menu_id, submenu_id, dish_id)

View File

@ -2,11 +2,8 @@ from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import schemas
from fastfood.cruds import crud
from fastfood.dbase import get_async_session
from fastfood.schemas import Menu, MenuBase, MenuRead
from fastfood.service.menu import MenuService
router = APIRouter(
@ -15,7 +12,7 @@ router = APIRouter(
)
@router.get("/", response_model=Optional[List[schemas.Menu]])
@router.get("/", response_model=Optional[List[Menu]])
async def get_menus(
menu: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
@ -23,16 +20,16 @@ async def get_menus(
return await menu.read_menus()
@router.post("/", status_code=201, response_model=schemas.Menu)
@router.post("/", status_code=201, response_model=Menu)
async def add_menu(
menu: schemas.MenuBase,
menu: MenuBase,
responce: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
return await responce.create_menu(menu)
@router.get("/{menu_id}", response_model=schemas.MenuRead)
@router.get("/{menu_id}", response_model=MenuRead)
async def get_menu(
menu_id: UUID,
responce: MenuService = Depends(),
@ -45,10 +42,10 @@ async def get_menu(
return result
@router.patch("/{menu_id}", response_model=schemas.MenuBase)
@router.patch("/{menu_id}", response_model=MenuRead)
async def update_menu(
menu_id: UUID,
menu: schemas.MenuBase,
menu: MenuBase,
responce: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
@ -59,10 +56,10 @@ async def update_menu(
return result.scalars().one()
#
# @router.delete("/{menu_id}")
# async def delete_menu(
# menu_id: UUID,
# session: AsyncSession = Depends(get_async_session),
# ):
# await crud.delete_menu_item(menu_id=menu_id, session=session)
@router.delete("/{menu_id}")
async def delete_menu(
menu_id: UUID,
menu: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
await menu.del_menu(menu_id)

View File

@ -1,11 +1,10 @@
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from fastfood import schemas
from fastfood.cruds import crud
from fastfood.dbase import get_async_session
from fastfood.schemas import MenuBase, SubMenuRead
from fastfood.service.submenu import SubmenuService
router = APIRouter(
prefix="/api/v1/menus/{menu_id}/submenus",
@ -13,38 +12,40 @@ router = APIRouter(
)
@router.get("/")
@router.get("/", response_model=Optional[List[SubMenuRead]])
async def get_submenus(
menu_id: UUID, session: AsyncSession = Depends(get_async_session)
menu_id: UUID,
submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.get_submenus(menu_id=menu_id, session=session)
result = await submenu.read_submenus(menu_id=menu_id)
return result.scalars().all()
@router.post("/", status_code=201)
@router.post("/", status_code=201, response_model=SubMenuRead)
async def create_submenu_item(
menu_id: UUID,
submenu: schemas.MenuBase,
session: AsyncSession = Depends(get_async_session),
submenu_data: MenuBase,
submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.create_submenu_item(
result = await submenu.create_submenu(
menu_id=menu_id,
submenu=submenu,
session=session,
submenu_data=submenu_data,
)
return result
@router.get("/{submenu_id}", response_model=schemas.SubMenuRead)
@router.get("/{submenu_id}", response_model=SubMenuRead)
async def get_submenu(
menu_id: UUID,
submenu_id: UUID,
session: AsyncSession = Depends(get_async_session),
submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.get_submenu_item(
result = await submenu.read_menu(
menu_id=menu_id,
submenu_id=submenu_id,
session=session,
)
if not result:
raise HTTPException(status_code=404, detail="submenu not found")
@ -53,24 +54,28 @@ async def get_submenu(
@router.patch(
"/{submenu_id}",
response_model=schemas.MenuBase,
response_model=MenuBase,
)
async def update_submenu(
menu_id: UUID,
submenu_id: UUID,
submenu: schemas.MenuBase,
session: AsyncSession = Depends(get_async_session),
submenu_data: MenuBase,
submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
result = await crud.update_submenu_item(
result = await submenu.update_submenu(
menu_id=menu_id,
submenu_id=submenu_id,
submenu=submenu,
session=session,
submenu_data=submenu_data,
)
return result.scalars().one()
@router.delete("/{submenu_id}")
async def delete_submenu(
menu_id: UUID, submenu_id: UUID, session: AsyncSession = Depends(get_async_session)
menu_id: UUID,
submenu_id: UUID,
submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
):
await crud.delete_submenu_item(submenu_id=submenu_id, session=session)
await submenu.del_menu(menu_id=menu_id, submenu_id=submenu_id)

58
fastfood/service/dish.py Normal file
View File

@ -0,0 +1,58 @@
from uuid import UUID
import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends
from fastfood.dbase import get_async_redis_client
from fastfood.repository.dish import DishRepository
from fastfood.repository.redis import RedisRepository
from fastfood.schemas import DishBase
class DishService:
def __init__(
self,
dish_repo: DishRepository = Depends(),
redis_client: redis.Redis = Depends(get_async_redis_client),
background_tasks: BackgroundTasks = None,
):
self.dish_repo = dish_repo
self.cache_client = RedisRepository(redis_client)
self.background_tasks = background_tasks
async def read_dishes(self, menu_id: UUID, submenu_id: UUID):
data = await self.dish_repo.get_dishes(menu_id, submenu_id)
return data
async def create_dish(
self,
menu_id: UUID,
submenu_id: UUID,
dish_data: DishBase,
):
data = await self.dish_repo.create_dish_item(
menu_id,
submenu_id,
dish_data,
)
return data
async def read_dish(self, menu_id: UUID, submenu_id: UUID, dish_id: UUID):
data = await self.dish_repo.get_dish_item(menu_id, submenu_id, dish_id)
return data
async def update_dish(
self, menu_id: UUID, submenu_id: UUID, dish_id, dish_data: DishBase
):
data = await self.dish_repo.update_dish_item(
menu_id, submenu_id, dish_id, dish_data
)
return data
async def del_dish(self, menu_id: UUID, submenu_id: UUID, dish_id: UUID):
data = await self.dish_repo.delete_dish_item(
menu_id,
submenu_id,
dish_id,
)
return data

View File

@ -1,66 +1,41 @@
import pickle
from uuid import UUID
import redis.asyncio as redis # type: ignore
import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends
from fastfood.cruds.menu import MenuCrud
from fastfood.cruds.redis_cache import AsyncRedisCache, get_async_redis_client
from fastfood.schemas import MenuRead
from fastfood.dbase import get_async_redis_client
from fastfood.repository.menu import MenuRepository
from fastfood.repository.redis import RedisRepository
from fastfood.schemas import MenuBase
class MenuService:
def __init__(
self,
menu_crud: MenuCrud = Depends(),
menu_repo: MenuRepository = Depends(),
redis_client: redis.Redis = Depends(get_async_redis_client),
background_tasks: BackgroundTasks = None,
):
self.menu_crud = menu_crud
self.cache_client = AsyncRedisCache(redis_client)
self.menu_repo = menu_repo
self.cache_client = RedisRepository(redis_client)
self.background_tasks = background_tasks
async def read_menus(self):
cached = await self.cache_client.get("all")
if cached:
return cached
data = await self.menu_crud.get_menus()
print("not cached", data)
await self.cache_client.set("all", data, self.background_tasks)
data = await self.menu_repo.get_menus()
return data
async def create_menu(self, menu_data):
data = await self.menu_crud.create_menu_item(menu_data)
await self.cache_client.set(str(data.id), data, self.background_tasks)
await self.cache_client.clear_after_change(str(data.id), self.background_tasks)
async def create_menu(self, menu_data: MenuBase):
data = await self.menu_repo.create_menu_item(menu_data)
return data
async def read_menu(self, menu_id: UUID):
cached = await self.cache_client.get(str(menu_id))
if cached is not None:
return cached
data = await self.menu_crud.get_menu_item(menu_id)
await self.cache_client.set(str(menu_id), data, self.background_tasks)
data = await self.menu_repo.get_menu_item(menu_id)
return data
async def update_menu(self, menu_id: UUID, menu_data):
data = await self.menu_crud.update_menu_item(menu_id, menu_data)
await self.cache_client.set(str(menu_id), data, self.background_tasks)
await self.cache_client.clear_after_change(str(menu_id), self.background_tasks)
data = await self.menu_repo.update_menu_item(menu_id, menu_data)
return data
# async def del_menu(self, menu_id: int | str):
# data = await self.menu_crud.del_menu(menu_id)
# await self.cache_client.delete(f'{menu_id}', self.background_tasks)
# await self.cache_client.clear_after_change(menu_id, self.background_tasks)
# return data
#
# async def orm_read_menu(self, menu_id: int | str):
# return await self.menu_crud.orm_read_menu(menu_id)
#
# async def read_full_menus(self):
# menu_data = await self.menu_crud.get_full_menus()
# return FullMenuListResponse(menus=menu_data)
async def del_menu(self, menu_id: UUID):
data = await self.menu_repo.delete_menu_item(menu_id)
return data

View File

@ -0,0 +1,48 @@
from uuid import UUID
import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends
from fastfood.dbase import get_async_redis_client
from fastfood.repository.redis import RedisRepository
from fastfood.repository.submenu import SubMenuRepository
from fastfood.schemas import MenuBase
class SubmenuService:
def __init__(
self,
submenu_repo: SubMenuRepository = Depends(),
redis_client: redis.Redis = Depends(get_async_redis_client),
background_tasks: BackgroundTasks = None,
):
self.submenu_repo = submenu_repo
self.cache_client = RedisRepository(redis_client)
self.background_tasks = background_tasks
async def read_submenus(self, menu_id: UUID):
data = await self.submenu_repo.get_submenus(menu_id=menu_id)
return data
async def create_submenu(self, menu_id: UUID, submenu_data: MenuBase):
data = await self.submenu_repo.create_submenu_item(
menu_id,
submenu_data,
)
return data
async def read_menu(self, menu_id: UUID, submenu_id: UUID):
data = await self.submenu_repo.get_submenu_item(menu_id, submenu_id)
return data
async def update_submenu(
self, menu_id: UUID, submenu_id: UUID, submenu_data: MenuBase
):
data = await self.submenu_repo.update_submenu_item(
menu_id, submenu_id, submenu_data
)
return data
async def del_menu(self, menu_id: UUID, submenu_id: UUID):
data = await self.submenu_repo.delete_submenu_item(menu_id, submenu_id)
return data

View File

@ -3,7 +3,7 @@ import sys
import uvicorn
from fastfood.cruds import create_db_and_tables
from fastfood.repository import create_db_and_tables
def run_app():

View File

@ -1,12 +1,11 @@
import asyncio
from typing import AsyncGenerator, Generator
from typing import AsyncGenerator, Dict, Generator
import pytest
import pytest_asyncio
from fastapi import FastAPI
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker,
create_async_engine)
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from fastfood.app import create_app
from fastfood.config import settings
@ -31,7 +30,7 @@ def event_loop():
loop.close()
@pytest_asyncio.fixture(scope="function", autouse=True)
@pytest_asyncio.fixture(scope="session", autouse=True)
async def db_init(event_loop):
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@ -60,3 +59,8 @@ async def client(app) -> AsyncGenerator[AsyncClient, None]:
base_url="http://localhost:8000/api/v1/menus",
) as async_client:
yield async_client
@pytest.fixture(scope="session")
def session_data() -> Dict:
return {}

150
tests/repository.py Normal file
View File

@ -0,0 +1,150 @@
from typing import Tuple
from httpx import AsyncClient, Response
class Repository:
class Menu:
@staticmethod
async def read_all(ac: AsyncClient) -> Tuple[int, dict]:
"""чтение всех меню"""
response: Response = await ac.get("/")
return response.status_code, response.json()
@staticmethod
async def get(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""Получение меню по id"""
response: Response = await ac.get(f"/{data.get('id')}")
return response.status_code, response.json()
@staticmethod
async def write(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""создания меню"""
response: Response = await ac.post("/", json=data)
return response.status_code, response.json()
@staticmethod
async def update(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""Обновление меню по id"""
response: Response = await ac.patch(
f"/{data.get('id')}",
json=data,
)
return response.status_code, response.json()
@staticmethod
async def delete(ac: AsyncClient, data: dict) -> int:
"""Удаление меню по id"""
response: Response = await ac.delete(f"/{data.get('id')}")
return response.status_code
class Submenu:
@staticmethod
async def read_all(ac: AsyncClient, menu: dict) -> Tuple[int, dict]:
"""чтение всех меню"""
response: Response = await ac.get(f"/{menu.get('id')}/submenus/")
return response.status_code, response.json()
@staticmethod
async def get(
ac: AsyncClient,
menu: dict,
submenu: dict,
) -> Tuple[int, dict]:
"""Получение меню по id"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
)
return response.status_code, response.json()
@staticmethod
async def write(
ac: AsyncClient,
menu: dict,
submenu: dict,
) -> Tuple[int, dict]:
"""создания меню"""
response: Response = await ac.post(
f"/{menu.get('id')}/submenus/",
json=submenu,
)
return response.status_code, response.json()
@staticmethod
async def update(
ac: AsyncClient, menu: dict, submenu: dict
) -> Tuple[int, dict]:
"""Обновление меню по id"""
response: Response = await ac.patch(
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
json=submenu,
)
return response.status_code, response.json()
@staticmethod
async def delete(ac: AsyncClient, menu: dict, submenu: dict) -> int:
"""Удаление меню по id"""
response: Response = await ac.delete(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
)
return response.status_code
class Dish:
@staticmethod
async def read_all(
ac: AsyncClient, menu: dict, submenu: dict
) -> Tuple[int, dict]:
"""чтение всех блюд"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
)
return response.status_code, response.json()
@staticmethod
async def get(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""Получение блюда по id"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}",
)
return response.status_code, response.json()
@staticmethod
async def write(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""создания блюда"""
response: Response = await ac.post(
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
json=dish,
)
return response.status_code, response.json()
@staticmethod
async def update(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""Обновление блюда по id"""
response: Response = await ac.patch(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}",
json=dish,
)
return response.status_code, response.json()
@staticmethod
async def delete(
ac: AsyncClient,
menu: dict,
submenu: dict,
dish: dict,
) -> int:
"""Удаление блюда по id"""
response: Response = await ac.delete(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}"
)
return response.status_code

View File

@ -1,414 +1,360 @@
from typing import Tuple
import pytest
from httpx import AsyncClient, Response
from httpx import AsyncClient
class TestBaseCrud:
class Menu:
@staticmethod
async def read_all(ac: AsyncClient) -> Tuple[int, dict]:
"""чтение всех меню"""
response: Response = await ac.get("/")
return response.status_code, response.json()
@staticmethod
async def get(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""Получение меню по id"""
response: Response = await ac.get(f"/{data.get('id')}")
return response.status_code, response.json()
@staticmethod
async def write(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""создания меню"""
response: Response = await ac.post("/", json=data)
return response.status_code, response.json()
@staticmethod
async def update(ac: AsyncClient, data: dict) -> Tuple[int, dict]:
"""Обновление меню по id"""
response: Response = await ac.patch(
f"/{data.get('id')}",
json=data,
)
return response.status_code, response.json()
@staticmethod
async def delete(ac: AsyncClient, data: dict) -> int:
"""Удаление меню по id"""
response: Response = await ac.delete(f"/{data.get('id')}")
return response.status_code
class Submenu:
@staticmethod
async def read_all(ac: AsyncClient, menu: dict) -> Tuple[int, dict]:
"""чтение всех меню"""
response: Response = await ac.get(f"/{menu.get('id')}/submenus/")
return response.status_code, response.json()
@staticmethod
async def get(
ac: AsyncClient,
menu: dict,
submenu: dict,
) -> Tuple[int, dict]:
"""Получение меню по id"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
)
return response.status_code, response.json()
@staticmethod
async def write(
ac: AsyncClient,
menu: dict,
submenu: dict,
) -> Tuple[int, dict]:
"""создания меню"""
response: Response = await ac.post(
f"/{menu.get('id')}/submenus/",
json=submenu,
)
return response.status_code, response.json()
@staticmethod
async def update(
ac: AsyncClient, menu: dict, submenu: dict
) -> Tuple[int, dict]:
"""Обновление меню по id"""
response: Response = await ac.patch(
f"/{menu.get('id')}/submenus/{submenu.get('id')}",
json=submenu,
)
return response.status_code, response.json()
@staticmethod
async def delete(ac: AsyncClient, menu: dict, submenu: dict) -> int:
"""Удаление меню по id"""
response: Response = await ac.delete(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
)
return response.status_code
class Dish:
@staticmethod
async def read_all(
ac: AsyncClient, menu: dict, submenu: dict
) -> Tuple[int, dict]:
"""чтение всех блюд"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
)
return response.status_code, response.json()
@staticmethod
async def get(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""Получение блюда по id"""
response: Response = await ac.get(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}",
)
return response.status_code, response.json()
@staticmethod
async def write(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""создания блюда"""
response: Response = await ac.post(
f"/{menu.get('id')}/submenus/{submenu.get('id')}/dishes/",
json=dish,
)
return response.status_code, response.json()
@staticmethod
async def update(
ac: AsyncClient, menu: dict, submenu: dict, dish: dict
) -> Tuple[int, dict]:
"""Обновление блюда по id"""
response: Response = await ac.patch(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}",
json=dish,
)
return response.status_code, response.json()
@staticmethod
async def delete(ac: AsyncClient, menu: dict, submenu: dict, dish: dict) -> int:
"""Удаление блюда по id"""
response: Response = await ac.delete(
f"/{menu.get('id')}/submenus/{submenu.get('id')}"
f"/dishes/{dish.get('id')}"
)
return response.status_code
from .repository import Repository as Repo
@pytest.mark.asyncio
async def test_menu_crud(client) -> None:
async def test_menu_crud_empty(client: AsyncClient) -> None:
"""Тестирование функций меню"""
code, menus = await TestBaseCrud.Menu.read_all(client)
code, rspn = await Repo.Menu.read_all(client)
assert code == 200
assert menus == []
assert rspn == []
@pytest.mark.asyncio
async def test_menu_crud_add(client: AsyncClient) -> None:
"""Тестирование функций меню"""
data = {"title": "Menu", "description": None}
code, menu = await TestBaseCrud.Menu.write(client, data)
code, rspn = await Repo.Menu.write(client, data)
assert code == 201
assert menu["title"] == "Menu"
assert menu["description"] is None
code, menus = await TestBaseCrud.Menu.read_all(client)
assert len(menus) == 1
assert rspn["title"] == "Menu"
assert rspn["description"] is None
await Repo.Menu.delete(client, rspn)
code, menu = await TestBaseCrud.Menu.get(client, {"id": menu.get("id")})
@pytest.mark.asyncio
async def test_menu_crud_get(client: AsyncClient) -> None:
"""Тестирование функций меню"""
data = {"title": "Menu", "description": None}
code, rspn = await Repo.Menu.write(client, data)
code, menu = await Repo.Menu.get(client, {"id": rspn.get("id")})
assert code == 200
assert menu["title"] == data["title"]
assert menu["title"] == rspn["title"]
await Repo.Menu.delete(client, menu)
upd_menu = {
"id": menu.get("id"),
@pytest.mark.asyncio
async def test_menu_crud_update(client: AsyncClient) -> None:
"""Тестирование функций меню"""
data = {"title": "Menu", "description": None}
code, rspn = await Repo.Menu.write(client, data)
upd_data = {
"id": rspn.get("id"),
"title": "upd Menu",
"description": "",
}
code, menu = await TestBaseCrud.Menu.update(client, upd_menu)
code, upd_rspn = await Repo.Menu.update(client, upd_data)
assert code == 200
print(menu)
# assert menu["title"] == "upd Menu"
assert upd_rspn["title"] == "upd Menu"
await Repo.Menu.delete(client, upd_rspn)
# code = await self.Menu.delete(client, rspn)
# assert code == 200
#
# code, menu = await self.Menu.get(client, {"id": rspn.get("id")})
# assert code == 404
#
# @pytest.mark.asyncio
# async def test_submenus(self, client) -> None:
# # Создаем меню и проверяем ответ
# menu = {"title": "Menu", "description": "main menu"}
# code, rspn = await self.Menu.write(client, menu)
# assert code == 201
# menu.update(rspn)
#
# # Проверяем наличие подменю
# code, rspn = await self.Submenu.read_all(client, menu)
# assert code == 200
# assert rspn == []
#
# # Создаем и проверяем подменю
# submenu = {
# "title": "Submenu",
# "description": "submenu",
# "parent_menu": menu["id"],
# }
# code, rspn = await self.Submenu.write(client, menu, submenu)
# assert code == 201
# submenu.update(rspn)
#
# # Проверяем меню на наличие подменю
# code, rspn = await self.Menu.get(client, menu)
# assert code == 200
# assert rspn["submenus_count"] == 1
#
# # Обновляем подменю и проверяем
# submenu["title"] = "updated_submenu"
# code, rspn = await self.Submenu.update(client, menu, submenu)
# assert code == 200
# assert submenu["title"] == rspn["title"]
# submenu.update(rspn)
#
# # Удаляем подменю
# code = await self.Submenu.delete(client, menu, submenu)
# assert code == 200
#
# # Проверяем меню
# code, rspn = await self.Menu.get(client, menu)
# assert code == 200
# assert rspn["submenus_count"] == 0
#
# # Проверяем удаленное подменю
# code, rspn = await self.Submenu.get(client, menu, submenu)
# assert code == 404
#
# # удаляем сопутствующее
# await self.Menu.delete(client, menu)
#
# @pytest.mark.asyncio
# async def test_dishes(self, client: AsyncClient) -> None:
# # Создаем меню и проверяем ответ
# menu = {
# "title": "Menu",
# "description": "main menu",
# }
# code, rspn = await self.Menu.write(client, menu)
# assert code == 201
# menu.update(rspn)
#
# # Создаем и проверяем подменю
# submenu = {
# "title": "Submenu",
# "description": "submenu",
# "parent_menu": menu["id"],
# }
# code, rspn = await self.Submenu.write(client, menu, submenu)
# assert code == 201
# submenu.update(rspn)
#
# # Проверяем все блюда в подменю
# code, rspn = await self.Dish.read_all(client, menu, submenu)
# assert code == 200
# assert rspn == []
#
# # Добавляем блюдо
# dish = {
# "title": "dish",
# "description": "some dish",
# "price": "12.5",
# "parent_submenu": submenu["id"],
# }
# code, rspn = await self.Dish.write(client, menu, submenu, dish)
# assert code == 201
# dish.update(rspn)
#
# # Получаем блюдо
# code, rspn = await self.Dish.get(client, menu, submenu, dish)
# assert code == 200
# assert rspn["title"] == dish["title"]
#
# # Проверяем меню на количество блюд
# code, rspn = await self.Menu.get(client, menu)
# assert code == 200
# assert rspn["dishes_count"] == 1
#
# # Проверяем подменю на наличие блюд
# code, rspn = await self.Submenu.get(client, menu, submenu)
# assert code == 200
# assert rspn["dishes_count"] == 1
#
# # Обновляем блюдо и проверяем
# dish["title"] = "updated_dish"
# code, rspn = await self.Dish.update(client, menu, submenu, dish)
# assert code == 200
# assert dish["title"] == rspn["title"]
# dish.update(rspn)
#
# # Удаляем подменю
# code = await self.Dish.delete(client, menu, submenu, dish)
# assert code == 200
#
# # Проверяем меню
# code, rspn = await self.Menu.get(client, menu)
# assert code == 200
# assert rspn["dishes_count"] == 0
#
# # Проверяем подменю на наличие блюд
# code, rspn = await self.Submenu.get(client, menu, submenu)
# assert code == 200
# assert rspn["dishes_count"] == 0
#
# # Проверяем удаленное блюдо
# code, rspn = await self.Dish.get(client, menu, submenu, dish)
# assert code == 404
#
# # удаляем сопутствующее
# await self.Submenu.delete(client, menu, submenu)
# await self.Menu.delete(client, menu)
#
#
# class TestСontinuity:
# @pytest.mark.asyncio
# async def test_postman_continuity(self, client):
# # Создаем меню
# menu = {
# "title": "Menu",
# "description": "main menu",
# }
# code, rspn = await TestBaseCrud.Menu.write(client, menu)
# assert code == 201
# assert "id" in rspn.keys()
# menu.update(rspn)
#
# # Создаем подменю
# submenu = {
# "title": "Submenu",
# "description": "submenu",
# "parent_menu": menu["id"],
# }
# code, rspn = await TestBaseCrud.Submenu.write(client, menu, submenu)
# assert code == 201
# assert "id" in rspn.keys()
# submenu.update(rspn)
#
# # Добавляем блюдо1
# dish = {
# "title": "dish1",
# "description": "some dish1",
# "price": "13.50",
# "parent_submenu": submenu["id"],
# }
# code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish)
# assert code == 201
# assert "id" in rspn.keys()
# dish.update(rspn)
#
# # Добавляем блюдо2
# dish = {
# "title": "dish2",
# "description": "some dish2",
# "price": "12.50",
# "parent_submenu": submenu["id"],
# }
# code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish)
# assert code == 201
# assert "id" in rspn.keys()
# dish.update(rspn)
#
# # Просматриваем конкретное меню
# code, rspn = await TestBaseCrud.Menu.get(client, menu)
# assert code == 200
# assert "id" in rspn.keys()
# assert menu["id"] == rspn["id"]
# assert "submenus_count" in rspn.keys()
# assert rspn["submenus_count"] == 1
# assert "dishes_count" in rspn.keys()
# assert rspn["dishes_count"] == 2
#
# # Просматриваем конкретное подменю
# code, rspn = await TestBaseCrud.Submenu.get(client, menu, submenu)
# assert code == 200
# assert "id" in rspn.keys()
# assert "dishes_count" in rspn.keys()
# assert rspn["dishes_count"] == 2
#
# # Удаляем подменю
# code = await TestBaseCrud.Submenu.delete(client, menu, submenu)
# assert code == 200
#
# # Просматриваем список подменю
# code, rspn = await TestBaseCrud.Submenu.read_all(client, menu)
# assert code == 200
# assert rspn == []
#
# # Просматриваем список блюд
# code, rspn = await TestBaseCrud.Dish.read_all(client, menu, submenu)
# assert code == 200
# assert rspn == []
#
# # Просматриваем конкретное меню
# code, rspn = await TestBaseCrud.Menu.get(client, menu)
# assert code == 200
# assert "id" in rspn.keys()
# assert menu["id"] == rspn["id"]
# assert "submenus_count" in rspn.keys()
# assert rspn["submenus_count"] == 0
# assert "dishes_count" in rspn.keys()
# assert rspn["dishes_count"] == 0
#
# # Удаляем меню
# code = await TestBaseCrud.Menu.delete(client, menu)
# assert code == 200
#
# # Просматриваем все меню
# code, rspn = await TestBaseCrud.Menu.read_all(client)
# assert code == 200
# assert rspn == []
@pytest.mark.asyncio
async def test_menu_crud_delete(client: AsyncClient) -> None:
"""Тестирование функций меню"""
data = {"title": "Menu", "description": None}
code, rspn = await Repo.Menu.write(client, data)
code = await Repo.Menu.delete(client, rspn)
assert code == 200
code, rspn = await Repo.Menu.get(client, {"id": rspn.get("id")})
assert code == 404
@pytest.mark.asyncio
async def test_menu_crud_get_all(client: AsyncClient) -> None:
"""Тестирование функций меню"""
code, rspn = await Repo.Menu.read_all(client)
assert code == 200
assert rspn == []
data = {"title": "Menu", "description": None}
code, rspn = await Repo.Menu.write(client, data)
code, upd_rspn = await Repo.Menu.read_all(client)
assert code == 200
assert upd_rspn == [rspn]
await Repo.Menu.delete(client, rspn)
@pytest.mark.asyncio
async def test_submenus_get_all(client) -> None:
# Создаем меню и проверяем ответ
menu = {"title": "Menu", "description": "main menu"}
code, rspn = await Repo.Menu.write(client, menu)
assert code == 201
menu.update(rspn)
# Проверяем наличие подменю
code, rspn = await Repo.Submenu.read_all(client, menu)
assert code == 200
assert rspn == []
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Проверяем наличие подменю
code, upd_rspn = await Repo.Submenu.read_all(client, menu)
assert code == 200
assert upd_rspn == [rspn]
# удаляем сопутствующее
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_submenus_add(client) -> None:
# Создаем меню и проверяем ответ
menu = {"title": "Menu", "description": "main menu"}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
assert code == 201
submenu.update(rspn)
# удаляем сопутствующее
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_submenus_update(client) -> None:
# Создаем меню и проверяем ответ
menu = {"title": "Menu", "description": "main menu"}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Обновляем подменю и проверяем
submenu["title"] = "updated_submenu"
code, rspn = await Repo.Submenu.update(client, menu, submenu)
assert code == 200
assert submenu["title"] == rspn["title"]
submenu.update(rspn)
# удаляем сопутствующее
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_submenus_delete(client) -> None:
# Создаем меню и проверяем ответ
menu = {"title": "Menu", "description": "main menu"}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Удаляем подменю
code = await Repo.Submenu.delete(client, menu, submenu)
assert code == 200
# Проверяем удаленное подменю
code, rspn = await Repo.Submenu.get(client, menu, submenu)
assert code == 404
# удаляем сопутствующее
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_dishes_get_all(client: AsyncClient) -> None:
# Создаем меню и проверяем ответ
menu = {
"title": "Menu",
"description": "main menu",
}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Проверяем все блюда в подменю
code, rspn = await Repo.Dish.read_all(client, menu, submenu)
assert code == 200
assert rspn == []
# Добавляем блюдо
dish = {
"title": "dish",
"description": "some dish",
"price": "12.5",
"parent_submenu": submenu["id"],
}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
assert code == 201
dish.update(rspn)
code, upd_rspn = await Repo.Dish.read_all(client, menu, submenu)
assert code == 200
# удаляем сопутствующее
await Repo.Dish.delete(client, menu, submenu, dish)
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_dishes_add(client: AsyncClient) -> None:
# Создаем меню и проверяем ответ
menu = {
"title": "Menu",
"description": "main menu",
}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Добавляем блюдо
dish = {
"title": "dish",
"description": "some dish",
"price": "12.5",
"parent_submenu": submenu["id"],
}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
assert code == 201
dish.update(rspn)
# Получаем блюдо
code, rspn = await Repo.Dish.get(client, menu, submenu, dish)
assert code == 200
assert rspn["title"] == dish["title"]
# удаляем сопутствующее
await Repo.Dish.delete(client, menu, submenu, dish)
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_dishes_update(client: AsyncClient) -> None:
# Создаем меню и проверяем ответ
menu = {
"title": "Menu",
"description": "main menu",
}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Добавляем блюдо
dish = {
"title": "dish",
"description": "some dish",
"price": "12.5",
"parent_submenu": submenu["id"],
}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
dish.update(rspn)
# Обновляем блюдо и проверяем
dish["title"] = "updated_dish"
code, rspn = await Repo.Dish.update(client, menu, submenu, dish)
assert code == 200
assert dish["title"] == rspn["title"]
dish.update(rspn)
# удаляем сопутствующее
await Repo.Dish.delete(client, menu, submenu, dish)
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)
@pytest.mark.asyncio
async def test_dishes_delete(client: AsyncClient) -> None:
# Создаем меню и проверяем ответ
menu = {
"title": "Menu",
"description": "main menu",
}
code, rspn = await Repo.Menu.write(client, menu)
menu.update(rspn)
# Создаем и проверяем подменю
submenu = {
"title": "Submenu",
"description": "submenu",
"parent_menu": menu["id"],
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
submenu.update(rspn)
# Добавляем блюдо
dish = {
"title": "dish",
"description": "some dish",
"price": "12.5",
"parent_submenu": submenu["id"],
}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
dish.update(rspn)
# Удаляем подменю
code = await Repo.Dish.delete(client, menu, submenu, dish)
assert code == 200
# Проверяем удаленное блюдо
code, rspn = await Repo.Dish.get(client, menu, submenu, dish)
assert code == 404
# удаляем сопутствующее
await Repo.Submenu.delete(client, menu, submenu)
await Repo.Menu.delete(client, menu)

235
tests/test_postman.py Normal file
View File

@ -0,0 +1,235 @@
from typing import Dict
import pytest
from httpx import AsyncClient
from .repository import Repository as Repo
@pytest.mark.asyncio
async def test_01(client: AsyncClient, session_data: Dict):
"""Проверяет создание меню"""
menu = {"title": "Menu", "description": "some_menu_desc"}
code, rspn = await Repo.Menu.write(client, menu)
assert code == 201
code, rspn = await Repo.Menu.get(client, rspn)
session_data["target_menu_id"] = rspn.get("id")
session_data["target_menu_title"] = rspn.get("title")
session_data["target_menu_description"] = rspn.get("description")
assert code == 200
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert "submenus_count" in rspn
assert "dishes_count" in rspn
assert rspn["title"] == menu.get("title")
assert rspn.get("description") == menu.get("description")
@pytest.mark.asyncio
async def test_02(client: AsyncClient, session_data: Dict):
submenu = {"title": "Submenu", "description": "submenu_descr"}
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
code, rspn = await Repo.Submenu.write(client, menu, submenu)
assert code == 201
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert "dishes_count" in rspn
assert rspn["title"] == submenu.get("title")
assert rspn.get("description") == submenu.get("description")
session_data["target_submenu_id"] = rspn.get("id")
session_data["target_submenu_title"] = rspn.get("title")
session_data["target_submenu_description"] = rspn.get("description")
@pytest.mark.asyncio
async def test_03_dish1(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
submenu = {
"id": session_data.get("target_submenu_id"),
"title": session_data.get("target_submenu_title"),
"description": session_data.get("target_submenu_description"),
}
dish = {"title": "dish_1", "description": "dish 1 descr", "price": "12.5"}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
assert code == 201
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert "price" in rspn
assert rspn["title"] == dish.get("title")
assert rspn.get("description") == dish.get("description")
assert rspn.get("price") == dish.get("price")
session_data["target_dish_id"] = rspn.get("id")
session_data["target_dish_title"] = rspn.get("title")
session_data["target_dish_description"] = rspn.get("description")
session_data["target_dish_price"] = rspn.get("price")
@pytest.mark.asyncio
async def test_04_dish2(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
submenu = {
"id": session_data.get("target_submenu_id"),
"title": session_data.get("target_submenu_title"),
"description": session_data.get("target_submenu_description"),
}
dish = {"title": "dish_2", "description": "dish 2 descr", "price": "13.5"}
code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
assert code == 201
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert "price" in rspn
assert rspn["title"] == dish.get("title")
assert rspn.get("description") == dish.get("description")
assert rspn.get("price") == dish.get("price")
session_data["target_dish1_id"] = rspn.get("id")
session_data["target_dish1_title"] = rspn.get("title")
session_data["target_dish1_description"] = rspn.get("description")
session_data["target_dish1_price"] = rspn.get("price")
@pytest.mark.asyncio
async def test_05_check_menu(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
code, rspn = await Repo.Menu.get(client, menu)
assert code == 200
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert rspn.get("submenus_count") == 1
assert rspn.get("dishes_count") == 2
@pytest.mark.asyncio
async def test_06_check_submenu(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
submenu = {
"id": session_data.get("target_submenu_id"),
"title": session_data.get("target_submenu_title"),
"description": session_data.get("target_submenu_description"),
}
code, rspn = await Repo.Submenu.get(client, menu, submenu)
assert code == 200
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert rspn.get("dishes_count") == 2
@pytest.mark.asyncio
async def test_07_del_submenu(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
submenu = {
"id": session_data.get("target_submenu_id"),
"title": session_data.get("target_submenu_title"),
"description": session_data.get("target_submenu_description"),
}
code = await Repo.Submenu.delete(client, menu, submenu)
assert code == 200
@pytest.mark.asyncio
async def test_07_check_submenus(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
code, rspn = await Repo.Submenu.read_all(client, menu)
assert code == 200
assert rspn == []
@pytest.mark.asyncio
async def test_08_check_dishes(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
submenu = {
"id": session_data.get("target_submenu_id"),
"title": session_data.get("target_submenu_title"),
"description": session_data.get("target_submenu_description"),
}
code, rspn = await Repo.Dish.read_all(client, menu, submenu)
assert code == 200
assert rspn == []
@pytest.mark.asyncio
async def test_09_check_menu(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
code, rspn = await Repo.Menu.get(client, menu)
assert code == 200
assert "id" in rspn
assert "title" in rspn
assert "description" in rspn
assert rspn.get("submenus_count") == 0
assert rspn.get("dishes_count") == 0
@pytest.mark.asyncio
async def test_10_del_menu(client: AsyncClient, session_data: Dict):
menu = {
"id": session_data.get("target_menu_id"),
"title": session_data.get("target_menu_title"),
"description": session_data.get("target_menu_description"),
}
code = await Repo.Menu.delete(client, menu)
assert code == 200
@pytest.mark.asyncio
async def test_11_check_menus(client: AsyncClient, session_data: Dict):
code, rspn = await Repo.Menu.read_all(client)
assert code == 200
assert rspn == []