Skip to content

Commit ae4c4e6

Browse files
committed
Updates.
Prove the checks work. Document test requirements.
1 parent 86b2519 commit ae4c4e6

File tree

4 files changed

+69
-13
lines changed

4 files changed

+69
-13
lines changed

docs/contributing.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,26 @@ Open :file:`mycoverage/index.html` in your browser and you can see a coverage su
252252

253253
There's no need to wait for Codecov to complain after you submit your PR.
254254

255+
The tests are generic and written to work with both single database and multiple database configurations. tox will run
256+
tests both ways. You can see the configurations used in tests/settings.py and tests/multi_db_settins.py.
257+
258+
When there are multiple databases defined, Django tests will not work unless they are told which database(s) to work with.
259+
For test writers this means any test must either:
260+
- instead of Django's TestCase or TransactionTestCase use the versions of those
261+
classes defined in tests/common_testing.py
262+
- when using pytest's `django_db` mark, define it like this:
263+
`@pytest.mark.django_db(databases=retrieve_current_databases())`
264+
265+
In test code, anywhere the database is referenced the Django router needs to be used exactly like the package's code.
266+
267+
.. code-block:: python
268+
269+
token_database = router.db_for_write(AccessToken)
270+
with self.assertNumQueries(1, using=token_database):
271+
# call something using the database
272+
273+
Without the 'using' option, this test fails in the multiple database scenario because 'default' will be used instead.
274+
255275
Code conventions matter
256276
-----------------------
257277

tests/common_testing.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,12 @@
33
from django.test import TransactionTestCase as DjangoTransactionTestCase
44

55

6+
# The multiple database scenario setup for these tests purposefully defines 'default' as
7+
# an empty database in order to catch any assumptions in this package about database names
8+
# and in particular to ensure there is no assumption that 'default' is a valid database.
9+
#
610
# When there are multiple databases defined, Django tests will not work unless they are
7-
# told which database(s) to work with. The multiple database scenario setup for these
8-
# tests purposefully defines 'default' as an empty database in order to catch any
9-
# assumptions in this package about database names and in particular to ensure there is
10-
# no assumption that 'default' is a valid database.
11-
# For any test that would usually use Django's TestCase or TransactionTestCase using
12-
# the classes defined here is all that is required.
13-
# In test code, anywhere the database is referenced the Django router needs to be used
14-
# exactly like the package's code.
15-
# For instance:
16-
# token_database = router.db_for_write(AccessToken)
17-
# with self.assertNumQueries(1, using=token_database):
18-
# Without the 'using' option, this test fails in the multiple database scenario because
19-
# 'default' is used.
11+
# told which database(s) to work with.
2012

2113

2214
def retrieve_current_databases():

tests/db_router.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,28 @@ def allow_relation(self, obj1, obj2, **hints):
4949
def allow_migrate(self, db, app_label, model_name=None, **hints):
5050
if app_label in apps_in_beta:
5151
return db == "beta"
52+
53+
54+
class CrossDatabaseRouter:
55+
# alpha is where the core Django models are stored including user. To keep things
56+
# simple this is where the oauth2 provider models are stored as well because they
57+
# have a foreign key to User.
58+
def db_for_read(self, model, **hints):
59+
if model._meta.model_name == "accesstoken":
60+
return "beta"
61+
return None
62+
63+
def db_for_write(self, model, **hints):
64+
if model._meta.model_name == "accesstoken":
65+
return "beta"
66+
return None
67+
68+
def allow_relation(self, obj1, obj2, **hints):
69+
if obj1._state.db == "beta" and obj2._state.db == "beta":
70+
return True
71+
return None
72+
73+
def allow_migrate(self, db, app_label, model_name=None, **hints):
74+
if model_name == "accesstoken":
75+
return db == "beta"
5276
return None

tests/test_django_checks.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.core.management import call_command
2+
from django.core.management.base import SystemCheckError
3+
from django.test import override_settings
4+
5+
from .common_testing import OAuth2ProviderTestCase as TestCase
6+
7+
8+
class DjangoChecksTestCase(TestCase):
9+
def test_checks_pass(self):
10+
call_command("check")
11+
12+
# CrossDatabaseRouter claims AccessToken is in beta while everything else is in alpha.
13+
# This will cause the database checks to fail.
14+
@override_settings(
15+
DATABASE_ROUTERS=["tests.db_router.CrossDatabaseRouter", "tests.db_router.AlphaRouter"]
16+
)
17+
def test_checks_fail_when_router_crosses_databases(self):
18+
message = "The token models are expected to be stored in the same database."
19+
with self.assertRaisesMessage(SystemCheckError, message):
20+
call_command("check")

0 commit comments

Comments
 (0)