[QG] Add quality gates on main branch
All checks were successful
ci/woodpecker/push/lints Pipeline was successful
ci/woodpecker/push/tests Pipeline was successful
ci/woodpecker/push/types Pipeline was successful

[+] add lint pipeline for ruff isort and black checks
[+] add types pipeline for mypy check
[+] add tests pipeline for pytest check with coverage less 70% blocker QG
[+] add some tests fo QG pass
This commit is contained in:
2026-04-19 16:45:34 +03:00
parent 048071263a
commit 9c3b44b561
13 changed files with 103 additions and 57 deletions

BIN
.coverage Normal file

Binary file not shown.

13
.woodpecker/lints.yaml Normal file
View File

@@ -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 .

View File

@@ -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 .

View File

@@ -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/

12
.woodpecker/tests.yaml Normal file
View File

@@ -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

11
.woodpecker/types.yaml Normal file
View File

@@ -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 .

0
app/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

View File

@@ -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()

View File

@@ -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

41
tests/test_app_run.py Normal file
View File

@@ -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)
)