Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Python CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
name: Test and Lint
runs-on: ubuntu-latest

steps:
- name: 🧾 Checkout repository
uses: actions/checkout@v4

- name: 🐍 Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.13'

- name: 📦 Install Poetry
run: |
pip install poetry
poetry config virtualenvs.create false

- name: 📄 Install dependencies
run: poetry install --no-interaction --no-root

- name: ✅ Run tests
run: poetry run pytest -v

- name: 🧼 Check formatting
run: |
pip install black
black --check .

- name: 🔍 Lint with ruff
run: |
pip install ruff
ruff check .
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Built to showcase best practices in Python backend development and serve as a so
- 🔀 Routing with versioned endpoints
- 📄 OpenAPI docs auto-generated (`/docs`, `/redoc`)
- ✅ Data validation and typing with Pydantic v2
- 🧪 Async-ready test setup with `pytest`, `httpx`, `pytest-asyncio` (in progress)
- 🧪 Async-ready test setup with `pytest`, `httpx`, `pytest-asyncio`
- 🐳 Minimal Docker support
- 🧰 Designed as a reusable starter template

Expand Down
3 changes: 2 additions & 1 deletion app/api/users.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter

from app.models.user import User, UserCreate
from app.services.user_service import save_user, get_all_users

Expand Down
3 changes: 2 additions & 1 deletion app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import sys

from fastapi import FastAPI

from app.api import users

log_format = '%(asctime)s | %(levelname)-8s | %(name)-12s - %(message)s'
log_format = "%(asctime)s | %(levelname)-8s | %(name)-12s - %(message)s"
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format=log_format)

app = FastAPI(title="Python FastAPI Template", version="0.1.0")
Expand Down
Empty file added tests/api/__init__.py
Empty file.
27 changes: 27 additions & 0 deletions tests/api/test_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import httpx
import pytest

from app.main import app


@pytest.mark.asyncio
async def test_create_user():
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as ac:
response = await ac.post(
"/api/v1/users/", json={"name": "Simone", "email": "[email protected]"}
)
assert response.status_code == 200
data = response.json()
assert data["name"] == "Simone"
assert data["email"] == "[email protected]"


@pytest.mark.asyncio
async def test_get_users():
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as ac:
response = await ac.get("/api/v1/users/")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
Empty file added tests/services/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions tests/services/test_user_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

from app.models.user import UserCreate
from app.services.user_service import save_user, get_all_users


@pytest.mark.asyncio
async def test_save_user():
user = UserCreate(name="Simone", email="[email protected]")
saved_user = await save_user(user)
assert saved_user.name == "Simone"
assert saved_user.email == "[email protected]"


@pytest.mark.asyncio
async def test_get_all_users_empty():
users = await get_all_users()
assert isinstance(users, list)
assert len(users) > 0