Skip to content

Commit dcf3c56

Browse files
committed
Drop test database from noproc fixture, if exists - closes #265
1 parent 9ba92dc commit dcf3c56

File tree

10 files changed

+143
-6
lines changed

10 files changed

+143
-6
lines changed

README.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ You can pick which you prefer, but remember that these settings are handled in t
227227
- postgresql_options
228228
- yes
229229
-
230+
* - Drop test database on start.
231+
232+
.. warning::
233+
234+
Use carefully as it might lead to unexpected results within your test suite.
235+
-
236+
- --postgresql-drop-test-database
237+
-
238+
- false
239+
-
240+
241+
230242

231243

232244
Example usage:

newsfragments/265.feature.1.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DatabaseJanitor.cursor now accepts optional parameter dbname, which defaults to `postgres`
2+
3+
This database name is used to make connection to and return a cursor.

newsfragments/265.feature.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
If a test run ended in an error that prevented proper test cleanup,
2+
developer can now use `--postgresql-drop-test-database` command line flag,
3+
to delete database from noproc fixture at the start.

pytest_postgresql/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class PostgresqlConfigDict(TypedDict):
2121
dbname: str
2222
load: List[Union[Path, str]]
2323
postgres_options: str
24+
drop_test_database: bool
2425

2526

2627
def get_config(request: FixtureRequest) -> PostgresqlConfigDict:
@@ -45,6 +46,7 @@ def get_postgresql_option(option: str) -> Any:
4546
dbname=get_postgresql_option("dbname"),
4647
load=load_paths,
4748
postgres_options=get_postgresql_option("postgres_options"),
49+
drop_test_database=request.config.getoption("postgresql_drop_test_database"),
4850
)
4951

5052

pytest_postgresql/factories/client.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from psycopg import Connection
2424
from pytest import FixtureRequest
2525

26+
from pytest_postgresql.config import get_config
2627
from pytest_postgresql.executor import PostgreSQLExecutor
2728
from pytest_postgresql.executor_noop import NoopExecutor
2829
from pytest_postgresql.janitor import DatabaseJanitor
@@ -52,14 +53,15 @@ def postgresql_factory(request: FixtureRequest) -> Iterator[Connection]:
5253
proc_fixture: Union[PostgreSQLExecutor, NoopExecutor] = request.getfixturevalue(
5354
process_fixture_name
5455
)
56+
config = get_config(request)
5557

5658
pg_host = proc_fixture.host
5759
pg_port = proc_fixture.port
5860
pg_user = proc_fixture.user
5961
pg_password = proc_fixture.password
6062
pg_options = proc_fixture.options
6163
pg_db = dbname or proc_fixture.dbname
62-
with DatabaseJanitor(
64+
janitor = DatabaseJanitor(
6365
user=pg_user,
6466
host=pg_host,
6567
port=pg_port,
@@ -68,7 +70,10 @@ def postgresql_factory(request: FixtureRequest) -> Iterator[Connection]:
6870
version=proc_fixture.version,
6971
password=pg_password,
7072
isolation_level=isolation_level,
71-
):
73+
)
74+
if config["drop_test_database"]:
75+
janitor.drop()
76+
with janitor:
7277
db_connection: Connection = psycopg.connect(
7378
dbname=pg_db,
7479
user=pg_user,

pytest_postgresql/factories/noprocess.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def postgresql_noproc_fixture(request: FixtureRequest) -> Iterator[NoopExecutor]
7272
pg_dbname = xdistify_dbname(dbname or config["dbname"])
7373
pg_options = options or config["options"]
7474
pg_load = load or config["load"]
75+
drop_test_database = config["drop_test_database"]
7576

7677
noop_exec = NoopExecutor(
7778
host=pg_host,
@@ -81,14 +82,17 @@ def postgresql_noproc_fixture(request: FixtureRequest) -> Iterator[NoopExecutor]
8182
dbname=pg_dbname,
8283
options=pg_options,
8384
)
84-
with DatabaseJanitor(
85+
janitor = DatabaseJanitor(
8586
user=noop_exec.user,
8687
host=noop_exec.host,
8788
port=noop_exec.port,
8889
template_dbname=noop_exec.template_dbname,
8990
version=noop_exec.version,
9091
password=noop_exec.password,
91-
) as janitor:
92+
)
93+
if drop_test_database is True:
94+
janitor.drop()
95+
with janitor:
9296
for load_element in pg_load:
9397
janitor.load(load_element)
9498
yield noop_exec

pytest_postgresql/janitor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,12 @@ def load(self, load: Union[Callable, str, Path]) -> None:
127127
)
128128

129129
@contextmanager
130-
def cursor(self) -> Iterator[Cursor]:
130+
def cursor(self, dbname: str = "postgres") -> Iterator[Cursor]:
131131
"""Return postgresql cursor."""
132132

133133
def connect() -> Connection:
134134
return psycopg.connect(
135-
dbname="postgres",
135+
dbname=dbname,
136136
user=self.user,
137137
password=self.password,
138138
host=self.host,

pytest_postgresql/plugin.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
_help_dbname = "Default database name"
3535
_help_load = "Dotted-style or entrypoint-style path to callable or path to SQL File"
3636
_help_postgres_options = "Postgres executable extra parameters. Passed via the -o option to pg_ctl"
37+
_help_drop_test_database = (
38+
"Drop test database in noproc and client fixture, for the cases, "
39+
"when database was not cleared due to errors in previous test runs. "
40+
"Use cautiously and not on CI."
41+
)
3742

3843

3944
def pytest_addoption(parser: Parser) -> None:
@@ -127,6 +132,13 @@ def pytest_addoption(parser: Parser) -> None:
127132
help=_help_postgres_options,
128133
)
129134

135+
parser.addoption(
136+
"--postgresql-drop-test-database",
137+
action="store_true",
138+
dest="postgresql_drop_test_database",
139+
help=_help_drop_test_database,
140+
)
141+
130142

131143
postgresql_proc = factories.postgresql_proc()
132144
postgresql_noproc = factories.postgresql_noproc()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""All tests for pytest-postgresql."""
2+
3+
from psycopg import Connection
4+
5+
from pytest_postgresql import factories
6+
7+
postgresql = factories.postgresql("postgresql_noproc")
8+
9+
10+
def test_postgres_load_override(postgresql: Connection) -> None:
11+
"""Check postgresql fixture can load one file and override database 'leftover'."""
12+
cur = postgresql.cursor()
13+
cur.execute("SELECT * FROM test;")
14+
results = cur.fetchall()
15+
assert len(results) == 1
16+
cur.close()

tests/test_postgres_options_plugin.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
from pytest import Pytester
77

88
import pytest_postgresql
9+
from pytest_postgresql.executor import PostgreSQLExecutor
10+
from pytest_postgresql.factories import postgresql_proc
11+
from pytest_postgresql.factories.noprocess import xdistify_dbname
12+
from pytest_postgresql.janitor import DatabaseJanitor
13+
from tests.loader import load_database
914

1015

1116
@pytest.fixture
@@ -41,3 +46,78 @@ def test_postgres_loader_in_cli(pointed_pytester: Pytester) -> None:
4146
test_sql_path = pointed_pytester.copy_example("test.sql")
4247
ret = pointed_pytester.runpytest(f"--postgresql-load={test_sql_path}", "test_load.py")
4348
ret.assert_outcomes(passed=1)
49+
50+
51+
postgresql_proc_to_override = postgresql_proc()
52+
53+
54+
def test_postgres_drop_test_database(
55+
postgresql_proc_to_override: PostgreSQLExecutor,
56+
pointed_pytester: Pytester,
57+
) -> None:
58+
"""Check that the database is dropped on both process and client level if argument is passed.
59+
60+
Given:
61+
Preexisting tables override_tmpl and override (created with two DatabaseJanitor instances)
62+
When:
63+
Run test that connects to the process from current process with flag to drop database
64+
specified
65+
Then:
66+
The internal pytest run will delete the database on start (and after).
67+
It Would fail if it did not. Checks are performed by trying to connect to both override_tmpl
68+
and override databases and checking resulting exceptions.
69+
"""
70+
dbname = xdistify_dbname("override")
71+
template_dbname = dbname + "_tmpl"
72+
template_janitor = DatabaseJanitor(
73+
user=postgresql_proc_to_override.user,
74+
host=postgresql_proc_to_override.host,
75+
port=postgresql_proc_to_override.port,
76+
template_dbname=template_dbname,
77+
version=postgresql_proc_to_override.version,
78+
password=postgresql_proc_to_override.password,
79+
connection_timeout=5,
80+
)
81+
template_janitor.init()
82+
template_janitor.load(load_database)
83+
assert template_janitor.template_dbname
84+
janitor = DatabaseJanitor(
85+
user=postgresql_proc_to_override.user,
86+
host=postgresql_proc_to_override.host,
87+
port=postgresql_proc_to_override.port,
88+
dbname=dbname,
89+
template_dbname=template_janitor.template_dbname,
90+
version=postgresql_proc_to_override.version,
91+
password=postgresql_proc_to_override.password,
92+
connection_timeout=5,
93+
)
94+
janitor.init()
95+
assert janitor.dbname
96+
with janitor.cursor(janitor.dbname) as cur:
97+
cur.execute("SELECT * FROM stories")
98+
res = cur.fetchall()
99+
assert len(res) == 4
100+
# Actual test happens now
101+
pointed_pytester.copy_example("test_drop_test_database.py")
102+
test_sql_path = pointed_pytester.copy_example("test.sql")
103+
ret = pointed_pytester.runpytest(
104+
f"--postgresql-load={test_sql_path}",
105+
f"--postgresql-port={postgresql_proc_to_override.port}",
106+
"--postgresql-dbname=override",
107+
"--postgresql-drop-test-database",
108+
"test_drop_test_database.py",
109+
)
110+
ret.assert_outcomes(passed=1)
111+
112+
with pytest.raises(TimeoutError) as excinfo:
113+
with janitor.cursor(janitor.dbname):
114+
pass
115+
assert hasattr(excinfo.value, "__cause__")
116+
assert f'FATAL: database "{janitor.dbname}" does not exist' in str(excinfo.value.__cause__)
117+
with pytest.raises(TimeoutError) as excinfo:
118+
with template_janitor.cursor(template_janitor.template_dbname):
119+
pass
120+
assert hasattr(excinfo.value, "__cause__")
121+
assert f'FATAL: database "{template_janitor.template_dbname}" does not exist' in str(
122+
excinfo.value.__cause__
123+
)

0 commit comments

Comments
 (0)