diff --git a/.coverage b/.coverage new file mode 100644 index 0000000..7e68d20 Binary files /dev/null and b/.coverage differ diff --git a/.woodpecker/lints.yaml b/.woodpecker/lints.yaml new file mode 100644 index 0000000..9e48e81 --- /dev/null +++ b/.woodpecker/lints.yaml @@ -0,0 +1,13 @@ +when: + - event: [push, pull_request] + branch: main + +steps: + - name: lint + image: python:3.11 + commands: + - pip install uv + - uv sync --no-dev --only-group lints + - uv run black --check . + - uv run ruff check . + - uv run isort --check-only . diff --git a/.woodpecker/python_lints.yaml b/.woodpecker/python_lints.yaml deleted file mode 100644 index a8f9f85..0000000 --- a/.woodpecker/python_lints.yaml +++ /dev/null @@ -1,33 +0,0 @@ -when: - - event: push - branch: main - - event: pull_request - branch: main - -steps: - - name: install-deps - image: python:3.11-slim - commands: - - pip install --upgrade pip - - pip install flake8 black isort # можно добавить pylint, mypy и др. - - - name: flake8 - image: python:3.11-slim - commands: - - flake8 . --max-line-length=120 --exclude=.venv,venv,__pycache__ - - - name: black - image: python:3.11-slim - commands: - - black --check --diff . - - - name: isort - image: python:3.11-slim - commands: - - isort --check-only --diff . - - - name: mypy - image: python:3.11-slim - commands: - - pip install mypy - - mypy . diff --git a/.woodpecker/test_pipeline.yaml b/.woodpecker/test_pipeline.yaml deleted file mode 100644 index 1f50a25..0000000 --- a/.woodpecker/test_pipeline.yaml +++ /dev/null @@ -1,19 +0,0 @@ -when: - - event: push - branch: main - - event: pull_request - branch: main - -steps: - - name: install - image: python:3.11-slim - commands: - - pip install --upgrade pip - - pip install pytest pytest-cov coverage - - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - - - name: test - image: python:3.11-slim - commands: - - pytest --cov=src --cov-report=term --cov-report=xml tests/ - diff --git a/.woodpecker/tests.yaml b/.woodpecker/tests.yaml new file mode 100644 index 0000000..d1e1421 --- /dev/null +++ b/.woodpecker/tests.yaml @@ -0,0 +1,12 @@ +when: + - event: [push, pull_request] + branch: main + +steps: + - name: tests + image: python:3.11 + commands: + - pip install uv + - uv sync --no-dev --group tests + - uv run pytest --cov=app --cov-fail-under=70 --cov-report=term-missing + diff --git a/.woodpecker/types.yaml b/.woodpecker/types.yaml new file mode 100644 index 0000000..6e2cebe --- /dev/null +++ b/.woodpecker/types.yaml @@ -0,0 +1,11 @@ +when: + - event: [push, pull_request] + branch: main + +steps: + - name: types + image: python:3.11 + commands: + - pip install uv + - uv sync --no-dev --only-group types + - uv run mypy . diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/__pycache__/__init__.cpython-313.pyc b/app/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..e6ff294 Binary files /dev/null and b/app/__pycache__/__init__.cpython-313.pyc differ diff --git a/app/__pycache__/main.cpython-313.pyc b/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..83b9a5f Binary files /dev/null and b/app/__pycache__/main.cpython-313.pyc differ diff --git a/main.py b/app/main.py similarity index 100% rename from main.py rename to app/main.py index 5f4edd5..70dbf6e 100644 --- a/main.py +++ b/app/main.py @@ -1,6 +1,7 @@ +from contextlib import asynccontextmanager + import uvicorn from fastapi import FastAPI -from contextlib import asynccontextmanager @asynccontextmanager @@ -17,6 +18,5 @@ def main(): uvicorn.run(app_factory, factory=True, host="0.0.0.0", port=8000) - if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index 96e9f65..d1b1794 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,10 +11,31 @@ dependencies = [ [dependency-groups] dev = [ - "isort>=8.0.1", - "mypy>=1.20.1", + {include-group = "lints"}, + {include-group = "tests"}, + {include-group = "types"}, "pre-commit>=4.5.1", +] +tests = [ + "httpx>=0.28.1", "pytest>=9.0.3", "pytest-asyncio>=1.3.0", - "ruff>=0.15.11", + "pytest-cov>=7.1.0", ] +lints = [ + "black>=23.7.0", + "ruff>=0.15.11", + "isort>=8.0.1", +] +types = [ + "mypy>=1.20.1", +] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +addopts = "--cov=src --cov-report=term" +pythonpath = "." +testpaths = "tests" +xfail_strict = true + diff --git a/tests/__pycache__/test_app_run.cpython-313-pytest-9.0.3.pyc b/tests/__pycache__/test_app_run.cpython-313-pytest-9.0.3.pyc new file mode 100644 index 0000000..a8580fb Binary files /dev/null and b/tests/__pycache__/test_app_run.cpython-313-pytest-9.0.3.pyc differ diff --git a/tests/test_app_run.py b/tests/test_app_run.py new file mode 100644 index 0000000..386e900 --- /dev/null +++ b/tests/test_app_run.py @@ -0,0 +1,41 @@ +from contextlib import asynccontextmanager +from unittest.mock import patch + +import pytest +from fastapi import FastAPI + +# Предполагаем, что тестируемый модуль называется `myapp` +# Импортируем из него нужные объекты +from app.main import app_factory, lifespan, main + + +@pytest.mark.asyncio +async def test_lifespan(): + """Проверяет, что lifespan является корректным асинхронным контекстным менеджером.""" + app = FastAPI() + # Проверяем, что lifespan - это asynccontextmanager + assert isinstance(lifespan, asynccontextmanager(lifespan).__class__) + + # Проверяем, что контекстный менеджер работает (ничего не ломается) + async with lifespan(app): + pass # Просто убеждаемся, что yield отрабатывает + + +def test_app_factory(): + """Проверяет, что app_factory создаёт правильное приложение FastAPI с переданным lifespan.""" + app = app_factory() + assert isinstance(app, FastAPI) + # Проверяем, что lifespan приложения установлен на функцию lifespan + assert app.router.lifespan_context == lifespan + + +@patch("app.main.uvicorn.run") +def test_main(mock_uvicorn_run): + """Проверяет, что main вызывает uvicorn.run с правильными параметрами.""" + main() + mock_uvicorn_run.assert_called_once_with( + app_factory, + factory=True, + host="0.0.0.0", + port=8000, # Предполагаемый порт (в коде обрезано, но обычно 8000) + )