#33275 closed Bug (duplicate)
DiscoverRunner.setup_databases() creates db based on ordering of aliases from unordered set.
Reported by: | augb | Owned by: | nobody |
---|---|---|---|
Component: | Testing framework | Version: | 3.2 |
Severity: | Normal | Keywords: | DiscoverRunner setup_database test database |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
We use settings.DATABASES
to change which database user is used to connect to our database. For example, 'default' might use something along the lines of admin_user
for use in migrations, tests, etc. restricted_user
might be used for the actual Django web app.
Given a settings.DATABASES
config along these lines:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'my_database_name', 'USER': 'admin_user', # rest of actual config here }, 'restricted': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'my_database_name', 'USER': 'restricted_user', # rest of actual config here }, # other databases, as needed ... }
The problem I appear to be running into is that the stock test runner sometimes creates a test database using the "default" alias. At other times, it appears to create the test database using the "restricted" alias. (Actual alias names may be different, but illustrative.) For the actual db tests, we have middleware in place to decide which connection should be used for that test. (Sometimes we may want to use admin_user
in the test, sometimes restricted_user
, but this is not the current concern.)
Given our setup, the actual database to be created will be correctly identified but with the wrong database user, in some cases. (admin_user
has the ability to create a database, but restricted_user
does not.) If the 'restricted' alias is used the tests will fail since restricted_user
does not have permission to create a database.
Sometimes it picks 'default' ...
(Pdb) kwargs {'aliases': {'default', 'restricted'}} (Pdb) n Using existing test database for alias 'default'...
Sometimes it picks 'restricted' ...
(Pdb) kwargs {'aliases': {'restricted', 'default'}} (Pdb) n Using existing test database for alias 'restricted'...
In practice this seems to be hit or miss due to the apparent use of a set (which does not guarantee ordering; see: https://docs.python.org/3.8/library/stdtypes.html#set) in an ordered scenario:
Relevant code in Django:
for alias in aliases: connection = connections[alias] old_names.append((connection, db_name, first_alias is None)) # Actually create the database for the first connection if first_alias is None: first_alias = alias
from django/django/test/utils.py / setup_databases (see: https://github.com/django/django/blob/1b3c0d3b54d4ff5f75af57d3130180b1d22468e9/django/test/utils.py#L171)
django/django/test/runner.py / DiscoverRunner appears to import setup_databases from django.test.utils (see: https://github.com/django/django/blob/1b3c0d3b54d4ff5f75af57d3130180b1d22468e9/django/test/runner.py#L642)
Simply overriding the passed in aliases
kwarg with an ordered list, does not appear to have any effect. This appears to be due to pulling the alias from the connections instead of the aliases parameter in get_unique_databases_and_mirrors
:
def get_unique_databases_and_mirrors(aliases=None): """ Figure out which databases actually need to be created. Deduplicate entries in DATABASES that correspond the same database or are configured as test mirrors. Return two values: - test_databases: ordered mapping of signatures to (name, list of aliases) where all aliases share the same underlying database. - mirrored_aliases: mapping of mirror aliases to original aliases. """ if aliases is None: aliases = connections mirrored_aliases = {} test_databases = {} dependencies = {} default_sig = connections[DEFAULT_DB_ALIAS].creation.test_db_signature() for alias in connections: # elided ...
Indeed line 270 referenced above appears to be the culprit.
For our use case, either always using the 'default' alias to create the test database, truly honoring the order of aliases, or fixing the aliases kwarg would solve our problem.
We are using 3.1; however, the code references above are essentially identical and are from 3.2.
Change History (2)
comment:1 by , 3 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
comment:2 by , 3 years ago
Summary: | django.test.runner.DiscoverRunner.setup_databases() appears to create db based on ordering of aliases from unordered set → DiscoverRunner.setup_databases() creates db based on ordering of aliases from unordered set. |
---|
Duplicate of #29052 (Django 3.2+).