diff --git a/src/pytest_postgresql/config.py b/src/pytest_postgresql/config.py index 926aaa2e..cb6f760a 100644 --- a/src/pytest_postgresql/config.py +++ b/src/pytest_postgresql/config.py @@ -17,6 +17,7 @@ def get_config(request: FixtureRequest) -> dict: "dbname", "load", "postgres_options", + "use_database", ] for option in options: option_name = "postgresql_" + option diff --git a/src/pytest_postgresql/executor_noop.py b/src/pytest_postgresql/executor_noop.py index 2ca6c846..29b5ef42 100644 --- a/src/pytest_postgresql/executor_noop.py +++ b/src/pytest_postgresql/executor_noop.py @@ -40,6 +40,7 @@ def __init__( options: str, dbname: str, password: Optional[str] = None, + use_database: bool = False, ): """ Initialize nooperator executor mock. @@ -57,6 +58,7 @@ def __init__( self.options = options self.password = password self.dbname = dbname + self._use_database = use_database self._version: Any = None @property @@ -67,7 +69,7 @@ def version(self) -> Any: # could be called before self.dbname will be created. # Use default postgres database with psycopg.connect( - dbname="postgres", + dbname=self.dbname if self._use_database else "postgres", user=self.user, host=self.host, port=self.port, diff --git a/src/pytest_postgresql/factories/noprocess.py b/src/pytest_postgresql/factories/noprocess.py index 15cc1ddf..fed76295 100644 --- a/src/pytest_postgresql/factories/noprocess.py +++ b/src/pytest_postgresql/factories/noprocess.py @@ -73,6 +73,7 @@ def postgresql_noproc_fixture(request: FixtureRequest) -> Iterator[NoopExecutor] pg_dbname = xdistify_dbname(dbname or config["dbname"]) pg_options = options or config["options"] pg_load = load or config["load"] + use_database = config["use_database"] noop_exec = NoopExecutor( host=pg_host, @@ -81,6 +82,7 @@ def postgresql_noproc_fixture(request: FixtureRequest) -> Iterator[NoopExecutor] password=pg_password, dbname=pg_dbname, options=pg_options, + use_database=use_database, ) template_dbname = f"{noop_exec.dbname}_tmpl" with DatabaseJanitor( @@ -90,6 +92,7 @@ def postgresql_noproc_fixture(request: FixtureRequest) -> Iterator[NoopExecutor] dbname=template_dbname, version=noop_exec.version, password=noop_exec.password, + use_database=use_database, ) as janitor: for load_element in pg_load: janitor.load(load_element) diff --git a/src/pytest_postgresql/janitor.py b/src/pytest_postgresql/janitor.py index 0cfd6c8e..ca9e1f51 100644 --- a/src/pytest_postgresql/janitor.py +++ b/src/pytest_postgresql/janitor.py @@ -30,6 +30,7 @@ def __init__( password: Optional[str] = None, isolation_level: "Optional[psycopg.IsolationLevel]" = None, connection_timeout: int = 60, + use_database: bool = False, ) -> None: """ Initialize janitor. @@ -44,6 +45,8 @@ def __init__( defaults to server's default :param connection_timeout: how long to retry connection before raising a TimeoutError + :param use_database: whether to use an existing database + (may imply manual cleanup) """ self.user = user self.password = password @@ -51,6 +54,7 @@ def __init__( self.port = port self.dbname = dbname self._connection_timeout = connection_timeout + self._use_database = use_database check_for_psycopg() self.isolation_level = isolation_level if not isinstance(version, Version): @@ -60,6 +64,8 @@ def __init__( def init(self) -> None: """Create database in postgresql.""" + if self._use_database: + return template_name = f"{self.dbname}_tmpl" with self.cursor() as cur: if self.dbname.endswith("_tmpl"): @@ -86,6 +92,8 @@ def drop(self) -> None: """Drop database in postgresql.""" # We cannot drop the database while there are connections to it, so we # terminate all connections first while not allowing new connections. + if self._use_database: + return with self.cursor() as cur: self._dont_datallowconn(cur, self.dbname) self._terminate_connection(cur, self.dbname) @@ -93,6 +101,8 @@ def drop(self) -> None: @staticmethod def _dont_datallowconn(cur: cursor, dbname: str) -> None: + if self._use_database: + return cur.execute(f'ALTER DATABASE "{dbname}" with allow_connections false;') @staticmethod @@ -136,7 +146,7 @@ def cursor(self) -> Iterator[cursor]: def connect() -> connection: return psycopg.connect( - dbname="postgres", + dbname=self.dbname if self._use_database else "postgres", user=self.user, password=self.password, host=self.host, diff --git a/src/pytest_postgresql/plugin.py b/src/pytest_postgresql/plugin.py index 0262e430..62453752 100644 --- a/src/pytest_postgresql/plugin.py +++ b/src/pytest_postgresql/plugin.py @@ -34,6 +34,7 @@ _help_dbname = "Default database name" _help_load = "Dotted-style or entrypoint-style path to callable or path to SQL File" _help_postgres_options = "Postgres executable extra parameters. Passed via the -o option to pg_ctl" +_help_use_database = "Use provided database, do not create/drop it" def pytest_addoption(parser: Parser) -> None: @@ -63,6 +64,7 @@ def pytest_addoption(parser: Parser) -> None: parser.addini(name="postgresql_unixsocketdir", help=_help_unixsocketdir, default=gettempdir()) parser.addini(name="postgresql_dbname", help=_help_dbname, default="tests") + parser.addini(name="postgresql_use_database", type="bool", help=_help_use_database, default=False) parser.addini(name="postgresql_load", type="pathlist", help=_help_load, default=None) parser.addini(name="postgresql_postgres_options", help=_help_postgres_options, default="") @@ -119,6 +121,11 @@ def pytest_addoption(parser: Parser) -> None: "--postgresql-dbname", action="store", dest="postgresql_dbname", help=_help_dbname ) + parser.addoption( + "--postgresql-use-database", action="store_true", dest="postgresql_use_database", + help=_help_use_database + ) + parser.addoption("--postgresql-load", action="append", dest="postgresql_load", help=_help_load) parser.addoption(