From 9e815b061945dce4cf00d7723df9665567111b6c Mon Sep 17 00:00:00 2001 From: 13orlov <13orlov@gmail.com> Date: Sat, 23 Aug 2025 22:17:53 +0100 Subject: [PATCH] feat(tests): Add test configuration and helpers --- Makefile | 26 ++++++++++++++++++++++++-- app/core/config.py | 16 ++++++++-------- app/main.py | 4 ++-- nano .env.test | 4 ++++ tests/conftest.py | 21 +++++++++++++++++++++ tests/test_counters.py | 16 ++++------------ tests/test_main.py | 29 ++++++++--------------------- 7 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 nano .env.test create mode 100644 tests/conftest.py diff --git a/Makefile b/Makefile index 1ded1ad..7a30002 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # ============================================================================== # ПРИМЕЧАНИЕ: Эти команды используют файлы-оверлеи из Git-репозитория. # Все секреты и уникальные настройки должны быть в .env файлах, -# которые создаются на сервере вручную и находятся в .gitignore. +# которые создаются на сервере вручную (или расшифровываются). # --- Staging Environment --- staging-up: @@ -43,4 +43,26 @@ install: test: @echo "--- Running tests ---" - poetry run pytest -v \ No newline at end of file + poetry run pytest -v + + +# ============================================================================== +# УПРАВЛЕНИЕ GIT +# ============================================================================== +# ПРИМЕЧАНИЕ: Перед пушем убедитесь, что вы находитесь в нужной ветке. + +# Добавляет все изменения и делает коммит с сообщением по умолчанию +commit: + @git add . + @git commit -m "chore: Regular commit" + +# Отправляет текущую ветку в удаленный репозиторий +push: + @git push + +# Выполняет commit и push одной командой. +# Требует передачи сообщения для коммита. Пример: make save msg="feat: Add new feature" +save: + @git add . + @git commit -m "$(msg)" + @git push \ No newline at end of file diff --git a/app/core/config.py b/app/core/config.py index 1621c01..5bb779c 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,18 +1,18 @@ +import os from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): - # Учетные данные приложения Яндекс.OAuth YANDEX_CLIENT_ID: str YANDEX_CLIENT_SECRET: str - - # URL API Яндекс.Метрики YANDEX_METRIKA_API_URL: str = "https://api-metrika.yandex.net" - - # Уровень логирования LOG_LEVEL: str = "INFO" - # Указываем, что нужно читать переменные из файла .env - model_config = SettingsConfigDict(env_file=".env") + # ИЗМЕНЕНИЕ ЗДЕСЬ: + # Если запущена среда pytest, используем .env.test, иначе .env + if "PYTEST_CURRENT_TEST" in os.environ: + model_config = SettingsConfigDict(env_file=".env.test") + else: + model_config = SettingsConfigDict(env_file=".env") + -# Создаем единый объект настроек для всего приложения settings = Settings() \ No newline at end of file diff --git a/app/main.py b/app/main.py index 19f5f74..cf93a6f 100644 --- a/app/main.py +++ b/app/main.py @@ -25,9 +25,9 @@ async def lifespan(app: FastAPI): # Создаем главный объект приложения FastAPI и подключаем lifespan app = FastAPI( - title="Marquiz Metrica Connector", + title="Marquiz Metrica Connector 2.0", version="1.0.0", - description="Микросервис для автоматизации настройки Яндекс.Метрики для квизов.", + description="Микросервис для автоматизации настройки api Яндекс.Метрики.", lifespan=lifespan # <-- Подключаем наш новый обработчик ) diff --git a/nano .env.test b/nano .env.test new file mode 100644 index 0000000..359e444 --- /dev/null +++ b/nano .env.test @@ -0,0 +1,4 @@ +YANDEX_CLIENT_ID="test_client_id" +YANDEX_CLIENT_SECRET="test_client_secret" +YANDEX_METRIKA_API_URL="https://api-metrika.yandex.net" +LOG_LEVEL="DEBUG" \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..b6c2349 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,21 @@ +import pytest +from httpx import AsyncClient, ASGITransport +from app.main import app + +@pytest.fixture(scope="session") +def anyio_backend(): + """ + Это фикстура, необходимая для pytest-asyncio, чтобы он работал + с httpx в асинхронном режиме. Просто стандартный шаблон. + """ + return "asyncio" + +@pytest.fixture +async def client() -> AsyncClient: + """ + Главная фикстура. Создает тестовый клиент, который мы будем + использовать во всех наших тестах. + """ + transport = ASGITransport(app=app) + async with AsyncClient(transport=transport, base_url="http://test") as async_client: + yield async_client diff --git a/tests/test_counters.py b/tests/test_counters.py index 9076d26..c33969e 100644 --- a/tests/test_counters.py +++ b/tests/test_counters.py @@ -1,16 +1,8 @@ import pytest -from httpx import AsyncClient, ASGITransport - -from app.main import app @pytest.mark.asyncio -async def test_get_counters_unauthorized(): - transport = ASGITransport(app=app) - async with AsyncClient(transport=transport, base_url="http://test") as client: - response = await client.get("/api/v1/counters/") +async def test_get_counters_unauthorized(client): # <-- Получаем client как аргумент + response = await client.get("/api/v1/counters/") - # Ожидаем 403, а не 401 - assert response.status_code == 403 - - # Тело ответа для этой ошибки от HTTPBearer - assert response.json() == {"detail": "Not authenticated"} \ No newline at end of file + assert response.status_code == 403 + assert response.json() == {"detail": "Not authenticated"} \ No newline at end of file diff --git a/tests/test_main.py b/tests/test_main.py index cb67db4..0ac47d1 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,25 +1,12 @@ import pytest -from httpx import AsyncClient, ASGITransport - -# Импортируем наше главное FastAPI приложение -from app.main import app +# Тесты теперь не знают про 'app', они знают только про 'client' @pytest.mark.asyncio -async def test_health_check(): - """ - Тестирует эндпоинт проверки работоспособности (health check). - """ - # Создаем "транспорт", который работает с нашим app, - # и передаем его в AsyncClient. - transport = ASGITransport(app=app) - async with AsyncClient(transport=transport, base_url="http://test") as client: +async def test_health_check(client): # <-- Получаем client как аргумент + response = await client.get("/") - # Отправляем асинхронный GET-запрос на корневой URL ("/") - response = await client.get("/") - - # Проверки остаются без изменений - assert response.status_code == 200 - assert response.json() == { - "status": "ok", - "message": "Welcome to Marquiz Metrica Connector!" - } \ No newline at end of file + assert response.status_code == 200 + assert response.json() == { + "status": "ok", + "message": "Welcome to Marquiz Metrica Connector!" + } \ No newline at end of file