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
[docs]
@dataclass
class MinioService(ServiceContainer):
    endpoint: str
    access_key: str
    secret_key: str
    secure: bool 
[docs]
@pytest.fixture(scope="session")
def minio_access_key() -> str:
    return os.getenv("MINIO_ACCESS_KEY", "minio") 
[docs]
@pytest.fixture(scope="session")
def minio_secret_key() -> str:
    return os.getenv("MINIO_SECRET_KEY", "minio123") 
[docs]
@pytest.fixture(scope="session")
def minio_secure() -> bool:
    return os.getenv("MINIO_SECURE", "false").lower() in TRUE_VALUES 
[docs]
@pytest.fixture(scope="session")
def xdist_minio_isolation_level() -> XdistIsolationLevel:
    return "database" 
[docs]
@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" 
[docs]
@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,
        ) 
[docs]
@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