mirror of
https://github.com/d3vyce/fastapi-toolsets.git
synced 2026-03-01 17:00:48 +01:00
Merge pull request #1 from d3vyce/add_workflows
chore: add lint/test workflows
This commit is contained in:
100
.github/workflows/ci.yml
vendored
Normal file
100
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint (Ruff)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Set up Python
|
||||
run: uv python install 3.13
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --extra dev
|
||||
|
||||
- name: Run Ruff linter
|
||||
run: uv run ruff check .
|
||||
|
||||
- name: Run Ruff formatter check
|
||||
run: uv run ruff format --check .
|
||||
|
||||
typecheck:
|
||||
name: Type Check (ty)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Set up Python
|
||||
run: uv python install 3.13
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --extra dev
|
||||
|
||||
- name: Run ty
|
||||
run: uv run ty check
|
||||
|
||||
test:
|
||||
name: Test (Python ${{ matrix.python-version }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.11", "3.12", "3.13"]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: test_db
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
run: uv python install ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync --extra dev
|
||||
|
||||
- name: Run tests with coverage
|
||||
env:
|
||||
DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5432/test_db
|
||||
run: |
|
||||
uv run pytest --cov --cov-report=xml --cov-report=term-missing
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
if: matrix.python-version == '3.13'
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: false
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
FastAPI Toolsets provides production-ready utilities for FastAPI applications built with async SQLAlchemy and PostgreSQL. It includes generic CRUD operations, a fixture system with dependency resolution, a Django-like CLI, standardized API responses, and structured exception handling with automatic OpenAPI documentation.
|
||||
|
||||
[](https://github.com/d3vyce/fastapi-toolsets/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/d3vyce/fastapi-toolsets)
|
||||
[](https://github.com/astral-sh/ty)
|
||||
[](https://github.com/astral-sh/uv)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
|
||||
@@ -76,7 +76,9 @@ def _load_module_from_path(path: Path) -> object:
|
||||
path = path.resolve()
|
||||
|
||||
# Add the parent directory to sys.path to support relative imports
|
||||
parent_dir = str(path.parent.parent) # Go up two levels (e.g., from app/cli_config.py to project root)
|
||||
parent_dir = str(
|
||||
path.parent.parent
|
||||
) # Go up two levels (e.g., from app/cli_config.py to project root)
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
|
||||
@@ -44,9 +44,7 @@ def _get_db_context(ctx: typer.Context):
|
||||
|
||||
get_db_context = getattr(config, "get_db_context", None)
|
||||
if get_db_context is None:
|
||||
raise typer.BadParameter(
|
||||
"Config module must have a 'get_db_context' function."
|
||||
)
|
||||
raise typer.BadParameter("Config module must have a 'get_db_context' function.")
|
||||
|
||||
return get_db_context
|
||||
|
||||
@@ -56,7 +54,11 @@ def list_fixtures(
|
||||
ctx: typer.Context,
|
||||
context: Annotated[
|
||||
str | None,
|
||||
typer.Option("--context", "-c", help="Filter by context (base, production, development, testing)."),
|
||||
typer.Option(
|
||||
"--context",
|
||||
"-c",
|
||||
help="Filter by context (base, production, development, testing).",
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
"""List all registered fixtures."""
|
||||
@@ -110,7 +112,9 @@ def show_graph(
|
||||
|
||||
typer.echo("\nFixture Dependency Graph:\n")
|
||||
for fixture in fixtures:
|
||||
deps = f" -> [{', '.join(fixture.depends_on)}]" if fixture.depends_on else ""
|
||||
deps = (
|
||||
f" -> [{', '.join(fixture.depends_on)}]" if fixture.depends_on else ""
|
||||
)
|
||||
typer.echo(f" {fixture.name}{deps}")
|
||||
|
||||
|
||||
@@ -119,15 +123,21 @@ def load(
|
||||
ctx: typer.Context,
|
||||
contexts: Annotated[
|
||||
list[str] | None,
|
||||
typer.Argument(help="Contexts to load (base, production, development, testing)."),
|
||||
typer.Argument(
|
||||
help="Contexts to load (base, production, development, testing)."
|
||||
),
|
||||
] = None,
|
||||
strategy: Annotated[
|
||||
str,
|
||||
typer.Option("--strategy", "-s", help="Load strategy: merge, insert, skip_existing."),
|
||||
typer.Option(
|
||||
"--strategy", "-s", help="Load strategy: merge, insert, skip_existing."
|
||||
),
|
||||
] = "merge",
|
||||
dry_run: Annotated[
|
||||
bool,
|
||||
typer.Option("--dry-run", "-n", help="Show what would be loaded without loading."),
|
||||
typer.Option(
|
||||
"--dry-run", "-n", help="Show what would be loaded without loading."
|
||||
),
|
||||
] = False,
|
||||
) -> None:
|
||||
"""Load fixtures into the database."""
|
||||
@@ -144,7 +154,9 @@ def load(
|
||||
try:
|
||||
load_strategy = LoadStrategy(strategy)
|
||||
except ValueError:
|
||||
typer.echo(f"Invalid strategy: {strategy}. Use: merge, insert, skip_existing", err=True)
|
||||
typer.echo(
|
||||
f"Invalid strategy: {strategy}. Use: merge, insert, skip_existing", err=True
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Resolve what will be loaded
|
||||
@@ -196,7 +208,9 @@ def show_fixture(
|
||||
|
||||
typer.echo(f"\nFixture: {fixture.name}")
|
||||
typer.echo(f"Contexts: {', '.join(fixture.contexts)}")
|
||||
typer.echo(f"Dependencies: {', '.join(fixture.depends_on) if fixture.depends_on else 'None'}")
|
||||
typer.echo(
|
||||
f"Dependencies: {', '.join(fixture.depends_on) if fixture.depends_on else 'None'}"
|
||||
)
|
||||
|
||||
# Show instances
|
||||
instances = list(fixture.func())
|
||||
|
||||
@@ -62,7 +62,6 @@ from ..db import get_transaction
|
||||
from .fixtures import FixtureRegistry, LoadStrategy
|
||||
|
||||
|
||||
|
||||
def register_fixtures(
|
||||
registry: FixtureRegistry,
|
||||
namespace: dict[str, Any],
|
||||
|
||||
@@ -5,13 +5,13 @@ import os
|
||||
import pytest
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import ForeignKey, String
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
from fastapi_toolsets.crud import CrudFactory
|
||||
|
||||
# PostgreSQL connection URL from environment or default for local development
|
||||
DATABASE_URL = os.getenv(
|
||||
DATABASE_URL = os.getenv("DATABASE_URL") or os.getenv(
|
||||
"TEST_DATABASE_URL",
|
||||
"postgresql+asyncpg://postgres:postgres@localhost:5432/fastapi_toolsets_test",
|
||||
)
|
||||
@@ -149,7 +149,7 @@ async def engine():
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
async def db_session(engine) -> AsyncSession:
|
||||
async def db_session(engine):
|
||||
"""Create a test database session with tables.
|
||||
|
||||
Creates all tables before the test and drops them after.
|
||||
|
||||
Reference in New Issue
Block a user