Ctrl K
ComunidadGuías…

tests/cluster/conftest.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""The cluster test's Postgres: ``NOETA_TEST_POSTGRES_DSN`` when set, else a
throwaway ``postgres:16`` container on a random loopback port, removed at the
end of the session. Each test gets a database of its own (the store's
schema name is fixed, so a database is the unit of isolation).

The tests need the ``noeta-cluster-scripted`` test package installed
(``make cluster`` runs them with it); without it they skip, and with
``NOETA_CLUSTER_REQUIRED`` set (``make cluster``) a skip is a failure."""

from __future__ import annotations

import os
import shutil
import subprocess
import time
import uuid
from collections.abc import Iterator
from importlib.metadata import entry_points
from typing import NoReturn
from urllib.parse import urlsplit, urlunsplit

import pytest

IMAGE = "postgres:16"


def _scripted_installed() -> bool:
    return any(ep.name == "cluster_scripted" for ep in entry_points(group="noeta.providers"))


def _wait_ready(dsn: str, timeout: float = 60.0) -> None:
    import psycopg

    deadline = time.monotonic() + timeout
    while True:
        try:
            with psycopg.connect(dsn, connect_timeout=2):
                return
        except psycopg.OperationalError:
            if time.monotonic() > deadline:
                raise
            time.sleep(0.3)


def _skip(reason: str) -> NoReturn:
    """Skip, or fail when the run requires the cluster test to run."""
    if os.environ.get("NOETA_CLUSTER_REQUIRED"):
        pytest.fail(f"cluster test required but cannot run: {reason}", pytrace=False)
    pytest.skip(reason)


@pytest.fixture(scope="session")
def pg_server() -> Iterator[str]:
    """A Postgres DSN (its maintenance database) for the session."""
    if not _scripted_installed():
        _skip("the cluster test needs its scripted provider: run `make cluster`")
    dsn = os.environ.get("NOETA_TEST_POSTGRES_DSN", "")
    if dsn:
        yield dsn
        return
    if shutil.which("docker") is None:
        _skip("no docker and no NOETA_TEST_POSTGRES_DSN")
    probe = subprocess.run(["docker", "image", "inspect", IMAGE], capture_output=True)
    if probe.returncode != 0:
        _skip(f"docker cannot use {IMAGE} (or the daemon is not reachable)")
    name = f"noeta-cluster-{uuid.uuid4().hex[:8]}"
    subprocess.run(["docker", "run", "-d", "--rm", "--name", name, "-e", "POSTGRES_PASSWORD=pw",
                    "-p", "127.0.0.1::5432", IMAGE, "-c", "max_connections=400"],
                   check=True, capture_output=True)
    try:
        port = subprocess.run(["docker", "port", name, "5432/tcp"], check=True,
                              capture_output=True, text=True).stdout.split(":")[-1].strip()
        dsn = f"postgresql://postgres:pw@127.0.0.1:{port}/postgres"
        _wait_ready(dsn)
        # The entrypoint restarts the server once after initdb; wait for that too.
        time.sleep(1.0)
        _wait_ready(dsn)
        yield dsn
    finally:
        subprocess.run(["docker", "rm", "-f", name], capture_output=True)


@pytest.fixture
def pg_url(pg_server: str) -> Iterator[str]:
    """A fresh database on the session's server, dropped afterwards."""
    import psycopg

    db = f"cluster_{uuid.uuid4().hex[:12]}"
    with psycopg.connect(pg_server, autocommit=True) as conn:
        conn.execute(f'CREATE DATABASE "{db}"')
    parts = urlsplit(pg_server)
    yield urlunsplit(parts._replace(path=f"/{db}"))
    with psycopg.connect(pg_server, autocommit=True) as conn:
        conn.execute(f'DROP DATABASE IF EXISTS "{db}" WITH (FORCE)')