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

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_PASSWORD=test
POSTGRES_DB=fastfood_db POSTGRES_DB=fastfood_db
POSTGRES_DB_TEST=testdb POSTGRES_DB_TEST=testdb
REDIS_DB=redis://localhost

View File

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

View File

@ -8,6 +8,7 @@ class Settings(BaseSettings):
POSTGRES_PASSWORD: str = "" POSTGRES_PASSWORD: str = ""
POSTGRES_USER: str = "" POSTGRES_USER: str = ""
POSTGRES_DB_TEST: str = "" POSTGRES_DB_TEST: str = ""
REDIS_DB: str = ""
@property @property
def DATABASE_URL_asyncpg(self): 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 typing import AsyncGenerator
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker, import redis.asyncio as redis
create_async_engine) from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from fastfood.config import settings from fastfood.config import settings
@ -16,3 +17,13 @@ async_session_maker = async_sessionmaker(
async def get_async_session() -> AsyncGenerator[AsyncSession, None]: async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session: async with async_session_maker() as session:
yield 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 import models
from fastfood.dbase import async_engine from fastfood.dbase import async_engine
from .dish import DishCrud from .dish import DishRepository
from .menu import MenuCrud from .menu import MenuRepository
from .submenu import SubMenuCrud from .submenu import SubMenuRepository
async def create_db_and_tables(): 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) await conn.run_sync(models.Base.metadata.create_all)
class Crud(MenuCrud, SubMenuCrud, DishCrud): class Repository(MenuRepository, SubMenuRepository, DishRepository):
pass 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 uuid import UUID
from fastapi import Depends from fastapi import Depends
@ -10,7 +9,7 @@ from fastfood import models, schemas
from fastfood.dbase import get_async_session from fastfood.dbase import get_async_session
class MenuCrud: class MenuRepository:
def __init__(self, session: AsyncSession = Depends(get_async_session)): def __init__(self, session: AsyncSession = Depends(get_async_session)):
self.db = 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 uuid import UUID
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import schemas from fastfood import schemas
from fastfood.cruds import crud from fastfood.service.dish import DishService
from fastfood.dbase import get_async_session
from fastfood.utils import price_converter from fastfood.utils import price_converter
router = APIRouter( router = APIRouter(
@ -17,9 +14,12 @@ router = APIRouter(
@router.get("/") @router.get("/")
async def get_dishes( 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 return result
@ -27,13 +27,14 @@ async def get_dishes(
async def create_dish( async def create_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
dish: schemas.DishBase, dish_data: schemas.DishBase,
session: AsyncSession = Depends(get_async_session), dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
result = await crud.create_dish_item( result = await dish.create_dish(
submenu_id=submenu_id, menu_id,
dish=dish, submenu_id,
session=session, dish_data,
) )
return price_converter(result) return price_converter(result)
@ -43,11 +44,13 @@ async def get_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
dish_id: UUID, dish_id: UUID,
session: AsyncSession = Depends(get_async_session), dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
result = await crud.get_dish_item( result = await dish.read_dish(
dish_id=dish_id, menu_id,
session=session, submenu_id,
dish_id,
) )
if not result: if not result:
raise HTTPException(status_code=404, detail="dish not found") raise HTTPException(status_code=404, detail="dish not found")
@ -59,13 +62,15 @@ async def update_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
dish_id: UUID, dish_id: UUID,
dish: schemas.DishBase, dish_data: schemas.DishBase,
session: AsyncSession = Depends(get_async_session), dish: DishService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
result = await crud.update_dish_item( result = await dish.update_dish(
dish_id=dish_id, menu_id,
dish=dish, submenu_id,
session=session, dish_id,
dish_data,
) )
return price_converter(result) return price_converter(result)
@ -75,6 +80,7 @@ async def delete_dish(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
dish_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 uuid import UUID
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import schemas from fastfood.schemas import Menu, MenuBase, MenuRead
from fastfood.cruds import crud
from fastfood.dbase import get_async_session
from fastfood.service.menu import MenuService from fastfood.service.menu import MenuService
router = APIRouter( 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( async def get_menus(
menu: MenuService = Depends(), menu: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(), background_tasks: BackgroundTasks = BackgroundTasks(),
@ -23,16 +20,16 @@ async def get_menus(
return await menu.read_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( async def add_menu(
menu: schemas.MenuBase, menu: MenuBase,
responce: MenuService = Depends(), responce: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(), background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
return await responce.create_menu(menu) 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( async def get_menu(
menu_id: UUID, menu_id: UUID,
responce: MenuService = Depends(), responce: MenuService = Depends(),
@ -45,10 +42,10 @@ async def get_menu(
return result return result
@router.patch("/{menu_id}", response_model=schemas.MenuBase) @router.patch("/{menu_id}", response_model=MenuRead)
async def update_menu( async def update_menu(
menu_id: UUID, menu_id: UUID,
menu: schemas.MenuBase, menu: MenuBase,
responce: MenuService = Depends(), responce: MenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(), background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
@ -59,10 +56,10 @@ async def update_menu(
return result.scalars().one() return result.scalars().one()
# @router.delete("/{menu_id}")
# @router.delete("/{menu_id}") async def delete_menu(
# async def delete_menu( menu_id: UUID,
# menu_id: UUID, menu: MenuService = Depends(),
# session: AsyncSession = Depends(get_async_session), background_tasks: BackgroundTasks = BackgroundTasks(),
# ): ):
# await crud.delete_menu_item(menu_id=menu_id, session=session) await menu.del_menu(menu_id)

View File

@ -1,11 +1,10 @@
from typing import List, Optional
from uuid import UUID from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood import schemas from fastfood.schemas import MenuBase, SubMenuRead
from fastfood.cruds import crud from fastfood.service.submenu import SubmenuService
from fastfood.dbase import get_async_session
router = APIRouter( router = APIRouter(
prefix="/api/v1/menus/{menu_id}/submenus", 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( 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() return result.scalars().all()
@router.post("/", status_code=201) @router.post("/", status_code=201, response_model=SubMenuRead)
async def create_submenu_item( async def create_submenu_item(
menu_id: UUID, menu_id: UUID,
submenu: schemas.MenuBase, submenu_data: MenuBase,
session: AsyncSession = Depends(get_async_session), submenu: SubmenuService = Depends(),
background_tasks: BackgroundTasks = BackgroundTasks(),
): ):
result = await crud.create_submenu_item( result = await submenu.create_submenu(
menu_id=menu_id, menu_id=menu_id,
submenu=submenu, submenu_data=submenu_data,
session=session,
) )
return result return result
@router.get("/{submenu_id}", response_model=schemas.SubMenuRead) @router.get("/{submenu_id}", response_model=SubMenuRead)
async def get_submenu( async def get_submenu(
menu_id: UUID, menu_id: UUID,
submenu_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, menu_id=menu_id,
submenu_id=submenu_id, submenu_id=submenu_id,
session=session,
) )
if not result: if not result:
raise HTTPException(status_code=404, detail="submenu not found") raise HTTPException(status_code=404, detail="submenu not found")
@ -53,24 +54,28 @@ async def get_submenu(
@router.patch( @router.patch(
"/{submenu_id}", "/{submenu_id}",
response_model=schemas.MenuBase, response_model=MenuBase,
) )
async def update_submenu( async def update_submenu(
menu_id: UUID, menu_id: UUID,
submenu_id: UUID, submenu_id: UUID,
submenu: schemas.MenuBase, submenu_data: MenuBase,
session: AsyncSession = Depends(get_async_session), 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_id=submenu_id,
submenu=submenu, submenu_data=submenu_data,
session=session,
) )
return result.scalars().one() return result.scalars().one()
@router.delete("/{submenu_id}") @router.delete("/{submenu_id}")
async def delete_submenu( 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 from uuid import UUID
import redis.asyncio as redis # type: ignore import redis.asyncio as redis
from fastapi import BackgroundTasks, Depends from fastapi import BackgroundTasks, Depends
from fastfood.cruds.menu import MenuCrud from fastfood.dbase import get_async_redis_client
from fastfood.cruds.redis_cache import AsyncRedisCache, get_async_redis_client from fastfood.repository.menu import MenuRepository
from fastfood.schemas import MenuRead from fastfood.repository.redis import RedisRepository
from fastfood.schemas import MenuBase
class MenuService: class MenuService:
def __init__( def __init__(
self, self,
menu_crud: MenuCrud = Depends(), menu_repo: MenuRepository = Depends(),
redis_client: redis.Redis = Depends(get_async_redis_client), redis_client: redis.Redis = Depends(get_async_redis_client),
background_tasks: BackgroundTasks = None, background_tasks: BackgroundTasks = None,
): ):
self.menu_crud = menu_crud self.menu_repo = menu_repo
self.cache_client = AsyncRedisCache(redis_client) self.cache_client = RedisRepository(redis_client)
self.background_tasks = background_tasks self.background_tasks = background_tasks
async def read_menus(self): async def read_menus(self):
cached = await self.cache_client.get("all") data = await self.menu_repo.get_menus()
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)
return data return data
async def create_menu(self, menu_data): async def create_menu(self, menu_data: MenuBase):
data = await self.menu_crud.create_menu_item(menu_data) data = await self.menu_repo.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)
return data return data
async def read_menu(self, menu_id: UUID): async def read_menu(self, menu_id: UUID):
cached = await self.cache_client.get(str(menu_id)) data = await self.menu_repo.get_menu_item(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)
return data return data
async def update_menu(self, menu_id: UUID, menu_data): async def update_menu(self, menu_id: UUID, menu_data):
data = await self.menu_crud.update_menu_item(menu_id, menu_data) data = await self.menu_repo.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)
return data return data
# async def del_menu(self, menu_id: int | str): async def del_menu(self, menu_id: UUID):
# data = await self.menu_crud.del_menu(menu_id) data = await self.menu_repo.delete_menu_item(menu_id)
# await self.cache_client.delete(f'{menu_id}', self.background_tasks) return data
# 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)

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 import uvicorn
from fastfood.cruds import create_db_and_tables from fastfood.repository import create_db_and_tables
def run_app(): def run_app():

View File

@ -1,12 +1,11 @@
import asyncio import asyncio
from typing import AsyncGenerator, Generator from typing import AsyncGenerator, Dict, Generator
import pytest import pytest
import pytest_asyncio import pytest_asyncio
from fastapi import FastAPI from fastapi import FastAPI
from httpx import AsyncClient from httpx import AsyncClient
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker, from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
create_async_engine)
from fastfood.app import create_app from fastfood.app import create_app
from fastfood.config import settings from fastfood.config import settings
@ -31,7 +30,7 @@ def event_loop():
loop.close() loop.close()
@pytest_asyncio.fixture(scope="function", autouse=True) @pytest_asyncio.fixture(scope="session", autouse=True)
async def db_init(event_loop): async def db_init(event_loop):
async with async_engine.begin() as conn: async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all) 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", base_url="http://localhost:8000/api/v1/menus",
) as async_client: ) as async_client:
yield 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 import pytest
from httpx import AsyncClient, Response from httpx import AsyncClient
from .repository import Repository as Repo
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
@pytest.mark.asyncio @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 code == 200
assert menus == [] assert rspn == []
@pytest.mark.asyncio
async def test_menu_crud_add(client: AsyncClient) -> None:
"""Тестирование функций меню"""
data = {"title": "Menu", "description": 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 code == 201
assert menu["title"] == "Menu" assert rspn["title"] == "Menu"
assert menu["description"] is None assert rspn["description"] is None
code, menus = await TestBaseCrud.Menu.read_all(client) await Repo.Menu.delete(client, rspn)
assert len(menus) == 1
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 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", "title": "upd Menu",
"description": "", "description": "",
} }
code, menu = await TestBaseCrud.Menu.update(client, upd_menu) code, upd_rspn = await Repo.Menu.update(client, upd_data)
assert code == 200 assert code == 200
print(menu) assert upd_rspn["title"] == "upd Menu"
# assert menu["title"] == "upd Menu" await Repo.Menu.delete(client, upd_rspn)
# code = await self.Menu.delete(client, rspn) @pytest.mark.asyncio
# assert code == 200 async def test_menu_crud_delete(client: AsyncClient) -> None:
# """Тестирование функций меню"""
# code, menu = await self.Menu.get(client, {"id": rspn.get("id")}) data = {"title": "Menu", "description": None}
# assert code == 404 code, rspn = await Repo.Menu.write(client, data)
#
# @pytest.mark.asyncio code = await Repo.Menu.delete(client, rspn)
# async def test_submenus(self, client) -> None: assert code == 200
# # Создаем меню и проверяем ответ
# menu = {"title": "Menu", "description": "main menu"} code, rspn = await Repo.Menu.get(client, {"id": rspn.get("id")})
# code, rspn = await self.Menu.write(client, menu) assert code == 404
# assert code == 201
# menu.update(rspn)
# @pytest.mark.asyncio
# # Проверяем наличие подменю async def test_menu_crud_get_all(client: AsyncClient) -> None:
# code, rspn = await self.Submenu.read_all(client, menu) """Тестирование функций меню"""
# assert code == 200 code, rspn = await Repo.Menu.read_all(client)
# assert rspn == [] assert code == 200
# assert rspn == []
# # Создаем и проверяем подменю
# submenu = { data = {"title": "Menu", "description": None}
# "title": "Submenu", code, rspn = await Repo.Menu.write(client, data)
# "description": "submenu",
# "parent_menu": menu["id"], code, upd_rspn = await Repo.Menu.read_all(client)
# } assert code == 200
# code, rspn = await self.Submenu.write(client, menu, submenu) assert upd_rspn == [rspn]
# assert code == 201 await Repo.Menu.delete(client, rspn)
# submenu.update(rspn)
#
# # Проверяем меню на наличие подменю @pytest.mark.asyncio
# code, rspn = await self.Menu.get(client, menu) async def test_submenus_get_all(client) -> None:
# assert code == 200 # Создаем меню и проверяем ответ
# assert rspn["submenus_count"] == 1 menu = {"title": "Menu", "description": "main menu"}
# code, rspn = await Repo.Menu.write(client, menu)
# # Обновляем подменю и проверяем assert code == 201
# submenu["title"] = "updated_submenu" menu.update(rspn)
# code, rspn = await self.Submenu.update(client, menu, submenu)
# assert code == 200 # Проверяем наличие подменю
# assert submenu["title"] == rspn["title"] code, rspn = await Repo.Submenu.read_all(client, menu)
# submenu.update(rspn) assert code == 200
# assert rspn == []
# # Удаляем подменю
# code = await self.Submenu.delete(client, menu, submenu) # Создаем и проверяем подменю
# assert code == 200 submenu = {
# "title": "Submenu",
# # Проверяем меню "description": "submenu",
# code, rspn = await self.Menu.get(client, menu) "parent_menu": menu["id"],
# assert code == 200 }
# assert rspn["submenus_count"] == 0 code, rspn = await Repo.Submenu.write(client, menu, submenu)
# submenu.update(rspn)
# # Проверяем удаленное подменю
# code, rspn = await self.Submenu.get(client, menu, submenu) # Проверяем наличие подменю
# assert code == 404 code, upd_rspn = await Repo.Submenu.read_all(client, menu)
# assert code == 200
# # удаляем сопутствующее assert upd_rspn == [rspn]
# await self.Menu.delete(client, menu)
# # удаляем сопутствующее
# @pytest.mark.asyncio await Repo.Submenu.delete(client, menu, submenu)
# async def test_dishes(self, client: AsyncClient) -> None: await Repo.Menu.delete(client, menu)
# # Создаем меню и проверяем ответ
# menu = {
# "title": "Menu", @pytest.mark.asyncio
# "description": "main menu", async def test_submenus_add(client) -> None:
# } # Создаем меню и проверяем ответ
# code, rspn = await self.Menu.write(client, menu) menu = {"title": "Menu", "description": "main menu"}
# assert code == 201 code, rspn = await Repo.Menu.write(client, menu)
# menu.update(rspn) menu.update(rspn)
#
# # Создаем и проверяем подменю # Создаем и проверяем подменю
# submenu = { submenu = {
# "title": "Submenu", "title": "Submenu",
# "description": "submenu", "description": "submenu",
# "parent_menu": menu["id"], "parent_menu": menu["id"],
# } }
# code, rspn = await self.Submenu.write(client, menu, submenu) code, rspn = await Repo.Submenu.write(client, menu, submenu)
# assert code == 201 assert code == 201
# submenu.update(rspn) submenu.update(rspn)
#
# # Проверяем все блюда в подменю # удаляем сопутствующее
# code, rspn = await self.Dish.read_all(client, menu, submenu) await Repo.Submenu.delete(client, menu, submenu)
# assert code == 200 await Repo.Menu.delete(client, menu)
# assert rspn == []
#
# # Добавляем блюдо @pytest.mark.asyncio
# dish = { async def test_submenus_update(client) -> None:
# "title": "dish", # Создаем меню и проверяем ответ
# "description": "some dish", menu = {"title": "Menu", "description": "main menu"}
# "price": "12.5", code, rspn = await Repo.Menu.write(client, menu)
# "parent_submenu": submenu["id"], menu.update(rspn)
# }
# code, rspn = await self.Dish.write(client, menu, submenu, dish) # Создаем и проверяем подменю
# assert code == 201 submenu = {
# dish.update(rspn) "title": "Submenu",
# "description": "submenu",
# # Получаем блюдо "parent_menu": menu["id"],
# code, rspn = await self.Dish.get(client, menu, submenu, dish) }
# assert code == 200 code, rspn = await Repo.Submenu.write(client, menu, submenu)
# assert rspn["title"] == dish["title"] submenu.update(rspn)
#
# # Проверяем меню на количество блюд # Обновляем подменю и проверяем
# code, rspn = await self.Menu.get(client, menu) submenu["title"] = "updated_submenu"
# assert code == 200 code, rspn = await Repo.Submenu.update(client, menu, submenu)
# assert rspn["dishes_count"] == 1 assert code == 200
# assert submenu["title"] == rspn["title"]
# # Проверяем подменю на наличие блюд submenu.update(rspn)
# code, rspn = await self.Submenu.get(client, menu, submenu)
# assert code == 200 # удаляем сопутствующее
# assert rspn["dishes_count"] == 1 await Repo.Submenu.delete(client, menu, submenu)
# await Repo.Menu.delete(client, menu)
# # Обновляем блюдо и проверяем
# dish["title"] = "updated_dish"
# code, rspn = await self.Dish.update(client, menu, submenu, dish) @pytest.mark.asyncio
# assert code == 200 async def test_submenus_delete(client) -> None:
# assert dish["title"] == rspn["title"] # Создаем меню и проверяем ответ
# dish.update(rspn) menu = {"title": "Menu", "description": "main menu"}
# code, rspn = await Repo.Menu.write(client, menu)
# # Удаляем подменю menu.update(rspn)
# code = await self.Dish.delete(client, menu, submenu, dish)
# assert code == 200 # Создаем и проверяем подменю
# submenu = {
# # Проверяем меню "title": "Submenu",
# code, rspn = await self.Menu.get(client, menu) "description": "submenu",
# assert code == 200 "parent_menu": menu["id"],
# assert rspn["dishes_count"] == 0 }
# code, rspn = await Repo.Submenu.write(client, menu, submenu)
# # Проверяем подменю на наличие блюд submenu.update(rspn)
# code, rspn = await self.Submenu.get(client, menu, submenu)
# assert code == 200 # Удаляем подменю
# assert rspn["dishes_count"] == 0 code = await Repo.Submenu.delete(client, menu, submenu)
# assert code == 200
# # Проверяем удаленное блюдо
# code, rspn = await self.Dish.get(client, menu, submenu, dish) # Проверяем удаленное подменю
# assert code == 404 code, rspn = await Repo.Submenu.get(client, menu, submenu)
# assert code == 404
# # удаляем сопутствующее
# await self.Submenu.delete(client, menu, submenu) # удаляем сопутствующее
# await self.Menu.delete(client, menu) await Repo.Menu.delete(client, menu)
#
#
# class TestСontinuity: @pytest.mark.asyncio
# @pytest.mark.asyncio async def test_dishes_get_all(client: AsyncClient) -> None:
# async def test_postman_continuity(self, client): # Создаем меню и проверяем ответ
# # Создаем меню menu = {
# menu = { "title": "Menu",
# "title": "Menu", "description": "main menu",
# "description": "main menu", }
# } code, rspn = await Repo.Menu.write(client, menu)
# code, rspn = await TestBaseCrud.Menu.write(client, menu) menu.update(rspn)
# assert code == 201
# assert "id" in rspn.keys() # Создаем и проверяем подменю
# menu.update(rspn) submenu = {
# "title": "Submenu",
# # Создаем подменю "description": "submenu",
# submenu = { "parent_menu": menu["id"],
# "title": "Submenu", }
# "description": "submenu", code, rspn = await Repo.Submenu.write(client, menu, submenu)
# "parent_menu": menu["id"], submenu.update(rspn)
# }
# code, rspn = await TestBaseCrud.Submenu.write(client, menu, submenu) # Проверяем все блюда в подменю
# assert code == 201 code, rspn = await Repo.Dish.read_all(client, menu, submenu)
# assert "id" in rspn.keys() assert code == 200
# submenu.update(rspn) assert rspn == []
#
# # Добавляем блюдо1 # Добавляем блюдо
# dish = { dish = {
# "title": "dish1", "title": "dish",
# "description": "some dish1", "description": "some dish",
# "price": "13.50", "price": "12.5",
# "parent_submenu": submenu["id"], "parent_submenu": submenu["id"],
# } }
# code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish) code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
# assert code == 201 assert code == 201
# assert "id" in rspn.keys() dish.update(rspn)
# dish.update(rspn)
# code, upd_rspn = await Repo.Dish.read_all(client, menu, submenu)
# # Добавляем блюдо2
# dish = { assert code == 200
# "title": "dish2",
# "description": "some dish2", # удаляем сопутствующее
# "price": "12.50", await Repo.Dish.delete(client, menu, submenu, dish)
# "parent_submenu": submenu["id"], await Repo.Submenu.delete(client, menu, submenu)
# } await Repo.Menu.delete(client, menu)
# code, rspn = await TestBaseCrud.Dish.write(client, menu, submenu, dish)
# assert code == 201
# assert "id" in rspn.keys() @pytest.mark.asyncio
# dish.update(rspn) async def test_dishes_add(client: AsyncClient) -> None:
# # Создаем меню и проверяем ответ
# # Просматриваем конкретное меню menu = {
# code, rspn = await TestBaseCrud.Menu.get(client, menu) "title": "Menu",
# assert code == 200 "description": "main menu",
# assert "id" in rspn.keys() }
# assert menu["id"] == rspn["id"] code, rspn = await Repo.Menu.write(client, menu)
# assert "submenus_count" in rspn.keys() menu.update(rspn)
# assert rspn["submenus_count"] == 1
# assert "dishes_count" in rspn.keys() # Создаем и проверяем подменю
# assert rspn["dishes_count"] == 2 submenu = {
# "title": "Submenu",
# # Просматриваем конкретное подменю "description": "submenu",
# code, rspn = await TestBaseCrud.Submenu.get(client, menu, submenu) "parent_menu": menu["id"],
# assert code == 200 }
# assert "id" in rspn.keys() code, rspn = await Repo.Submenu.write(client, menu, submenu)
# assert "dishes_count" in rspn.keys() submenu.update(rspn)
# assert rspn["dishes_count"] == 2
# # Добавляем блюдо
# # Удаляем подменю dish = {
# code = await TestBaseCrud.Submenu.delete(client, menu, submenu) "title": "dish",
# assert code == 200 "description": "some dish",
# "price": "12.5",
# # Просматриваем список подменю "parent_submenu": submenu["id"],
# code, rspn = await TestBaseCrud.Submenu.read_all(client, menu) }
# assert code == 200 code, rspn = await Repo.Dish.write(client, menu, submenu, dish)
# assert rspn == [] assert code == 201
# dish.update(rspn)
# # Просматриваем список блюд
# code, rspn = await TestBaseCrud.Dish.read_all(client, menu, submenu) # Получаем блюдо
# assert code == 200 code, rspn = await Repo.Dish.get(client, menu, submenu, dish)
# assert rspn == [] assert code == 200
# assert rspn["title"] == dish["title"]
# # Просматриваем конкретное меню
# code, rspn = await TestBaseCrud.Menu.get(client, menu) # удаляем сопутствующее
# assert code == 200 await Repo.Dish.delete(client, menu, submenu, dish)
# assert "id" in rspn.keys() await Repo.Submenu.delete(client, menu, submenu)
# assert menu["id"] == rspn["id"] await Repo.Menu.delete(client, menu)
# assert "submenus_count" in rspn.keys()
# assert rspn["submenus_count"] == 0
# assert "dishes_count" in rspn.keys() @pytest.mark.asyncio
# assert rspn["dishes_count"] == 0 async def test_dishes_update(client: AsyncClient) -> None:
# # Создаем меню и проверяем ответ
# # Удаляем меню menu = {
# code = await TestBaseCrud.Menu.delete(client, menu) "title": "Menu",
# assert code == 200 "description": "main menu",
# }
# # Просматриваем все меню code, rspn = await Repo.Menu.write(client, menu)
# code, rspn = await TestBaseCrud.Menu.read_all(client) menu.update(rspn)
# 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)
# Добавляем блюдо
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 == []