Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
109 changes: 109 additions & 0 deletions src/pytest_databases/docker/minio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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


DEFAULT_MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY", "minio")
DEFAULT_MINIO_SECRET_KEY = os.getenv("MINIO_SECRET_KEY", "minio123")
DEFAULT_MINIO_SECURE = os.getenv("MINIO_SECURE", "false").lower() in TRUE_VALUES


@dataclass
class MinioService(ServiceContainer):
endpoint: str
access_key: str = DEFAULT_MINIO_ACCESS_KEY
secret_key: str = DEFAULT_MINIO_SECRET_KEY
secure: bool = DEFAULT_MINIO_SECURE


@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",
xdist_minio_isolation_level: "XdistIsolationLevel",
) -> "Generator[MinioService, None, None]":
def check(_service: ServiceContainer) -> bool:
scheme = "https" if DEFAULT_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"
access_key = DEFAULT_MINIO_ACCESS_KEY
secret_key = DEFAULT_MINIO_SECRET_KEY
env = {
"MINIO_ROOT_USER": access_key,
"MINIO_ROOT_PASSWORD": 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=access_key,
secret_key=secret_key,
)


@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=False,
)
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