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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:

# Ruff replaces black, flake8, autoflake and isort
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: "v0.9.6" # make sure this is always consistent with hatch configs
rev: "v0.11.2" # make sure this is always consistent with hatch configs
hooks:
- id: ruff
args: [--config, ./pyproject.toml]
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,6 @@
"tag:yaml.org,2002:python/name:material.extensions.emoji.to_svg",
"tag:yaml.org,2002:python/name:material.extensions.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
]
],
"python.analysis.extraPaths": ["${workspaceFolder}/src"]
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ pre-commit: ## Run pre-commit hooks
.PHONY: slotscheck
slotscheck: ## Run slotscheck
@echo "${INFO} Running slots check... 🔍"
@uv run slotscheck
@uv run slotscheck src/pytest_databases
@echo "${OK} Slots check passed ✨"

.PHONY: fix
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

Reusable test fixtures for any and all databases.

<div align="center">

<!-- markdownlint-disable no-inline-html -->
<!-- prettier-ignore-start -->
<p align="center">

| Project | | Status |
| Project | | Status |
|-----------|:----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CI/CD | | [![Latest Release](https://github.com/litestar-org/pytest-databases/actions/workflows/release.yaml/badge.svg)](https://github.com/litestar-org/pytest-databases/actions/workflows/release.yaml) [![ci](https://github.com/litestar-org/pytest-databases/actions/workflows/ci.yaml/badge.svg)](https://github.com/litestar-org/pytest-databases/actions/workflows/ci.yaml) [![Documentation Building](https://github.com/litestar-org/pytest-databases/actions/workflows/docs.yaml/badge.svg?branch=main)](https://github.com/litestar-org/pytest-databases/actions/workflows/docs.yaml) |
| Quality | | [![Coverage](https://codecov.io/github/litestar-org/pytest-databases/graph/badge.svg?token=vKez4Pycrc)](https://codecov.io/github/litestar-org/pytest-databases) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_pytest-databases&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=litestar-org_pytest-databases) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_pytest-databases&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_pytest-databases) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_pytest-databases&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_pytest-databases) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_pytest-databases&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_pytest-databases) |
| Package | | [![PyPI - Version](https://img.shields.io/pypi/v/pytest-databases?labelColor=202235&color=edb641&logo=python&logoColor=edb641)](https://badge.fury.io/py/pytest-databases) ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/pytest-databases?labelColor=202235&color=edb641&logo=python&logoColor=edb641) |
| Community | | [![Discord](https://img.shields.io/discord/919193495116337154?labelColor=202235&color=edb641&label=chat%20on%20discord&logo=discord&logoColor=edb641)](https://discord.gg/litestar-919193495116337154) [![Matrix](https://img.shields.io/badge/chat%20on%20Matrix-bridged-202235?labelColor=202235&color=edb641&logo=matrix&logoColor=edb641)](https://matrix.to/#/#litestar:matrix.org) [![Medium](https://img.shields.io/badge/Medium-202235?labelColor=202235&color=edb641&logo=medium&logoColor=edb641)](https://blog.litestar.dev) [![Twitter](https://img.shields.io/twitter/follow/LitestarAPI?labelColor=202235&color=edb641&logo=twitter&logoColor=edb641&style=flat)](https://twitter.com/LitestarAPI) [![Blog](https://img.shields.io/badge/Blog-litestar.dev-202235?logo=blogger&labelColor=202235&color=edb641&logoColor=edb641)](https://blog.litestar.dev) |
| Meta | | [![Litestar Project](https://img.shields.io/badge/Litestar%20Org-%E2%AD%90%20Litestar-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://github.com/litestar-org/pytest-databases) [![types - Mypy](https://img.shields.io/badge/types-Mypy-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://github.com/python/mypy) [![License - MIT](https://img.shields.io/badge/license-MIT-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://spdx.org/licenses/) [![Litestar Sponsors](https://img.shields.io/badge/Sponsor-%E2%9D%A4-%23edb641.svg?&logo=github&logoColor=edb641&labelColor=202235)](https://github.com/sponsors/litestar-org) [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json&labelColor=202235)](https://github.com/astral-sh/ruff) [![code style - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/format.json&labelColor=202235)](https://github.com/psf/black)|

</p>

<!-- prettier-ignore-end -->
</div>

> [!WARNING]
>
Expand All @@ -30,7 +31,7 @@ It is designed to offer pre-configured testing setups for many different types a

## Features

`pytest-databases` currently utilizes `docker compose` (or the legacy `docker-compose`) commands to manage the startup and shutdown of each database service. The following databases are currently available:
`pytest-databases` uses the Docker Python SDK to manage the startup and shutdown of database services in containers. The following databases are currently available:

- **Postgres**: Version 12, 13, 14, 15, 16 and 17 are available
- **MySQL**: Version 5.6, 5.7 and 8 are available
Expand All @@ -46,6 +47,7 @@ It is designed to offer pre-configured testing setups for many different types a
- **KeyDB**: Latest version
- **Elasticsearch**: Version 7 and 8 are available
- **Azure blob storage**: Via azurite
- **Minio**: Latest version

## Contributing

Expand All @@ -56,9 +58,8 @@ Before contributing, please review the [contribution guide][contributing].
If you have any questions, reach out to us on [Discord][discord], our org-wide [GitHub discussions][litestar-discussions] page,
or the [project-specific GitHub discussions page][project-discussions].

<hr>

<!-- markdownlint-disable -->
<hr>
<p align="center">
<!-- github-banner-start -->
<img src="https://raw.githubusercontent.com/litestar-org/meta/2901c9c5c5895a83fbfa56944c33bca287f88d42/branding/SVG%20-%20Transparent/logo-full-wide.svg" alt="Litestar Logo - Light" width="20%" height="auto" />
Expand All @@ -73,4 +74,3 @@ or the [project-specific GitHub discussions page][project-discussions].
[project-discussions]: https://github.com/litestar-org/pytest-databases/discussions
[project-docs]: https://docs.pytest-databases.litestar.dev
[install-guide]: https://docs.pytest-databases.litestar.dev/latest/#installation
[newrepo]: https://github.com/organizations/litestar-org/repositories/new?template=pytest-databases
19 changes: 10 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ readme = "README.md"
requires-python = ">=3.9"
version = "0.11.1"
#
authors = [{ name = "Cody Fincher", email = "cody[email protected]" }]
authors = [{ name = "Cody Fincher", email = "cody@litestar.dev" }]
keywords = [
"database",
"migration",
Expand Down Expand Up @@ -61,27 +61,28 @@ elasticsearch7 = ["elasticsearch7"]
elasticsearch8 = ["elasticsearch8"]
keydb = ["redis"]
mariadb = ["mariadb"]
mssql = ["pymssql<=2.3.1"]
minio = ["minio"]
mssql = ["pymssql"]
mysql = ["mysql-connector-python"]
oracle = ["oracledb"]
postgres = ["psycopg>=3"]
redis = ["redis"]
spanner = ["google-cloud-spanner"]


[dependency-groups]
dev = [
# tests
"bump-my-version",
"pytest-databases[azure-storage,bigquery,cockroachdb,dragonfly,elasticsearch7,elasticsearch8,keydb,mssql,mysql,mariadb,oracle,postgres,redis,spanner]",
"coverage[toml]>=6.2",
"pytest-databases[azure-storage,bigquery,cockroachdb,dragonfly,elasticsearch7,elasticsearch8,keydb,mssql,mysql,mariadb,oracle,postgres,redis,spanner,minio]",
"coverage[toml]>=6.2",
"pytest",
"pytest-cov",
"pytest-cdist>=0.2",
"pytest-mock",
"pytest-vcr",
"pytest-click",
"pytest-xdist",
"pytest-sugar",
"slotscheck",
# lint
"mypy",
"ruff",
Expand All @@ -104,7 +105,7 @@ dev = [
"sphinx-design>=0.5.0",
"sphinxcontrib-mermaid>=0.9.2",
"auto-pytabs[sphinx]>=0.4.0",
"pytest-cdist>=0.2",

]


Expand All @@ -116,7 +117,7 @@ dev = [
allow_dirty = true
commit = true
commit_args = "--no-verify"
current_version = "0.11.0"
current_version = "0.11.1"
ignore_missing_files = false
ignore_missing_version = false
message = "chore(release): bump to v{new_version}"
Expand Down Expand Up @@ -295,7 +296,7 @@ lint.select = ["ALL"]
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
preview = true # preview features & checks, use with caution
src = ["src", "tests/", "docs/"]
target-version = "py38"
target-version = "py39"

[tool.ruff.lint.pydocstyle]
convention = "google"
Expand Down
5 changes: 4 additions & 1 deletion scripts/build_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
import subprocess # noqa: S404
from contextlib import contextmanager
from pathlib import Path
from typing import Generator
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from collections.abc import Generator

REDIRECT_TEMPLATE = """
<!DOCTYPE HTML>
Expand Down
3 changes: 2 additions & 1 deletion src/pytest_databases/_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import subprocess # noqa: S404
import time
from contextlib import AbstractContextManager, contextmanager
from typing import TYPE_CHECKING, Any, Callable, Generator
from typing import TYPE_CHECKING, Any, Callable

import filelock
import pytest
Expand All @@ -19,6 +19,7 @@

if TYPE_CHECKING:
import pathlib
from collections.abc import Generator
from types import TracebackType

from docker.models.containers import Container
Expand Down
6 changes: 3 additions & 3 deletions src/pytest_databases/docker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
import time
import timeit
from contextlib import AbstractContextManager
from typing import TYPE_CHECKING, Any, Callable, Iterable
from typing import TYPE_CHECKING, Any, Callable

from pytest_databases.helpers import simple_string_hash

if TYPE_CHECKING:
from collections.abc import Awaitable, Generator
from collections.abc import Awaitable, Generator, Iterable
from pathlib import Path
from types import TracebackType

TRUE_VALUES = {"True", "true", "1", "yes", "Y", "T"}
TRUE_VALUES = {"True", "true", "1", "yes", "y", "Y", "T", "on", "enabled", "ok"}


def wait_until_responsive(
Expand Down
4 changes: 3 additions & 1 deletion src/pytest_databases/docker/azure_blob.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, AsyncGenerator, Generator
from typing import TYPE_CHECKING

import pytest
from azure.storage.blob import ContainerClient
Expand All @@ -11,6 +11,8 @@
from pytest_databases.types import ServiceContainer, XdistIsolationLevel

if TYPE_CHECKING:
from collections.abc import AsyncGenerator, Generator

from pytest_databases._service import DockerService


Expand Down
3 changes: 2 additions & 1 deletion src/pytest_databases/docker/mariadb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import contextlib
import traceback
from collections.abc import Generator
from dataclasses import dataclass
from typing import TYPE_CHECKING, Generator
from typing import TYPE_CHECKING

import mariadb
import pytest
Expand Down
120 changes: 120 additions & 0 deletions src/pytest_databases/docker/minio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import os
from dataclasses import dataclass
from typing import TYPE_CHECKING
from urllib.error import URLError
from urllib.request import Request, urlopen

import pytest
from minio.api import Minio

from pytest_databases.docker import TRUE_VALUES
from pytest_databases.helpers import get_xdist_worker_num
from pytest_databases.types import ServiceContainer, XdistIsolationLevel

if TYPE_CHECKING:
from collections.abc import Generator

from pytest_databases._service import DockerService


@dataclass
class MinioService(ServiceContainer):
endpoint: str
access_key: str
secret_key: str
secure: bool


@pytest.fixture(scope="session")
def minio_access_key() -> str:
return os.getenv("MINIO_ACCESS_KEY", "minio")


@pytest.fixture(scope="session")
def minio_secret_key() -> str:
return os.getenv("MINIO_SECRET_KEY", "minio123")


@pytest.fixture(scope="session")
def minio_secure() -> bool:
return os.getenv("MINIO_SECURE", "false").lower() in TRUE_VALUES


@pytest.fixture(scope="session")
def xdist_minio_isolation_level() -> XdistIsolationLevel:
return "database"


@pytest.fixture(scope="session")
def minio_default_bucket_name(xdist_minio_isolation_level: XdistIsolationLevel) -> str:
worker_num = get_xdist_worker_num()
if worker_num is not None and xdist_minio_isolation_level == "server":
return f"pytest-databases-{worker_num}"
return "pytest-databases"


@pytest.fixture(scope="session")
def minio_service(
docker_service: "DockerService",
minio_access_key: str,
minio_secret_key: str,
minio_secure: bool,
) -> "Generator[MinioService, None, None]":
def check(_service: ServiceContainer) -> bool:
scheme = "https" if minio_secure else "http"
url = f"{scheme}://{_service.host}:{_service.port}/minio/health/ready"
if not url.startswith(("http:", "https:")):
msg = "URL must start with 'http:' or 'https:'"
raise ValueError(msg)
try:
with urlopen(url=Request(url, method="GET"), timeout=10) as response: # noqa: S310
return response.status == 200
except (URLError, ConnectionError):
return False

command = "server /data"
name = "minio"
env = {
"MINIO_ROOT_USER": minio_access_key,
"MINIO_ROOT_PASSWORD": minio_secret_key,
}

with docker_service.run(
image="quay.io/minio/minio",
name=name,
command=command,
container_port=9000,
timeout=20,
pause=0.5,
env=env,
check=check,
) as service:
yield MinioService(
host=service.host,
port=service.port,
endpoint=f"{service.host}:{service.port}",
access_key=minio_access_key,
secret_key=minio_secret_key,
secure=minio_secure,
)


@pytest.fixture(scope="session")
def minio_client(
minio_service: "MinioService",
minio_default_bucket_name: str,
) -> "Generator[Minio, None, None]":
client = Minio(
endpoint=minio_service.endpoint,
access_key=minio_service.access_key,
secret_key=minio_service.secret_key,
secure=minio_service.secure,
)
try:
if not client.bucket_exists(minio_default_bucket_name):
client.make_bucket(minio_default_bucket_name)
except Exception as e:
msg = f"Failed to create bucket {minio_default_bucket_name}"
raise RuntimeError(msg) from e
else:
yield client
Loading
Loading