From 9efe832ee1e5da326e4ee5ed370db963b8fe6624 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 2 Feb 2021 21:34:36 +0100 Subject: [3.1.x] Fixed #32403 -- Fixed re-raising DatabaseErrors when using only 'postgres' database. Thanks Kazantcev Andrey for the report. Regression in f48f671223a20b161ca819cf7d6298e43b8ba5fe. Backport of f131841c601b9d4884adcdb284b4213c2ad89231 from master --- tests/backends/postgresql/tests.py | 50 +++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py index 8d0a801ea2..c37b83bb90 100644 --- a/tests/backends/postgresql/tests.py +++ b/tests/backends/postgresql/tests.py @@ -1,9 +1,10 @@ +import copy import unittest from io import StringIO from unittest import mock from django.core.exceptions import ImproperlyConfigured -from django.db import DatabaseError, connection, connections +from django.db import DEFAULT_DB_ALIAS, DatabaseError, connection, connections from django.db.backends.base.base import BaseDatabaseWrapper from django.test import TestCase, override_settings @@ -54,6 +55,53 @@ class Tests(TestCase): self.assertIsNone(cursor.db.connection) self.assertIsNotNone(cursor.db.settings_dict['NAME']) self.assertEqual(cursor.db.settings_dict['NAME'], connections['other'].settings_dict['NAME']) + # Cursor is yielded only for the first PostgreSQL database. + with self.assertWarnsMessage(RuntimeWarning, msg): + with mock.patch( + 'django.db.backends.base.base.BaseDatabaseWrapper.connect', + side_effect=mocked_connect, + autospec=True, + ): + with connection._nodb_cursor() as cursor: + self.assertIs(cursor.closed, False) + self.assertIsNotNone(cursor.db.connection) + + def test_nodb_cursor_raises_postgres_authentication_failure(self): + """ + _nodb_cursor() re-raises authentication failure to the 'postgres' db + when other connection to the PostgreSQL database isn't available. + """ + def mocked_connect(self): + raise DatabaseError() + + def mocked_all(self): + test_connection = copy.copy(connections[DEFAULT_DB_ALIAS]) + test_connection.settings_dict = copy.deepcopy(connection.settings_dict) + test_connection.settings_dict['NAME'] = 'postgres' + return [test_connection] + + msg = ( + "Normally Django will use a connection to the 'postgres' database " + "to avoid running initialization queries against the production " + "database when it's not needed (for example, when running tests). " + "Django was unable to create a connection to the 'postgres' " + "database and will use the first PostgreSQL database instead." + ) + with self.assertWarnsMessage(RuntimeWarning, msg): + mocker_connections_all = mock.patch( + 'django.utils.connection.BaseConnectionHandler.all', + side_effect=mocked_all, + autospec=True, + ) + mocker_connect = mock.patch( + 'django.db.backends.base.base.BaseDatabaseWrapper.connect', + side_effect=mocked_connect, + autospec=True, + ) + with mocker_connections_all, mocker_connect: + with self.assertRaises(DatabaseError): + with connection._nodb_cursor(): + pass def test_database_name_too_long(self): from django.db.backends.postgresql.base import DatabaseWrapper -- cgit v1.3