diff --git a/.env_template b/.env_template index 68db720..54b18a1 100644 --- a/.env_template +++ b/.env_template @@ -1,3 +1,5 @@ +MODE= + DB_HOST= DB_PORT= DB_USER= diff --git a/app/config.py b/app/config.py index 086241d..82eb11b 100644 --- a/app/config.py +++ b/app/config.py @@ -1,13 +1,23 @@ +from typing import Literal + from pydantic_settings import BaseSettings class Settings(BaseSettings): + MODE: Literal["DEV", "TEST", "PROD"] + DB_USER: str DB_PASS: str DB_HOST: str DB_PORT: str DB_NAME: str + TEST_DB_HOST: str + TEST_DB_PORT: str + TEST_DB_USER: str + TEST_DB_PASS: str + TEST_DB_NAME: str + SECRET_KEY: str ALGORITHM: str diff --git a/app/conftest.py b/app/conftest.py new file mode 100644 index 0000000..9da598e --- /dev/null +++ b/app/conftest.py @@ -0,0 +1,4 @@ +import os + +os.environ["MODE"] = "TEST" + diff --git a/app/database.py b/app/database.py index 469cc21..b982fb0 100644 --- a/app/database.py +++ b/app/database.py @@ -1,14 +1,20 @@ +from sqlalchemy import NullPool from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import DeclarativeBase, sessionmaker from app.config import settings - -# URL по которому лежит БД -DATABASE_URL = f"""postgresql+asyncpg://{settings.DB_USER}: - {settings.DB_PASS}@{settings.DB_HOST}: - {settings.DB_PORT}/{settings.DB_NAME}""" -DATABASE_PARAMS = {} +if settings.MODE == "TEST": + DATABASE_URL = f"""postgresql+asyncpg://{settings.TEST_DB_USER}: + {settings.TEST_DB_PASS}@{settings.TEST_DB_HOST}: + {settings.TEST_DB_PORT}/{settings.TEST_DB_NAME}""" + DATABASE_PARAMS = {"poolclass": NullPool} +else: + # URL по которому лежит БД + DATABASE_URL = f"""postgresql+asyncpg://{settings.DB_USER}: + {settings.DB_PASS}@{settings.DB_HOST}: + {settings.DB_PORT}/{settings.DB_NAME}""" + DATABASE_PARAMS = {} engine = create_async_engine(DATABASE_URL, **DATABASE_PARAMS) # Создание ассинхронной сессии для БД diff --git a/app/tests/conftest.py b/app/tests/conftest.py new file mode 100644 index 0000000..5081ab7 --- /dev/null +++ b/app/tests/conftest.py @@ -0,0 +1,58 @@ +import asyncio +import json +from datetime import datetime + +import pytest +from sqlalchemy import insert + +from app.config import settings +from app.database import Base, async_session_maker, engine +from app.users.models import Users, UsersVerificationCodes +from app.users.chat.models import Chats, UsersXChats, Messages + + +@pytest.fixture(autouse=True, scope='session') +async def prepare_database(): + assert settings.MODE == "TEST" + + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.drop_all) + await conn.run_sync(Base.metadata.create_all) + + def open_mock_json(model: str): + with open(f"app/tests/mock_{model}.json", 'r') as file: + return json.load(file) + + users = open_mock_json("users") + users_verification_codes = open_mock_json("verification_codes") + chats = open_mock_json("chats") + users_x_chats = open_mock_json("x_chats") + messages = open_mock_json("messages") + + new_users = [] + for i in users: + i['date_of_birth'] = datetime.strptime(i['date_of_birth'], '%Y-%m-%d') + # i['date_of_birth'] = i['date_of_birth'].isoformat() + new_users.append(i) + + async with async_session_maker() as session: + add_users = insert(Users).values(new_users) + add_users_verification_codes = insert(UsersVerificationCodes).values(users_verification_codes) + add_chats = insert(Chats).values(chats) + add_users_x_chats = insert(UsersXChats).values(users_x_chats) + add_messages = insert(Messages).values(messages) + + await session.execute(add_users) + await session.execute(add_users_verification_codes) + await session.execute(add_chats) + await session.execute(add_users_x_chats) + await session.execute(add_messages) + + await session.commit() + + +@pytest.fixture(scope="session") +def event_loop(request): + loop = asyncio.get_event_loop_policy().new_event_loop() + yield loop + loop.close() diff --git a/app/tests/integration_tests/api_test.py b/app/tests/integration_tests/api_test.py new file mode 100644 index 0000000..c777d32 --- /dev/null +++ b/app/tests/integration_tests/api_test.py @@ -0,0 +1,2 @@ +def test_abc(): + assert 1 == 1 diff --git a/app/tests/mock_chats.json b/app/tests/mock_chats.json new file mode 100644 index 0000000..49e33db --- /dev/null +++ b/app/tests/mock_chats.json @@ -0,0 +1,14 @@ +[ + { + "chat_name": "lox", + "chat_for": 2, + "visibility": true, + "created_by": 1 + }, + { + "chat_name": "lox", + "chat_for": 2, + "visibility": true, + "created_by": 1 + } +] \ No newline at end of file diff --git a/app/tests/mock_messages.json b/app/tests/mock_messages.json new file mode 100644 index 0000000..2633874 --- /dev/null +++ b/app/tests/mock_messages.json @@ -0,0 +1,9 @@ +[ + { + "message": "ВАЫФ", + "image_url": null, + "chat_id": 1, + "user_id": 1, + "visibility": true + } +] \ No newline at end of file diff --git a/app/tests/mock_users.json b/app/tests/mock_users.json new file mode 100644 index 0000000..fddec5f --- /dev/null +++ b/app/tests/mock_users.json @@ -0,0 +1,20 @@ +[ + { + "email": "user@example.com", + "username": "string", + "hashed_password": "$2b$12$OkYfLiufUGGpxcJIC1TxQeEZNllHpS/jWICF9iRC7E/WTv7uFePbK", + "date_of_birth": "2024-02-13" + }, + { + "email": "test@test.com", + "username": "test", + "hashed_password": "$2b$12$Q65fcP54s8gLeIjDo5EPLeyqD7oc8YFXl/mV1CDpnKvFKW8exGBOi", + "date_of_birth": "2024-02-14" + }, + { + "email": "urec@urec.com", + "username": "urec", + "hashed_password": "$2b$12$sWscnmmhugSNJECjiz.j4eQK0vVYYA.fsYZD7a00WuJIsj9bDzj3m", + "date_of_birth": "2024-02-14" + } +] \ No newline at end of file diff --git a/app/tests/mock_verification_codes.json b/app/tests/mock_verification_codes.json new file mode 100644 index 0000000..fe6d6b2 --- /dev/null +++ b/app/tests/mock_verification_codes.json @@ -0,0 +1,17 @@ +[ + { + "user_id": 1, + "code": "2vwQ6k", + "description": "Код подтверждения почты" + }, + { + "user_id": 2, + "code": "W6VruA", + "description": "Код подтверждения почты" + }, + { + "user_id": 3, + "code": "bWe93v", + "description": "Код подтверждения почты" + } +] \ No newline at end of file diff --git a/app/tests/mock_x_chats.json b/app/tests/mock_x_chats.json new file mode 100644 index 0000000..7ec2b0a --- /dev/null +++ b/app/tests/mock_x_chats.json @@ -0,0 +1,10 @@ +[ + { + "user_id": 1, + "chat_id": 2 + }, + { + "user_id": 3, + "chat_id": 2 + } +] \ No newline at end of file diff --git a/app/users/chat/models.py b/app/users/chat/models.py index d02813d..618a7ef 100644 --- a/app/users/chat/models.py +++ b/app/users/chat/models.py @@ -6,6 +6,7 @@ from sqlalchemy.orm import mapped_column, Mapped, relationship from app.database import Base + class Chats(Base): __tablename__ = "chats" @@ -20,7 +21,6 @@ class Chats(Base): user_to_exclude = relationship("Users", primaryjoin='Chats.chat_for == Users.id', back_populates="chat") user_who_create = relationship("Users", primaryjoin='Chats.created_by == Users.id', back_populates="creator") - def __str__(self): return f"Чат #{self.id}. Виден: {self.visibility}. Сделан {self.created_by}" diff --git a/app/users/chat/shemas.py b/app/users/chat/shemas.py index 64b5e74..201944b 100644 --- a/app/users/chat/shemas.py +++ b/app/users/chat/shemas.py @@ -1,10 +1,12 @@ from datetime import datetime from typing import Optional -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict class SMessage(BaseModel): + model_config = ConfigDict(from_attributes=True) + message: Optional[str] = None image_url: Optional[str] = None chat_id: int @@ -13,9 +15,6 @@ class SMessage(BaseModel): created_at: datetime avatar_image: str - class Config: - from_attributes = True - class SLastMessages: def __init__( diff --git a/app/users/schemas.py b/app/users/schemas.py index d2f1f76..b0c5174 100644 --- a/app/users/schemas.py +++ b/app/users/schemas.py @@ -1,6 +1,6 @@ from datetime import date -from pydantic import BaseModel, EmailStr +from pydantic import BaseModel, EmailStr, ConfigDict from fastapi import Query @@ -17,6 +17,8 @@ class SUserRegister(BaseModel): class SUser(BaseModel): + model_config = ConfigDict(from_attributes=True) + email: EmailStr id: int username: str @@ -25,9 +27,6 @@ class SUser(BaseModel): date_of_birth: date date_of_registration: date - class Config: - from_attributes = True - class SUserName(BaseModel): username: str = Query(None, min_length=2, max_length=30) @@ -37,4 +36,3 @@ class SUserPassword(BaseModel): password: str = Query(None, min_length=8) new_password: str = Query(None, min_length=8) new_password2: str = Query(None, min_length=8) - diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..c794028 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +pythonpath = . app +asyncio_mode = auto +python_files = *_test.py *_tests.py test_*.py \ No newline at end of file