1import pytest
2
3from plain.signals import request_finished, request_started
4
5from .. import transaction
6from ..backends.base.base import BaseDatabaseWrapper
7from ..db import close_old_connections, connections
8from .utils import (
9 setup_databases,
10 teardown_databases,
11)
12
13
14@pytest.fixture(autouse=True)
15def _db_disabled():
16 """
17 Every test should use this fixture by default to prevent
18 access to the normal database.
19 """
20
21 def cursor_disabled(self):
22 pytest.fail("Database access not allowed without the `db` fixture")
23
24 BaseDatabaseWrapper._old_cursor = BaseDatabaseWrapper.cursor
25 BaseDatabaseWrapper.cursor = cursor_disabled
26
27 yield
28
29 BaseDatabaseWrapper.cursor = BaseDatabaseWrapper._old_cursor
30
31
32@pytest.fixture(scope="session")
33def setup_db(request):
34 """
35 This fixture is called automatically by `db`,
36 so a test database will only be setup if the `db` fixture is used.
37 """
38 verbosity = request.config.option.verbose
39
40 # Set up the test db across the entire session
41 _old_db_config = setup_databases(verbosity=verbosity)
42
43 # Keep connections open during request client / testing
44 request_started.disconnect(close_old_connections)
45 request_finished.disconnect(close_old_connections)
46
47 yield _old_db_config
48
49 # Put the signals back...
50 request_started.connect(close_old_connections)
51 request_finished.connect(close_old_connections)
52
53 # When the test session is done, tear down the test db
54 teardown_databases(_old_db_config, verbosity=verbosity)
55
56
57@pytest.fixture()
58def db(setup_db):
59 # Set .cursor() back to the original implementation
60 BaseDatabaseWrapper.cursor = BaseDatabaseWrapper._old_cursor
61
62 # Keep track of the atomic blocks so we can roll them back
63 atomics = {}
64
65 for connection in connections.all():
66 # By default we use transactions to rollback changes,
67 # so we need to ensure the database supports transactions
68 if not connection.features.supports_transactions:
69 pytest.fail("Database does not support transactions")
70
71 # Clear the queries log before each test?
72 # connection.queries_log.clear()
73
74 atomic = transaction.atomic(using=connection.alias)
75 atomic._from_testcase = True # TODO remove this somehow?
76 atomic.__enter__()
77 atomics[connection] = atomic
78
79 yield setup_db
80
81 for connection, atomic in atomics.items():
82 if (
83 connection.features.can_defer_constraint_checks
84 and not connection.needs_rollback
85 and connection.is_usable()
86 ):
87 connection.check_constraints()
88
89 transaction.set_rollback(True, using=connection.alias)
90 atomic.__exit__(None, None, None)
91
92 connection.close()
93
94
95# @pytest.fixture(scope="function")
96# def transactional_db(request, _plain_db_setup):
97# BaseDatabaseWrapper.cursor = BaseDatabaseWrapper._cursor
98
99# yield
100
101# # Flush databases instead of rolling back transactions...
102
103# connections.close_all()