Skip to content

Commit

Permalink
Updates.
Browse files Browse the repository at this point in the history
Prove the checks work.
Document test requirements.
  • Loading branch information
shaleh committed Aug 16, 2024
1 parent 86b2519 commit ae4c4e6
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 13 deletions.
20 changes: 20 additions & 0 deletions docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,26 @@ Open :file:`mycoverage/index.html` in your browser and you can see a coverage su

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

The tests are generic and written to work with both single database and multiple database configurations. tox will run
tests both ways. You can see the configurations used in tests/settings.py and tests/multi_db_settins.py.

When there are multiple databases defined, Django tests will not work unless they are told which database(s) to work with.
For test writers this means any test must either:
- instead of Django's TestCase or TransactionTestCase use the versions of those
classes defined in tests/common_testing.py
- when using pytest's `django_db` mark, define it like this:
`@pytest.mark.django_db(databases=retrieve_current_databases())`

In test code, anywhere the database is referenced the Django router needs to be used exactly like the package's code.

.. code-block:: python
token_database = router.db_for_write(AccessToken)
with self.assertNumQueries(1, using=token_database):
# call something using the database
Without the 'using' option, this test fails in the multiple database scenario because 'default' will be used instead.

Code conventions matter
-----------------------

Expand Down
18 changes: 5 additions & 13 deletions tests/common_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,12 @@
from django.test import TransactionTestCase as DjangoTransactionTestCase


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


def retrieve_current_databases():
Expand Down
24 changes: 24 additions & 0 deletions tests/db_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,28 @@ def allow_relation(self, obj1, obj2, **hints):
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in apps_in_beta:
return db == "beta"


class CrossDatabaseRouter:
# alpha is where the core Django models are stored including user. To keep things
# simple this is where the oauth2 provider models are stored as well because they
# have a foreign key to User.
def db_for_read(self, model, **hints):
if model._meta.model_name == "accesstoken":
return "beta"
return None

def db_for_write(self, model, **hints):
if model._meta.model_name == "accesstoken":
return "beta"
return None

def allow_relation(self, obj1, obj2, **hints):
if obj1._state.db == "beta" and obj2._state.db == "beta":
return True
return None

def allow_migrate(self, db, app_label, model_name=None, **hints):
if model_name == "accesstoken":
return db == "beta"
return None
20 changes: 20 additions & 0 deletions tests/test_django_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.core.management import call_command
from django.core.management.base import SystemCheckError
from django.test import override_settings

from .common_testing import OAuth2ProviderTestCase as TestCase


class DjangoChecksTestCase(TestCase):
def test_checks_pass(self):
call_command("check")

# CrossDatabaseRouter claims AccessToken is in beta while everything else is in alpha.
# This will cause the database checks to fail.
@override_settings(
DATABASE_ROUTERS=["tests.db_router.CrossDatabaseRouter", "tests.db_router.AlphaRouter"]
)
def test_checks_fail_when_router_crosses_databases(self):
message = "The token models are expected to be stored in the same database."
with self.assertRaisesMessage(SystemCheckError, message):
call_command("check")

0 comments on commit ae4c4e6

Please sign in to comment.