GithubHelp home page GithubHelp logo

clearcodehq / pytest-postgresql Goto Github PK

View Code? Open in Web Editor NEW
383.0 12.0 40.0 1.48 MB

This is a pytest plugin, that enables you to test your code that relies on a running PostgreSQL Database. It allows you to specify fixtures for PostgreSQL process and client.

Home Page: https://pypi.python.org/pypi/pytest-postgresql/

License: GNU Lesser General Public License v3.0

Python 100.00%
python pytest-plugin postgresql hacktoberfest

pytest-postgresql's Introduction

image

pytest-postgresql

Latest PyPI version

Wheel Status

Supported Python Versions

License

What is this?

This is a pytest plugin, that enables you to test your code that relies on a running PostgreSQL Database. It allows you to specify fixtures for PostgreSQL process and client.

How to use

Warning

Tested on PostgreSQL versions >= 10. See tests for more details.

Install with:

pip install pytest-postgresql

You will also need to install psycopg. See its installation instructions. Note that this plugin requires psycopg version 3. It is possible to simultaneously install version 3 and version 2 for libraries that require the latter (see those instructions).

Plugin contains three fixtures:

  • postgresql - it's a client fixture that has functional scope. After each test it ends all leftover connections, and drops test database from PostgreSQL ensuring repeatability. This fixture returns already connected psycopg connection.
  • postgresql_proc - session scoped fixture, that starts PostgreSQL instance at it's first use and stops at the end of the tests.
  • postgresql_noproc - a noprocess fixture, that's connecting to already running postgresql instance. For example on dockerized test environments, or CI providing postgresql services

Simply include one of these fixtures into your tests fixture list.

You can also create additional postgresql client and process fixtures if you'd need to:

from pytest_postgresql import factories

postgresql_my_proc = factories.postgresql_proc(
    port=None, unixsocketdir='/var/run')
postgresql_my = factories.postgresql('postgresql_my_proc')

Note

Each PostgreSQL process fixture can be configured in a different way than the others through the fixture factory arguments.

Sample test

def test_example_postgres(postgresql):
    """Check main postgresql fixture."""
    cur = postgresql.cursor()
    cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
    postgresql.commit()
    cur.close()

If you want the database fixture to be automatically populated with your schema there are two ways:

  1. client fixture specific
  2. process fixture specific

Both are accepting same set of possible loaders:

  • sql file path
  • loading function import path (string)
  • actual loading function

That function will receive host, port, user, dbname and password kwargs and will have to perform connection to the database inside. However, you'll be able to run SQL files or even trigger programmatically database migrations you have.

Client specific loads the database each test

from pathlib import Path
postgresql_my_with_schema = factories.postgresql(
    'postgresql_my_proc',
    load=[Path("schemafile.sql"), Path("otherschema.sql"), "import.path.to.function", "import.path.to:otherfunction", load_this]
)

Warning

This way, the database will still be dropped each time.

The process fixture performs the load once per test session, and loads the data into the template database. Client fixture then creates test database out of the template database each test, which significantly speeds up the tests.

from pathlib import Path
postgresql_my_proc = factories.postgresql_proc(
    load=[Path("schemafile.sql"), Path("otherschema.sql"), "import.path.to.function", "import.path.to:otherfunction", load_this]
)
pytest --postgresql-populate-template=path.to.loading_function --postgresql-populate-template=path.to.other:loading_function --postgresql-populate-template=path/to/file.sql

The loading_function from example will receive , and have to commit that.

Connecting to already existing postgresql database

Some projects are using already running postgresql servers (ie on docker instances). In order to connect to them, one would be using the postgresql_noproc fixture.

postgresql_external = factories.postgresql('postgresql_noproc')

By default the postgresql_noproc fixture would connect to postgresql instance using 5432 port. Standard configuration options apply to it.

These are the configuration options that are working on all levels with the postgresql_noproc fixture:

Configuration

You can define your settings in three ways, it's fixture factory argument, command line option and pytest.ini configuration option. You can pick which you prefer, but remember that these settings are handled in the following order:

  • Fixture factory argument
  • Command line option
  • Configuration option in your pytest.ini file
Configuration options
PostgreSQL option Fixture factory argument Command line option pytest.ini option Noop process fixture Default
Path to executable executable --postgresql-exec postgresql_exec
/usr/lib/postgresql/13/bin/pg_ctl
host host --postgresql-host postgresql_host yes 127.0.0.1
port port --postgresql-port postgresql_port yes (5432) random
postgresql user user --postgresql-user postgresql_user yes postgres
password password --postgresql-password postgresql_password yes
Starting parameters (extra pg_ctl arguments) startparams --postgresql-startparams postgresql_startparams
-w
Postgres exe extra arguments (passed via pg_ctl's -o argument) postgres_options --postgresql-postgres-options postgresql_postgres_options
Location for unixsockets unixsocket --postgresql-unixsocketdir postgresql_unixsocketdir
$TMPDIR
Database name which will be created by the fixtures dbname --postgresql-dbname postgresql_dbname yes, however with xdist an index is being added to name, resulting in test0, test1 for each worker. test
Default Schema either in sql files or import path to function that will load it (list of values for each) load --postgresql-load postgresql_load yes
PostgreSQL connection options options --postgresql-options postgresql_options yes

Example usage:

  • pass it as an argument in your own fixture

    postgresql_proc = factories.postgresql_proc(
        port=8888)
  • use --postgresql-port command line option when you run your tests

    py.test tests --postgresql-port=8888
  • specify your port as postgresql_port in your pytest.ini file.

    To do so, put a line like the following under the [pytest] section of your pytest.ini:

    [pytest]
    postgresql_port = 8888

Examples

Populating database for tests

With SQLAlchemy

This example shows how to populate database and create an SQLAlchemy's ORM connection:

Sample below is simplified session fixture from pyramid_fullauth tests:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.pool import NullPool
from zope.sqlalchemy import register


@pytest.fixture
def db_session(postgresql):
    """Session for SQLAlchemy."""
    from pyramid_fullauth.models import Base

    connection = f'postgresql+psycopg2://{postgresql.info.user}:@{postgresql.info.host}:{postgresql.info.port}/{postgresql.info.dbname}'

    engine = create_engine(connection, echo=False, poolclass=NullPool)
    pyramid_basemodel.Session = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
    pyramid_basemodel.bind_engine(
        engine, pyramid_basemodel.Session, should_create=True, should_drop=True)

    yield pyramid_basemodel.Session

    transaction.commit()
    Base.metadata.drop_all(engine)


@pytest.fixture
def user(db_session):
    """Test user fixture."""
    from pyramid_fullauth.models import User
    from tests.tools import DEFAULT_USER

    new_user = User(**DEFAULT_USER)
    db_session.add(new_user)
    transaction.commit()
    return new_user


def test_remove_last_admin(db_session, user):
    """
    Sample test checks internal login, but shows usage in tests with SQLAlchemy
    """
    user = db_session.merge(user)
    user.is_admin = True
    transaction.commit()
    user = db_session.merge(user)

    with pytest.raises(AttributeError):
        user.is_admin = False

Note

See the original code at pyramid_fullauth's conftest file. Depending on your needs, that in between code can fire alembic migrations in case of sqlalchemy stack or any other code

Maintaining database state outside of the fixtures

It is possible and appears it's used in other libraries for tests, to maintain database state with the use of the pytest-postgresql database managing functionality:

For this import DatabaseJanitor and use its init and drop methods:

import pytest
from pytest_postgresql.janitor import DatabaseJanitor

@pytest.fixture
def database(postgresql_proc):
    # variable definition

    janitor = DatabaseJanitor(
        postgresql_proc.user,
        postgresql_proc.host,
        postgresql_proc.port,
        "my_test_database",
        postgresql_proc.version,
        password="secret_password",
    )
    janitor.init()
    yield psycopg2.connect(
        dbname="my_test_database",
        user=postgresql_proc.user,
        password="secret_password",
        host=postgresql_proc.host,
        port=postgresql_proc.port,
    )
    janitor.drop()

or use it as a context manager:

import pytest
from pytest_postgresql.janitor import DatabaseJanitor

@pytest.fixture
def database(postgresql_proc):
    # variable definition

    with DatabaseJanitor(
        postgresql_proc.user,
        postgresql_proc.host,
        postgresql_proc.port,
        "my_test_database",
        postgresql_proc.version,
        password="secret_password",
    ):
        yield psycopg2.connect(
            dbname="my_test_database",
            user=postgresql_proc.user,
            password="secret_password",
            host=postgresql_proc.host,
            port=postgresql_proc.port,
        )

Note

DatabaseJanitor manages the state of the database, but you'll have to create connection to use in test code yourself.

You can optionally pass in a recognized postgresql ISOLATION_LEVEL for additional control.

Note

See DatabaseJanitor usage in python's warehouse test code https://github.com/pypa/warehouse/blob/5d15bfe/tests/conftest.py#L127

Connecting to Postgresql (in a docker)

To connect to a docker run postgresql and run test on it, use noproc fixtures.

docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres

This will start postgresql in a docker container, however using a postgresql installed locally is not much different.

In tests, make sure that all your tests are using postgresql_noproc fixture like that:

from pytest_postgresql import factories


postgresql_in_docker = factories.postgresql_noproc()
postgresql = factories.postgresql("postgresql_in_docker", dbname="test")


def test_postgres_docker(postgresql):
    """Run test."""
    cur = postgresql.cursor()
    cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
    postgresql.commit()
    cur.close()

And run tests:

pytest --postgresql-host=172.17.0.2 --postgresql-password=mysecretpassword

Basic database state for all tests

If you've got several tests that require common initialisation, you can to define a load and pass it to your custom postgresql process fixture:

import pytest_postgresql.factories
def load_database(**kwargs):
    db_connection: connection = psycopg2.connect(**kwargs)
    with db_connection.cursor() as cur:
        cur.execute("CREATE TABLE stories (id serial PRIMARY KEY, name varchar);")
        cur.execute(
            "INSERT INTO stories (name) VALUES"
            "('Silmarillion'), ('Star Wars'), ('The Expanse'), ('Battlestar Galactica')"
        )
        db_connection.commit()

postgresql_proc = factories.postgresql_proc(
    load=[load_database],
)

postgresql = factories.postgresql(
    "postgresql_proc",
)

The way this will work is that the process fixture will populate template database, which in turn will be used automatically by client fixture to create a test database from scratch. Fast, clean and no dangling transactions, that could be accidentally rolled back.

Same approach will work with noproces fixture, while connecting to already running postgresql instance whether it'll be on a docker machine or running remotely or locally.

Using SQLAlchemy to initialise basic database state

How to use SQLAlchemy for common initialisation:

def load_database(**kwargs):
    connection = f"postgresql+psycopg2://{kwargs['user']}:@{kwargs['host']}:{kwargs['port']}/{kwargs['dbname']}"
    engine = create_engine(connection)
    Base.metadata.create_all(engine)
    session = scoped_session(sessionmaker(bind=engine))
    # add things to session
    session.commit()

postgresql_proc = factories.postgresql_proc(load=[load_database])

postgresql = factories.postgresql('postgresql_proc') # still need to check if this is actually needed or not

@pytest.fixture
def dbsession(postgresql):
    connection = f'postgresql+psycopg2://{postgresql.info.user}:@{postgresql.info.host}:{postgresql.info.port}/{postgresql.info.dbname}'
    engine = create_engine(connection)

    session = scoped_session(sessionmaker(bind=engine))

    yield session
    # 'Base.metadata.drop_all(engine)' here specifically does not work. It is also not needed. If you leave out the session.close()
    # all the tests still run, but you get a warning/error at the end of the tests.
    session.close()

Release

Install pipenv and --dev dependencies first, Then run:

pipenv run tbump [NEW_VERSION]

pytest-postgresql's People

Contributors

adamchainz avatar ashokdelphia avatar cool-rr avatar ctholho avatar damianskrzypczak avatar dependabot-preview[bot] avatar dependabot[bot] avatar ericdasse-stonal avatar fizyk avatar github-actions[bot] avatar hugovk avatar hynek avatar jaredkeil avatar jcpunk avatar jdavcs avatar jorenham avatar kianmeng avatar lpsinger avatar merger-application[bot] avatar michael-k avatar mivade avatar mmaslowskicc avatar nackjicholson avatar ohayak avatar rafmagns-skepa-dreag avatar requires avatar stpierre avatar stranger6667 avatar tl24 avatar zoowirt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pytest-postgresql's Issues

UnboundLocalError when attempting to connect as user requiring a password

What action do you want to perform

I attempt to connect to a password-protected server via NoopExecutor.

What are the results

This gives a confusing error (I deleted the venv name from the output):

self = <pytest_postgresql.factories.NoopExecutor object at 0x7fbe5d4df760>
    @property
    def version(self):
        """Get postgresql's version."""
        if self._version:
            return self._version
        try:
>           connection = psycopg2.connect(
                dbname='postgres',
                user=self.user,
                host=self.host,
                port=self.port,
                options=self.options
            )
env/lib/python3.8/site-packages/pytest_postgresql/factories.py:58:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
dsn = "dbname=postgres user=dbuser host=localhost port=5432 options=''", connection_factory = None, cursor_factory = None
kwargs = {'dbname': 'postgres', 'host': 'localhost', 'options': '', 'port': 5432, ...}, kwasync = {}
    def connect(dsn=None, connection_factory=None, cursor_factory=None, **kwargs):
        """
        Create a new database connection.

        The connection parameters can be specified as a string:

            conn = psycopg2.connect("dbname=test user=postgres password=secret")

        or using a set of keyword arguments:

            conn = psycopg2.connect(database="test", user="postgres", password="secret")

        Or as a mix of both. The basic connection parameters are:

        - *dbname*: the database name
        - *database*: the database name (only as keyword argument)
        - *user*: user name used to authenticate
        - *password*: password used to authenticate
        - *host*: database host address (defaults to UNIX socket if not provided)
        - *port*: connection port number (defaults to 5432 if not provided)

        Using the *connection_factory* parameter a different class or connections
        factory can be specified. It should be a callable object taking a dsn
        argument.

        Using the *cursor_factory* parameter, a new default cursor factory will be
        used by cursor().

        Using *async*=True an asynchronous connection will be created. *async_* is
        a valid alias (for Python versions where ``async`` is a keyword).

        Any other keyword parameter will be passed to the underlying client
        library: the list of supported parameters depends on the library version.

        """
        kwasync = {}
        if 'async' in kwargs:
            kwasync['async'] = kwargs.pop('async')
        if 'async_' in kwargs:
            kwasync['async_'] = kwargs.pop('async_')

        if dsn is None and not kwargs:
            raise TypeError('missing dsn and no parameters')

        dsn = _ext.make_dsn(dsn, **kwargs)
>       conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
E       psycopg2.OperationalError: fe_sendauth: no password supplied
env/lib/python3.8/site-packages/psycopg2/__init__.py:126: OperationalError
During handling of the above exception, another exception occurred:
request = <SubRequest 'postgresql_external' for <Function test_use_the_db>>
    @pytest.fixture
    def postgresql_factory(request):
        """
        Fixture factory for PostgreSQL.

        :param FixtureRequest request: fixture request object
        :rtype: psycopg2.connection
        :returns: postgresql client
        """
        config = get_config(request)
        if not psycopg2:
            raise ImportError(
                'No module named psycopg2. Please install either '
                'psycopg2 or psycopg2-binary package for CPython '
                'or psycopg2cffi for Pypy.'
            )
        proc_fixture = request.getfixturevalue(process_fixture_name)

        # _, config = try_import('psycopg2', request)
        pg_host = proc_fixture.host
        pg_port = proc_fixture.port
        pg_user = proc_fixture.user
        pg_options = proc_fixture.options
        pg_db = db_name or config['dbname']

        with DatabaseJanitor(
>               pg_user, pg_host, pg_port, pg_db, proc_fixture.version
        ):
env/lib/python3.8/site-packages/pytest_postgresql/factories.py:282:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <pytest_postgresql.factories.NoopExecutor object at 0x7fbe5d4df760>
    @property
    def version(self):
        """Get postgresql's version."""
        if self._version:
            return self._version
        try:
            connection = psycopg2.connect(
                dbname='postgres',
                user=self.user,
                host=self.host,
                port=self.port,
                options=self.options
            )
            version = str(connection.server_version)
            self._version = parse_version(
                '.'.join([
                    version[i: i+2] for i in range(0, len(version), 2)
                    if int(version[i: i+2])
                ])
            )
            return self._version
        finally:
>           connection.close()
E           UnboundLocalError: local variable 'connection' referenced before assignment
env/lib/python3.8/site-packages/pytest_postgresql/factories.py:74: UnboundLocalError

Since https://github.com/ClearcodeHQ/pytest-postgresql/blob/master/src/pytest_postgresql/factories.py#L58 is in try/finally and raises an exception but no connection is assigned to be closed.

What are the expected results

connection.close() runs only if we have a connection.

psycopg2.OperationalError: fe_sendauth: no password supplied is the only exception raised.

Fix race conditions during database drop when other processes connect after killing connections

What action do you want to perform

Just use the PostgreSQL database fixture in tests with session-scoped fixtures running processes that randomly connect to the database during tests.

What are the results

Dropping the database fails, since it has other connections. All following tests fail at setup, since the database exists.

What are the expected results

Dropping the database succeeds, the following tests pass.

Probably using https://www.postgresql.org/docs/9.5/static/catalog-pg-database.html's datallowconn to disallow connections before killing them in drop_postgresql_database will fix it.

DatabaseJanitor ignores password

Hi all

Just stumpled upon something. A PostgreSQL instance runs in a Docker container. The instance requires password. But when using the postgresql_noproc factory with a password supplied as factory parameter, it results in a psycopg2.OperationalError: fe_sendauth: no password supplied

The problems seems to be that the password parameter is not actually passed to DatabaseJanitor inside the factory:

with DatabaseJanitor(
pg_user, pg_host, pg_port, pg_db, proc_fixture.version
):

Altough DatabaseJanitor passes the password parameter to psycopg2 when used as a context manager:

@contextmanager
def cursor(self) -> cursor:
"""Return postgresql cursor."""
conn = psycopg2.connect(
dbname='postgres',
user=self.user,
password=self.password,
host=self.host,
port=self.port,
)

password is set to None by default:

class DatabaseJanitor:
"""Manage database state for specific tasks."""
def __init__(
self,
user: str,
host: str,
port: str,
db_name: str,
version: Union[str, float, Version],
password: str = None
) -> None:

Let me know if that is indeed a bug or if I can provide further details.

Thanks!

Support deleting the database on start

What action do you want to perform

I use postgresql_noproc and my tests crash with a segmentation fault in the code that I'm testing.

I attempt to rerun them to see if they fail again or to debug them.

What are the results

The tests database from previous run already exists and the fixture fails to create it:

>       with DatabaseJanitor(
                pg_user, pg_host, pg_port, pg_db, proc_fixture.version
        ):

../../.local/share/virtualenvs/venvname/lib/python3.8/site-packages/pytest_postgresql/factories.py:281: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../.local/share/virtualenvs/venvname/lib/python3.8/site-packages/pytest_postgresql/janitor.py:89: in __enter__
    self.init()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pytest_postgresql.janitor.DatabaseJanitor object at 0x7ffff25220a0>

    def init(self) -> None:
        """Create database in postgresql."""
        with self.cursor() as cur:
>           cur.execute('CREATE DATABASE "{}";'.format(self.db_name))
E           psycopg2.errors.DuplicateDatabase: database "tests" already exists

../../.local/share/virtualenvs/venvname/lib/python3.8/site-packages/pytest_postgresql/janitor.py:55: DuplicateDatabase

What are the expected results

I somehow configure pytest-postgresql that it's not a mistake and it drops the test database before creating it, just like it would drop it at the end of the test.

Not dropping the database by default is good, since I could have accidentally configured it to use a more useful database with some data I want to keep.

Invalid database status detection system

The issue is related to this peace of code located in executor.py:

    def wait_for_postgres(self):
        """Wait for postgresql being started."""
        if '-w' not in self.startparams:
            return
        # wait until logfile is created
        while not os.path.isfile(self.logfile):
            time.sleep(1)

        start_info = 'database system is ready to accept connections'
        # wait for expected message.
        while 1:
            with open(self.logfile, 'r') as content_file:
                content = content_file.read()
                if start_info in content:
                    break
            time.sleep(1)

First things first, there should be some timeout out of this loop. My pytest tests were freezing because of it not working and I didn't know what is happening.
And the reason why they where freezing is that you are detecting that the database is up by searching for the log message. Great, but... the message is different in each available language. At first I had a default language set to English, but after i reinstalled postgres package in my system, it switched to Polish and my tests suddenly stopped working. That's because my log file looks like that:

2020-07-31 16:07:21.350 CEST [13984] DZIENNIK:  starting PostgreSQL 12.3 (Ubuntu 12.3-1.pgdg16.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609, 64-bit
2020-07-31 16:07:21.350 CEST [13984] DZIENNIK:  listening on IPv6 address "::1", port 19450
2020-07-31 16:07:21.350 CEST [13984] DZIENNIK:  listening on IPv4 address "127.0.0.1", port 19450
2020-07-31 16:07:21.350 CEST [13984] DZIENNIK:  listening on Unix socket "/tmp/.s.PGSQL.19450"
2020-07-31 16:07:21.359 CEST [13985] DZIENNIK:  system bazy danych zostaล‚ zamkniฤ™ty 2020-07-31 16:07:21 CEST
2020-07-31 16:07:21.362 CEST [13984] DZIENNIK:  system bazy danych jest gotowy do przyjmowania poล‚ฤ…czeล„

The last message should be of course the one you are looking for. Do you have any idea how to fix that? Thanks.

Add option for username to run `pg_ctl` commands as

What action do you want to perform

I would like to be able to use this test library in a Docker container as part of a CI/CD pipeline

What are the results

The fixture raised an error because PSQL doesn't want to run as root (the default in a Docker container):

E               subprocess.CalledProcessError: Command '['/usr/lib/postgresql/11/bin/pg_ctl', 'initdb', '--pgdata', '/tmp/postgresqldata.16728', '-o', '--username=postgres --auth=trust']' returned non-zero exit status 1.
/usr/local/lib/python3.7/subprocess.py:512: CalledProcessError
---------------------------- Captured stderr setup -----------------------------
pg_ctl: cannot be run as root
Please log in (using, e.g., "su") as the (unprivileged) user that will
own the server process.
________________________ ERROR at setup of test_sample _________________________

What are the expected results

For the fixture to be configured to (create and) use a user that is not root. As you're using subprocess.check_output, you could simply (create a user and) add the user kwarg which gets proxied to subprocess.Popen

Use the tmpdir built-in fixture for logs

Use the built-in tmpdir fixture instead of /tmp/ directly for storing files such as postgresql logs. The main reason is that the temporary directories managed by pytest are automatically rotated, so you won't end up with hundreds of left-over files in /tmp/...

Support transactional testing for fast tests with shared setup

When writing tests, developers need to ensure that tests are both isolated from each other and fast. These two goals come into conflict when tests require interacting with a database, since tests typically require the database to be in a certain state. This state may include:

  • Database schema
  • Specific data in the database

Bringing the database to the required state from scratch takes time. Additionally, to ensure isolation, the database is typically completely wiped between each test. This means that every test needs to bring the database to a specific state, even if some part of that state is shared with other tests.

An alternative to resetting the database between each test is using the built-in isolation capabilities of the database to prevent tests from affecting each other. This is called "transactional testing", and often uses PostgreSQL-specific features such as SAVEPOINTs to roll-back changes made by a test before the next test runs, without completely resetting the database.

One example of a library implementing this approach is pytest-flask-sqlalchemy. However, that library's use case is fairly narrowly defined to only projects using both Flask and SQLAlchemy.

I think it would be really awesome if pytest-postgresql were to support transactional testing use cases (perhaps using scoped fixtures or fixture decorators). Do you think this would be within the scope of pytest-postgresql, or would this be better-suited as a separate library?

Is this expected?

What action do you want to perform

I want to create database on the fly to perform multiple unit tests against it. I am trying this :

>>> from pytest_postgresql.factories import DatabaseJanitor
>>> DatabaseJanitor('postgres', 'localhost', 1234, 'foo', 0.0).init()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/litvinse/venv/lib/python3.6/site-packages/pytest_postgresql/janitor.py", line 57, in init
    with self.cursor() as cur:
  File "/usr/lib64/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "/home/litvinse/venv/lib/python3.6/site-packages/pytest_postgresql/janitor.py", line 87, in cursor
    port=self.port,
  File "/home/litvinse/venv/lib/python3.6/site-packages/psycopg2/__init__.py", line 127, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP/IP connections on port 12345?

Is this expected? The doc is not quite clear to me. Looking at the code of janitor.py indeed I do not see any code spawning postgersql process. So what is the proper way to do it.

Set locale for running database processes

From @enkore on May 4, 2016 10:9

We had the issue here that our tests would hang forever or fail in strange ways sometimes (nonsensical errors implying that pgsql was not running while it was running etc). This was tracked down to the locale being set to something not-English, which caused the log messages of pgsql to be translated as well (e.g. into German).

Bug description: dbfixtures fails when wait_for_postgres is called as part of the fixture initialization and tries to wait for database system is ready to accept connections appearing the log file (pgsql would start up fine, but print Datenbanksystem ist bereit, um Verbindungen anzunehmen instead, or some other localized text).

Proposed fix: set LC_ALL=C in the database process environment, since that's what dbfixtures expects and needs to work. This is also our workaround (export LC_ALL=C before py.test in the same env/shell).


By the way, I suspect that the wait_for_postgres mechanism isn't really doing anything after the first start; since the log file is not deleted automatically it will always contain the startup message from the first start, hence the check will pass immediately, independent of pgsql actually having printed that message during this run:

Copied from original issue: ClearcodeHQ/pytest-dbfixtures#150

pytest-postgresql 2.0.0 broken on macOS (AttributeError: module 'select' has no attribute 'poll')

What action do you want to perform

I have a minimal test file, test_postgres.py:

def test_postgres(postgresql_proc):
    pass

and I run pytest test_postgres.py on macOS 10.14.6.

What are the results

With pytest-postgresql I get the following error message.

This is broken in pytest-postgresql 2.0.0. If I downgrade to pytest-postgresql <= 1.4.1 and mirakuru <= 1.1.0, it's not broken.

(test) bash-3.2$ pytest test_postgres.py 
Traceback (most recent call last):
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/bin/pytest", line 10, in <module>
    sys.exit(main())
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/config/__init__.py", line 58, in main
    config = _prepareconfig(args, plugins)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/config/__init__.py", line 208, in _prepareconfig
    pluginmanager=pluginmanager, args=args
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/hooks.py", line 289, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/manager.py", line 87, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/manager.py", line 81, in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/helpconfig.py", line 89, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/config/__init__.py", line 716, in pytest_cmdline_parse
    self.parse(args)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/config/__init__.py", line 924, in parse
    self._preparse(args, addopts=addopts)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/config/__init__.py", line 870, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pluggy/manager.py", line 292, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/importlib_metadata/__init__.py", line 90, in load
    module = import_module(match.group('module'))
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pytest_postgresql/plugin.py", line 21, in <module>
    from pytest_postgresql import factories
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pytest_postgresql/factories.py", line 33, in <module>
    from pytest_postgresql.executor import PostgreSQLExecutor
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/pytest_postgresql/executor.py", line 28, in <module>
    from mirakuru import TCPExecutor
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/mirakuru/__init__.py", line 24, in <module>
    from mirakuru.output import OutputExecutor
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/mirakuru/output.py", line 30, in <module>
    class OutputExecutor(SimpleExecutor):
  File "/Users/lpsinger/.local/share/virtualenvs/test-6C_DLGjE/lib/python3.7/site-packages/mirakuru/output.py", line 107, in OutputExecutor
    def _wait_for_output(self, *polls: Tuple[select.poll, IO[Any]]) -> bool:
AttributeError: module 'select' has no attribute 'poll'

What are the expected results

(test) bash-3.2$ pytest test_postgres.py 
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-5.1.1, py-1.8.0, pluggy-0.12.0
rootdir: /private/tmp/test
plugins: postgresql-1.4.1
collected 1 item                                                               

test_postgres.py .                                                       [100%]

============================== 1 passed in 1.20s ===============================

postgresql 10 support

What action do you want to perform

I've run tests on 9.6 successfully. upgraded version to 10, re-run tests.

What are the results

test crashes at pg version check in executor.py, no test relying on db connection performs.

What are the expected results

tests running as in 9.6

workaround/potential fix

Now, my workaround is to change version regex to account for that extra digit
PostgreSQLExecutor::version - I've added extra \d?

I haven't noticed any issues so far, as I'm not aware of any backwards incompatible changes in 10.0. yet I'm not sure if it's forward compatible just like that, as my test suite is rather small, and doesn't span all pg features.

remove path.py dependency

Path.py has a really nice api, however it's use can safely be replace by stdlib limiting the number of dependencies pytest-postgresql has

TypeError: '>=' not supported between instances of 'float' and 'Version'

Tried upgrading to 2.0.0 and same problem

    @request.addfinalizer
    def drop_database():
>       drop_postgresql_database(pg_user, pg_host, pg_port, pg_db, 11.2)

qc/test/conftest.py:46: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

user = None, host = None, port = None, db_name = 'qc_test', version = 11.2

    def drop_postgresql_database(user, host, port, db_name, version):
        """
        Drop databse in postgresql.
    
        :param str user: postgresql username
        :param str host: postgresql host
        :param str port: postgresql port
        :param str db_name: database name
        :param packaging.version.Version version: postgresql version number
        """
        conn = psycopg2.connect(user=user, host=host, port=port)
        conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
        cur = conn.cursor()
        # We cannot drop the database while there are connections to it, so we
        # terminate all connections first while not allowing new connections.
>       if version >= parse_version('9.2'):
E       TypeError: '>=' not supported between instances of 'float' and 'Version'

../../../../.virtualenvs/qc-backend-qjdNaO4n/lib/python3.7/site-packages/pytest_postgresql/factories.p:
84: TypeError                                                                                         

Make temporary data directories configurable

From @enkore on May 4, 2016 10:20

Currently these are hardcoded to paths like /tmp/<dbname>.<port> or similar paths (e.g. /tmp/postgresqldata.5433). It would be nice if this could be influenced in some way, e.g. an environment variable.

Use case: This would allow to put these temporary paths in the working directory of CI jobs (travis, jenkins, ...), which rules out any issues that can occur with multiple jobs running in parallel. It also would make clean-up in case of crashes/errors easier (since the workdir is usually either automatically deleted after or before each build, or can be easily deleted from within the CI software).

Copied from original issue: ClearcodeHQ/pytest-dbfixtures#151

perform test queries to check database info

What action do you want to perform

Hey I was wondering if there is a way to perform pytests about the data inside the database.
I mean to query data from the database and do a simple test all in a docker instance. I perform a script to fill the database with some info, then i want to test if the info match some numbers everything in a docker

What are the results

What are the expected results

How to enable the plugin for gitlab-CI / dockerized tests

# .gitlab-ci.yml

image: python:3.6

services:
  ## https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#available-settings-for-services
  - name: kartoza/postgis:11.5-2.5
    alias: db

variables:

  # In gitlab-ci, the host connection name should be the service name or alias
  # (with all / replaced with - or _) and everything after the colon ( : ) is stripped
  POSTGRES_HOST: db  # try to use service-alias (similar to docker-compose)
  POSTGRES_PORT: 5432
  ALLOW_IP_RANGE: 0.0.0.0/0
  POSTGRES_MULTIPLE_EXTENSIONS: postgis,postgis_topology

  # Using the admin-db and user allows the CI to do anything
  POSTGRES_DB: postgres
  POSTGRES_USER: postgres
  POSTGRES_PASS: ''  # no passwd on gitlab-CI
  SQLALCHEMY_DATABASE_URI: "postgres://${POSTGRES_USER}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"

# etc etc

This docker provision seems to work for creating a db instance, but this plugin tries to create another one. However, the posgresql factory can't find the pg_ctl to start a new instance.

>               raise child_exception_type(errno_num, err_msg, err_filename)
E               FileNotFoundError: [Errno 2] No such file or directory: '/usr/lib/postgresql/11/bin/pg_ctl': '/usr/lib/postgresql/11/bin/pg_ctl'
/usr/local/lib/python3.6/subprocess.py:1364: FileNotFoundError

It seems like this plugin needs an installation of postgresql (e.g. apt install).

Weird error at shutdown

I'm getting the following error at the end of pytest's output when a test fails. It doesn't prevent a successful pass when everything is OK, though.

********************************************************************************
Exception while deleting Executor. 'It is strongly suggested that you use
it as a context manager instead.
********************************************************************************
Exception ignored in: <bound method SimpleExecutor.__del__ of <pytest_postgresql.executor.PostgreSQLExecutor: "/usr/bin/p..." 0x7fe758d124a8>>
Traceback (most recent call last):
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/mirakuru/base.py", line 361, in __del__
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/mirakuru/base.py", line 314, in kill
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/mirakuru/base.py", line 227, in _kill_all_kids
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/mirakuru/base_env.py", line 52, in processes_with_env_psutil
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/psutil/__init__.py", line 1426, in process_iter
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/psutil/__init__.py", line 1371, in pids
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/psutil/_pslinux.py", line 1324, in pids
  File "/home/lahwaacz/Bbox/pg/python3/wiki-scripts/.tox/py36/lib/python3.6/site-packages/psutil/_pslinux.py", line 214, in get_procfs_path
KeyError: 'psutil'

Configure test database through settings

postgresql fixture to set up custom database

We should have a pytest.ini, command line option to complement postgresql argument that allows to change test database name.

DatabaseJanitor.init() fails if username doesn't match an existing db name

What action do you want to perform

I have a service, 'something-backend', which has an existing dev database called something_backend with the database user something-backend. For tests, I'd like to use pytest-postgresql to make a throwaway database called something_backend_test, using the same database user.

So I want to initialize a new database where the username does not match an existing database, e.g.

from pytest_postgresql.factories import init_postgresql_database

user = 'something-backend'
host = 'db'
port = '5432'
db_name = 'something_backend_test'
init_postgresql_database(user, host, port, db_name)

or similarly calling the non-deprecated version:

DatabaseJanitor(user, host, port, db_name, 0.0).init()

What are the results

psycopg2.OperationalError: FATAL:  database "something-backend" does not exist

If I rename the user to something_backend, and have an existing database called that, then the code above will make the something_backend_test database.

What are the expected results

The database is successfully created.

I believe this is because the psycopg2.connect() call in Janitor is only passing the user, host and port, so Postgres is defaulting to a database name that matches the user's name.

I think it would be safe to pass dbname='postgres' here, and if I make that change locally, everything appears to work as expected.

C.UTF-8 locale are not supported on Centos 7

What action do you want to perform

Use pytest-postgresql version 2.5.1 on Centos 7.

What are the results

Our test suite fails with the following message when pytest-postgresql executes the pg_ctl initdb command.

sh: warning: setlocale: LC_ALL: cannot change locale (C.UTF-8)
sh: warning: setlocale: LC_ALL: cannot change locale (C.UTF-8)
sh: warning: setlocale: LC_ALL: cannot change locale (C.UTF-8)
initdb: invalid locale settings; check LANG and LC_* environment variables
pg_ctl: database system initialization failed

What are the expected results

It would be nice if pytest-postgresql would allow the locale en_US.UTF-8, since C.UTF-8 is not supported on Centos 7 and no support will be added in the future.

Unable to specify `options` kwarg to underlying psycopg2.connect()

What action do you want to perform

I would like to specify options as specified in 33.1 Database Control Functions as well as in psycopg2.connect.

Basically I want to set the search_path=myschema.

I think I could also get around this by setting an environment variable of PGOPTIONS if I really have to.

What are the results

Write now the results are, I can't use this project to run my postgresql tests because I get errors due to tables not being found.

I have my tables defined in a schema:

CREATE SCHEMA myschema;

But in my app I use the options='-c search_path=myschema' to automatically make that the default so that in all my queries I don't have to write select * from myschema.foo;. I can write select * from foo;.

I need to do the same thing for my psycopg2 connection when it's under test.

What are the expected results

To be able to send an options option through the pytest.ini or as a command line option, or as an option to the postgresql_proc factory.

PR

I'll have a PR up shortly that adds that option.

Workarounds

Somehow get my tables to just build in the public schema when I'm testing. (I'm using my same migration scripts for production to build my database under test)

Environment variable everywhere I run tests PGOPTIONS="-c search_path=myschema"

Question: Is a postgres installation needed for pytest-postgresql?

It doesn't get that clear from the setup instructions:
Do I need to install posgres (server) separately in order to use pytest-postgresql?

When I found this pytest extension I first assumed that it only mocks a connection, but it seems it creates a real test database on a real local postgres server.

Could you please clarify this?

Tests hung if logging_collector is on in postgresql.conf

In official postgresql RPMs for Centos 7 logging_collector enabled by default (in /usr/pgsql-9.6/share/postgresql.conf.sample) and will catch all output to stderr.

wait_for_postgres() will wait for message 'database system is ready to accept connections' forever.

As workaround you can disable logging_collector manually:

# conftest.py
from pytest_postgresql import factories
postgresql_proc = factories.postgresql_proc(startparams='-o "-c logging_collector=off"')

Drop python 2 support

Since Python 2 reaches EOL by the end of the year, it should be safe to drop support for it.

Does not work in some earlier versions of python 3.5

What action do you want to perform

I want to use the postgresql fixture in a test. I have python 3.5.2 and pytest-postgresql 2.1.0.

What are the results

  File "/opt/venvs/test/bin/pytest", line 10, in <module>
    sys.exit(main())
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/config/__init__.py", line 58, in main
    config = _prepareconfig(args, plugins)
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/config/__init__.py", line 208, in _prepareconfig
    pluginmanager=pluginmanager, args=args
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/manager.py", line 92, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/manager.py", line 86, in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/helpconfig.py", line 89, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/config/__init__.py", line 719, in pytest_cmdline_parse
    self.parse(args)
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/config/__init__.py", line 927, in parse
    self._preparse(args, addopts=addopts)
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/config/__init__.py", line 873, in _preparse
    self.pluginmanager.load_setuptools_entrypoints("pytest11")
  File "/opt/venvs/test/lib/python3.5/site-packages/pluggy/manager.py", line 297, in load_setuptools_entrypoints
    plugin = ep.load()
  File "/opt/venvs/test/lib/python3.5/site-packages/importlib_metadata/__init__.py", line 92, in load
    module = import_module(match.group('module'))
  File "/opt/venvs/test/lib/python3.5/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 986, in _gcd_import
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/opt/venvs/test/lib/python3.5/site-packages/pytest_postgresql/plugin.py", line 21, in <module>
    from pytest_postgresql import factories
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/opt/venvs/test/lib/python3.5/site-packages/pytest_postgresql/factories.py", line 28, in <module>
    from pytest_postgresql.janitor import DatabaseJanitor, psycopg2
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "/opt/venvs/test/lib/python3.5/site-packages/_pytest/assertion/rewrite.py", line 140, in exec_module
    exec(co, module.__dict__)
  File "/opt/venvs/test/lib/python3.5/site-packages/pytest_postgresql/janitor.py", line 23, in <module>
    class DatabaseJanitor:
  File "/opt/venvs/test/lib/python3.5/site-packages/pytest_postgresql/janitor.py", line 94, in DatabaseJanitor
    exc_type: Optional[Type[BaseException]],
  File "/usr/lib/python3.5/typing.py", line 649, in __getitem__
    return Union[arg, type(None)]
  File "/usr/lib/python3.5/typing.py", line 552, in __getitem__
    dict(self.__dict__), parameters, _root=True)
  File "/usr/lib/python3.5/typing.py", line 512, in __new__
    for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
  File "/usr/lib/python3.5/typing.py", line 512, in <genexpr>
    for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
  File "/usr/lib/python3.5/typing.py", line 1077, in __subclasscheck__
    if super().__subclasscheck__(cls):
  File "/opt/venvs/test/lib/python3.5/abc.py", line 225, in __subclasscheck__
    for scls in cls.__subclasses__():
TypeError: descriptor '__subclasses__' of 'type' object needs an argument

What are the expected results

Including postgresql fixture should not cause this exception(?)

Upon a little further digging, I found that the version of mirakuru installed requires python 3.6 or later. Downgrading mirikuru to 1.1.0 and pytest-postgresql to 1.4.1 got things to work, but at the cost of installing a version of mirakuru that is incompatible according to the specifications.

In this commit: 76e6605 I see someone pinning to <2.1.0 for python3.5, possibly to fix this. However, 2.0.1 appears to be the only version that suits the hard-coded requirements, but it doesn't fix the problem. Downgrading mirakuru to 1.1.0 eliminated the traceback I reported above, but still threw the same exception at a different point in code.

Unable to set database password

Hi
Thanks for the great library. I found the issue while trying to set the password in the fixture. I tried to set the password by 2 ways, the first is by the factory object and the second by pytest.ini and in both cases the error was the same:

TypeError: can only concatenate tuple (not "str") to tuple

It seems like the flaw is located in pytest_postgresql/executor.py file in these lines:

        if self.password:
            with tempfile.NamedTemporaryFile() as password_file:
                init_directory += (
                    '--pwfile "%s"' % password_file.name
                )

Here you are trying to concatenate string to init_directory variable which as we can see here:

        init_directory = (
            self.executable, 'initdb',
            '-o "--auth=trust --username=%s"' % self.user,
            '-D %s' % self.datadir,
        )

...is a tuple.
So the problem is simply lack of the comma. I can fix this on today's evening (Polish time) if you don't mind it. I really need that to be fixed quickly.

TravisCI, made no code changes. Build started failing.

What action do you want to perform

Run tests in travis ci

What are the results

==================================== ERRORS ====================================
____________ ERROR at setup of TestPutSeries.test_create_new_source ____________
request = <SubRequest 'postgresql' for <Function 'test_create_new_source'>>
    @pytest.fixture
    def postgresql_factory(request):
        """
            Fixture factory for PostgreSQL.
    
            :param FixtureRequest request: fixture request object
            :rtype: psycopg2.connection
            :returns: postgresql client
            """
>       proc_fixture = request.getfixturevalue(process_fixture_name)
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/pytest_postgresql/factories.py:259: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/pytest_postgresql/factories.py:211: in postgresql_proc_fixture
    postgresql_ctl, pg_user, datadir
../../../virtualenv/python3.6.3/lib/python3.6/site-packages/pytest_postgresql/factories.py:101: in init_postgresql_directory
    subprocess.check_output(' '.join(init_directory), shell=True)
/opt/python/3.6.3/lib/python3.6/subprocess.py:336: in check_output
    **kwargs).stdout
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
input = None, timeout = None, check = True
popenargs = ('/usr/lib/postgresql/11/bin/pg_ctl initdb -o "--auth=trust --username=postgres" -D /tmp/postgresqldata.21515',)
kwargs = {'shell': True, 'stdout': -1}
process = <subprocess.Popen object at 0x2ae2978fca90>, stdout = b''
stderr = None, retcode = 127
    def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
        """Run command with arguments and return a CompletedProcess instance.
    
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
    
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output & stderr attributes if those streams
        were captured.
    
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
    
        There is an optional argument "input", allowing you to
        pass a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
    
        The other arguments are the same as for the Popen constructor.
    
        If universal_newlines=True is passed, the "input" argument must be a
        string and stdout/stderr in the returned object will be strings rather than
        bytes.
        """
        if input is not None:
            if 'stdin' in kwargs:
                raise ValueError('stdin and input arguments may not both be used.')
            kwargs['stdin'] = PIPE
    
        with Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input, timeout=timeout)
            except TimeoutExpired:
                process.kill()
                stdout, stderr = process.communicate()
                raise TimeoutExpired(process.args, timeout, output=stdout,
                                     stderr=stderr)
            except:
                process.kill()
                process.wait()
                raise
            retcode = process.poll()
            if check and retcode:
                raise CalledProcessError(retcode, process.args,
>                                        output=stdout, stderr=stderr)
E               subprocess.CalledProcessError: Command '/usr/lib/postgresql/11/bin/pg_ctl initdb -o "--auth=trust --username=postgres" -D /tmp/postgresqldata.21515' returned non-zero exit status 127.

What are the expected results

Tests to pass.

๐Ÿคทโ€โ™‚๏ธ Any ideas?

Regression with drop_postgresql_database

Since pytest-postgresql==2.1.0 we've gotten test failures indicating that our test database is not being properly dropped.

In #227, it looks like there is a copy/paste error, and drop_postgresql_database is calling DatabaseJanitor.init() instead of DatabaseJanitor.drop():

def drop_postgresql_database(user, host, port, db_name, version):
"""
Drop databse in postgresql.
:param str user: postgresql username
:param str host: postgresql host
:param str port: postgresql port
:param str db_name: database name
:param packaging.version.Version version: postgresql version number
"""
warn(
'drop_postgresql_database is deprecated, '
'use DatabaseJanitor.drop istead.',
DeprecationWarning
)
DatabaseJanitor(user, host, port, db_name, version).init()

Can't connect / timeout (Postgresql in docker container)?

Python 3.5.2
Postgresql 9.6.2 running inside docker container with exposed ports (so I can run psql locally)
Port: 5432

tests are blocked

(storm) โžœ  src git:(develop) โœ— pytest -v 
============================================================================================================= test session starts =============================================================================================================
platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.33, pluggy-0.4.0 -- /home/dmitry/.pyenv/versions/3.5.2/envs/storm/bin/python
cachedir: .cache
rootdir: /home/dmitry/Projects/trivver/storm/src, inifile:
plugins: xdist-1.16.0, postgresql-1.2.0, cov-2.4.0
collected 5 items 

cache/tests/lru_test.py::TestCacheLRU::test_get_session_user  ^C

So my execution stuck inside test_get_session_user()

code:

postgresql_proc = factories.postgresql_proc(user='postgres')
postgresql = factories.postgresql('postgresql_proc')


class TestCacheLRU(object):
    def test_get_session_user(self, postgresql):

        cur = postgresql.cursor()
        cur.execute(
            'CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);')
        postgresql.commit()
        cur.close()

        assert lru.get_session_user(None) is None

Postgres status checks depend on pg_ctl logs language

What action do you want to perform

If pg_ctl does not write logs in english, two status checks do not work:

if start_info in content:

if b'pg_ctl: no server running' in ex.output:

For instance, on my system the logs are in german. In this case database system is ready to accept connections is equivalent to Datenbanksystem ist bereit, um Verbindungen anzunehmen. As as result, the postgres is never recognized as running, even though it is.

It would be nice if both status checks were independent of the language in the pg_ctl logs, such that the module works for any pg_ctl language. A possible solution would be to use subprocess.getstatusoutput instead of reading the log files and check for status == 0 (running) or status == 3 (not running), as described here: https://www.postgresql.org/docs/current/app-pg-ctl.html

Issue stopping postgresql during teardown

After updating to 1.3.4 of this package I started having issues running things on Mac OSX. Looks like the issue is tied to f224d93. Specifically, the super(PostgreSQLExecutor, self).stop() addition. Haven't tried other platforms. I'm also not running as root when performing my tests.

Here is a snippet from the traceback when things go wrong:

        try:
>           os.killpg(self.process.pid, sig)
E           PermissionError: [Errno 1] Operation not permitted

.tox/py36/lib/python3.6/site-packages/mirakuru/base.py:266: PermissionError

Putting a guard around that line seems to help:

        if self.running():
            super(PostgreSQLExecutor, self).stop()

This seems okay though it does go against the "ask for forgiveness" approach. Not sure if something should really be done in the mirakuru library instead. Thanks!

Race condition between closing connections and dropping the database

What action do you want to perform

Run tests with postgresql fixture. Have services with connection pooling started in session-scoped fixtures, reconnecting on error, also randomly querying the database.

What are the results

We run:

    cur.execute(
        'SELECT pg_terminate_backend(pg_stat_activity.{0})'
        'FROM pg_stat_activity WHERE pg_stat_activity.datname = %s;'.format(
            pid_column),
        (db,))
    cur.execute('DROP DATABASE IF EXISTS {0};'.format(db))

The second query fails with connections existing. A service might have reconnected between these two queries.

What are the expected results

The tests succceed. If we really cannot drop the database once, other tests succeed.

Database creation fails when name contains hyphen

What action do you want to perform

Create a database called "qp-test"

What are the results

Failure with:

> cur.execute('CREATE DATABASE {0};'.format(db_name))
E psycopg2.ProgrammingError: syntax error at or near "-"
E LINE 1: CREATE DATABASE qp-test;

What are the expected results

Database should be created.

need help to understand documentation

hi all,

Can any one explain me what are these tests files in tests dir of the source code ?
test_executor.py, test_janitor.py, test_noopexecutor.py

what is executor, janitor and noopexecutor? how can some one use these features in his testing?

I have used factories.postgresql_proc() and factories.postgresql() in my trial project and it is working fine. But not sure about
others. I find little difficult to understand the documentation as a pytest newbie.

-Thanks

Different fixture for two environments to set up test DB either locally or on CI

What action do you want to perform

I am trying to have pytest setup a test database based on whether it runs locally with PostgreSQL installation or on our GitLab CI wihere an existing server is running.

What are the results

I tried to do that with a fixture that decides whether to use the proc or noproc factory based on a CLI argument.

import os
import tempfile

from psycopg2.extensions import connection
import pytest
from pytest_postgresql import factories
from sqlalchemy import create_engine
from sqlalchemy.engine import Engine


def pytest_addoption(parser):
    parser.addoption(
        "--remote-psql", action="store_true", help="Run it in environment where PSQL runs on network."
    )


@pytest.fixture(scope="session")
def cmdopt_remote_psql(request):
    return request.config.getoption("--remote-psql")


# Using the postgresql factories to create a postgresql fixture instance
socket_dir = tempfile.TemporaryDirectory()
postgresql_proc_factory = factories.postgresql_proc(port=None, unixsocketdir=socket_dir.name)
postgresql_noproc_factory = factories.postgresql_noproc()


@pytest.fixture(scope="session")
def postgresql_my(cmdopt_remote_psql):
    if cmdopt_remote_psql:
        return factories.postgresql("postgresql_noproc_factory")
    else:
        return factories.postgresql("postgresql_proc_factory")


@pytest.fixture(scope="function", autouse=True)
def setup_database(postgresql_my: connection) -> Engine:
    """
    See https://medium.com/@geoffreykoh/fun-with-fixtures-for-database-applications-8253eaf1a6d
    """
    def dbcreator():
        return postgresql_my.cursor().connection

    engine = create_engine("postgresql+psycopg2://", creator=dbcreator)
    return engine

This doesn't work though, because if I understand correctly, the postgresql_my fixture creates a sort of "fixture of a fixture" so then in setup_database it fails because postgresql_my is a function object, not a connection.

What are the expected results

How would you go about doing this? Any idea?

pscyopg2-binary

What action do you want to perform

Install this alongside psycopg2-binary instead of psycopg2, because it's a pre-compiled wheel.

What are the results

Since pytest-postgresql depends directly on psycopg2, this isn't possible.

What are the expected results

Can install just fine.

How can I have two connections to the tests database?

What action do you want to perform

I have a test of a flask server which is injected with the postgresql connnection object. Once it finishes serving a request, it closes that connection.

After making a request to create objects in my test database I would like to open a second connection to verify the objects are in the database. I can't seem to get this to work with the factories.

postgresql startup-wait usually doesn't do anything

From @enkore on May 5, 2016 22:18

The wait_for_postgres mechanism isn't really doing anything after the first run of tests; since the log file is not deleted automatically it will always contain the startup message from the first start, hence the check will pass immediately, independent of pgsql actually having printed that message during this run:

After tests have run, before /tmp is emptied by the system (reboot or cronjob, or manually). My observation is that the logfile is not deleted (contrary to the docstring of remove_postgresql_directory, but in line with how I read the code) when the fixture finalizer is run.

$ rm /tmp/postgresql.5433.log
$ py.test
...
== xyz passed ... ==
$ py.test
...
$ grep "database system is ready to accept connections" /tmp/postgresql.5433.log 
LOG:  database system is ready to accept connections
LOG:  database system is ready to accept connections

Proposed fix: Truncate the log file when starting a new instance in case it already exists. It makes debugging easy (since the logfile "survives" fixture teardown by default, as it does now), and doesn't create issues like this (possibly even in downstream test code looking at the log?)

(From ClearcodeHQ/pytest-dbfixtures#150 (comment) and the following)

Copied from original issue: ClearcodeHQ/pytest-dbfixtures#152

Windows support

What action do you want to perform

Hi, we are wanting to use the postgresql_proc fixture in our test suite and we ran into a few errors. Version 2.4.0 on Windows 10 and PostgreSQL version 11.

What are the results

On the creation of PostgreSQLExecutor we find it errors when calling pg_ctl due to the quotes around stderr on this line , seems Windows is not happy. By removing the quotes it managed to set up the database and run the test suite.

However it now runs into the problem of not being able to stop with os.killpg, as this function doesn't exist for Windows.

    @pytest.fixture(scope='session')
    def postgresql_proc_fixture(request, tmpdir_factory):
        """
        Process fixture for PostgreSQL.

        :param FixtureRequest request: fixture request object
        :rtype: pytest_dbfixtures.executors.TCPExecutor
        :returns: tcp executor
        """
        .
        .
        .
        # start server
        with postgresql_executor:
            postgresql_executor.wait_for_postgres()

>           yield postgresql_executor

..\..\appdata\local\pypoetry\cache\virtualenvs\trase-iwsqw52c-py3.7\lib\site-packages\pytest_postgresql\factories.py:200:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\appdata\local\pypoetry\cache\virtualenvs\trase-iwsqw52c-py3.7\lib\site-packages\mirakuru\base.py:179: in __exit__
    self.stop()
..\..\appdata\local\pypoetry\cache\virtualenvs\trase-iwsqw52c-py3.7\lib\site-packages\pytest_postgresql\executor.py:220: in stop
    super().stop(sig, exp_sig)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pytest_postgresql.executor.PostgreSQLExecutor: "C:/PROGRA~..." 0x194068a34a8>, sig = <Signals.SIGTERM: 15>, exp_sig = None

    def stop(
            self: SimpleExecutorType,
            sig: int = None,
            exp_sig: int = None
    ) -> SimpleExecutorType:
        .
        .
        .
        try:
>           os.killpg(self.process.pid, sig)
E           AttributeError: module 'os' has no attribute 'killpg'

What are the expected results

We were wondering if others have run into this issue too and if there's a way to get this fix in. Any help is appreciated as its a great plugin to use!

AttributeError: 'function' object has no attribute 'cursor'

From @tommagic on October 27, 2016 12:14

I follow the example here - http://pytest-dbfixtures.readthedocs.io/en/latest/howtouse.html for postgres -

from pytest_dbfixtures import factories

postgresql_proc2 = factories.postgresql_proc(port=9876)
postgresql2 = factories.postgresql('postgresql_proc2')
cur = postgresql2.cursor()

I get the following error -
AttributeError: 'function' object has no attribute 'cursor'

My version is - pytest-dbfixtures==0.16.0 and psycopg2==2.6.2, postgres 9.6 running locally.
What do I do wrong?

Copied from original issue: ClearcodeHQ/pytest-dbfixtures#208

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.