From 06e5f7ae1639f1e275e7cc1076dc70ca3ebaa946 Mon Sep 17 00:00:00 2001 From: Harm Geerts Date: Mon, 15 Feb 2021 14:42:00 +0100 Subject: [PATCH] Fixed #29052 -- Made test database creation preserve alias order and prefer the "default" database. This fixes flushing test databases when two aliases point to the same database. Use a list() to store the test database aliases so the order remains stable by following the order of the connections. Also, always use the "default" database alias as the first alias to accommodate `migrate`. Previously `migrate` could be executed on a secondary alias which caused truncating the "default" database. --- AUTHORS | 1 + django/test/utils.py | 9 +++++++-- tests/test_runner/tests.py | 31 ++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 68783f4cfd..904165e0ba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -361,6 +361,7 @@ answer newbie questions, and generally made Django that much better: Hang Park Hannes Ljungberg Hannes Struß + Harm Geerts Hasan Ramezani Hawkeye Helen Sherwood-Taylor diff --git a/django/test/utils.py b/django/test/utils.py index 4f4ce99967..4963d2185a 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -280,9 +280,14 @@ def get_unique_databases_and_mirrors(aliases=None): # we only need to create the test database once. item = test_databases.setdefault( connection.creation.test_db_signature(), - (connection.settings_dict['NAME'], set()) + (connection.settings_dict['NAME'], []), ) - item[1].add(alias) + # The default database must be the first because data migrations + # use the default alias by default. + if alias == DEFAULT_DB_ALIAS: + item[1].insert(0, alias) + else: + item[1].append(alias) if 'DEPENDENCIES' in test_settings: dependencies[alias] = test_settings['DEPENDENCIES'] diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py index 07c7836708..87d432a2ab 100644 --- a/tests/test_runner/tests.py +++ b/tests/test_runner/tests.py @@ -14,7 +14,9 @@ from django.core.management.base import SystemCheckError from django.test import TransactionTestCase, skipUnlessDBFeature from django.test.runner import DiscoverRunner from django.test.testcases import connections_support_transactions -from django.test.utils import captured_stderr, dependency_ordered +from django.test.utils import ( + captured_stderr, dependency_ordered, get_unique_databases_and_mirrors, +) from .models import B, Person, Through @@ -336,6 +338,33 @@ class SetupDatabasesTests(unittest.TestCase): self.runner_instance.teardown_databases(old_config) mocked_db_creation.return_value.destroy_test_db.assert_called_once_with('dbname', 0, False) + def test_setup_test_database_aliases(self): + """ + The default database must be the first because data migrations + use the default alias by default. + """ + tested_connections = db.ConnectionHandler({ + 'other': { + 'ENGINE': 'django.db.backends.dummy', + 'NAME': 'dbname', + }, + 'default': { + 'ENGINE': 'django.db.backends.dummy', + 'NAME': 'dbname', + } + }) + with mock.patch('django.test.utils.connections', new=tested_connections): + test_databases, _ = get_unique_databases_and_mirrors() + self.assertEqual( + test_databases, + { + ('', '', 'django.db.backends.dummy', 'test_dbname'): ( + 'dbname', + ['default', 'other'], + ), + }, + ) + def test_destroy_test_db_restores_db_name(self): tested_connections = db.ConnectionHandler({ 'default': {