|
@@ -1,8 +1,8 @@
|
|
|
-from unittest.mock import MagicMock
|
|
|
+from unittest.mock import MagicMock, patch
|
|
|
|
|
|
from django.db import DEFAULT_DB_ALIAS, connection, connections
|
|
|
from django.db.backends.base.base import BaseDatabaseWrapper
|
|
|
-from django.test import SimpleTestCase, TestCase
|
|
|
+from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
|
|
|
|
|
|
from ..models import Square
|
|
|
|
|
@@ -134,3 +134,156 @@ class ExecuteWrapperTests(TestCase):
|
|
|
self.assertFalse(wrapper.called)
|
|
|
self.assertEqual(connection.execute_wrappers, [])
|
|
|
self.assertEqual(connections['other'].execute_wrappers, [])
|
|
|
+
|
|
|
+
|
|
|
+class ConnectionHealthChecksTests(SimpleTestCase):
|
|
|
+ databases = {'default'}
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+
|
|
|
+
|
|
|
+ connection.close()
|
|
|
+ self.addCleanup(connection.close)
|
|
|
+
|
|
|
+ def patch_settings_dict(self, conn_health_checks):
|
|
|
+ self.settings_dict_patcher = patch.dict(connection.settings_dict, {
|
|
|
+ **connection.settings_dict,
|
|
|
+ 'CONN_MAX_AGE': None,
|
|
|
+ 'CONN_HEALTH_CHECKS': conn_health_checks,
|
|
|
+ })
|
|
|
+ self.settings_dict_patcher.start()
|
|
|
+ self.addCleanup(self.settings_dict_patcher.stop)
|
|
|
+
|
|
|
+ def run_query(self):
|
|
|
+ with connection.cursor() as cursor:
|
|
|
+ cursor.execute('SELECT 42' + connection.features.bare_select_suffix)
|
|
|
+
|
|
|
+ @skipUnlessDBFeature('test_db_allows_multiple_connections')
|
|
|
+ def test_health_checks_enabled(self):
|
|
|
+ self.patch_settings_dict(conn_health_checks=True)
|
|
|
+ self.assertIsNone(connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+ old_connection = connection.connection
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', return_value=False) as mocked_is_usable:
|
|
|
+ self.run_query()
|
|
|
+ new_connection = connection.connection
|
|
|
+
|
|
|
+ self.assertIsNot(new_connection, old_connection)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ self.run_query()
|
|
|
+ self.assertIs(new_connection, connection.connection)
|
|
|
+ self.assertEqual(mocked_is_usable.call_count, 1)
|
|
|
+
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+
|
|
|
+
|
|
|
+ self.run_query()
|
|
|
+ self.run_query()
|
|
|
+ self.assertIs(new_connection, connection.connection)
|
|
|
+
|
|
|
+ @skipUnlessDBFeature('test_db_allows_multiple_connections')
|
|
|
+ def test_health_checks_enabled_errors_occurred(self):
|
|
|
+ self.patch_settings_dict(conn_health_checks=True)
|
|
|
+ self.assertIsNone(connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+ old_connection = connection.connection
|
|
|
+
|
|
|
+ connection.errors_occurred = True
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+ @skipUnlessDBFeature('test_db_allows_multiple_connections')
|
|
|
+ def test_health_checks_disabled(self):
|
|
|
+ self.patch_settings_dict(conn_health_checks=False)
|
|
|
+ self.assertIsNone(connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+ old_connection = connection.connection
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+ self.run_query()
|
|
|
+
|
|
|
+
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+
|
|
|
+ @skipUnlessDBFeature('test_db_allows_multiple_connections')
|
|
|
+ def test_set_autocommit_health_checks_enabled(self):
|
|
|
+ self.patch_settings_dict(conn_health_checks=True)
|
|
|
+ self.assertIsNone(connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', side_effect=AssertionError):
|
|
|
+
|
|
|
+
|
|
|
+ connection.set_autocommit(False)
|
|
|
+ self.run_query()
|
|
|
+ connection.commit()
|
|
|
+ connection.set_autocommit(True)
|
|
|
+
|
|
|
+ old_connection = connection.connection
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+
|
|
|
+ self.assertIs(old_connection, connection.connection)
|
|
|
+
|
|
|
+
|
|
|
+ with patch.object(connection, 'is_usable', return_value=False) as mocked_is_usable:
|
|
|
+
|
|
|
+
|
|
|
+ connection.set_autocommit(False)
|
|
|
+ new_connection = connection.connection
|
|
|
+ self.assertIsNot(new_connection, old_connection)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ self.run_query()
|
|
|
+ connection.commit()
|
|
|
+ connection.set_autocommit(True)
|
|
|
+
|
|
|
+ self.assertIs(new_connection, connection.connection)
|
|
|
+ self.assertEqual(mocked_is_usable.call_count, 1)
|
|
|
+
|
|
|
+
|
|
|
+ connection.close_if_unusable_or_obsolete()
|
|
|
+
|
|
|
+
|
|
|
+ connection.set_autocommit(False)
|
|
|
+ self.run_query()
|
|
|
+ connection.commit()
|
|
|
+ connection.set_autocommit(True)
|
|
|
+ self.assertIs(new_connection, connection.connection)
|