1. import unittest
    
  2. from contextlib import contextmanager
    
  3. from unittest import mock
    
  4. 
    
  5. from django.core.exceptions import ImproperlyConfigured
    
  6. from django.db import NotSupportedError, connection
    
  7. from django.test import TestCase, override_settings
    
  8. 
    
  9. 
    
  10. @contextmanager
    
  11. def get_connection():
    
  12.     new_connection = connection.copy()
    
  13.     yield new_connection
    
  14.     new_connection.close()
    
  15. 
    
  16. 
    
  17. @override_settings(DEBUG=True)
    
  18. @unittest.skipUnless(connection.vendor == "mysql", "MySQL tests")
    
  19. class IsolationLevelTests(TestCase):
    
  20.     read_committed = "read committed"
    
  21.     repeatable_read = "repeatable read"
    
  22.     isolation_values = {
    
  23.         level: level.upper() for level in (read_committed, repeatable_read)
    
  24.     }
    
  25. 
    
  26.     @classmethod
    
  27.     def setUpClass(cls):
    
  28.         super().setUpClass()
    
  29.         configured_isolation_level = (
    
  30.             connection.isolation_level or cls.isolation_values[cls.repeatable_read]
    
  31.         )
    
  32.         cls.configured_isolation_level = configured_isolation_level.upper()
    
  33.         cls.other_isolation_level = (
    
  34.             cls.read_committed
    
  35.             if configured_isolation_level != cls.isolation_values[cls.read_committed]
    
  36.             else cls.repeatable_read
    
  37.         )
    
  38. 
    
  39.     @staticmethod
    
  40.     def get_isolation_level(connection):
    
  41.         with connection.cursor() as cursor:
    
  42.             cursor.execute(
    
  43.                 "SHOW VARIABLES "
    
  44.                 "WHERE variable_name IN ('transaction_isolation', 'tx_isolation')"
    
  45.             )
    
  46.             return cursor.fetchone()[1].replace("-", " ")
    
  47. 
    
  48.     def test_auto_is_null_auto_config(self):
    
  49.         query = "set sql_auto_is_null = 0"
    
  50.         connection.init_connection_state()
    
  51.         last_query = connection.queries[-1]["sql"].lower()
    
  52.         if connection.features.is_sql_auto_is_null_enabled:
    
  53.             self.assertIn(query, last_query)
    
  54.         else:
    
  55.             self.assertNotIn(query, last_query)
    
  56. 
    
  57.     def test_connect_isolation_level(self):
    
  58.         self.assertEqual(
    
  59.             self.get_isolation_level(connection), self.configured_isolation_level
    
  60.         )
    
  61. 
    
  62.     def test_setting_isolation_level(self):
    
  63.         with get_connection() as new_connection:
    
  64.             new_connection.settings_dict["OPTIONS"][
    
  65.                 "isolation_level"
    
  66.             ] = self.other_isolation_level
    
  67.             self.assertEqual(
    
  68.                 self.get_isolation_level(new_connection),
    
  69.                 self.isolation_values[self.other_isolation_level],
    
  70.             )
    
  71. 
    
  72.     def test_uppercase_isolation_level(self):
    
  73.         # Upper case values are also accepted in 'isolation_level'.
    
  74.         with get_connection() as new_connection:
    
  75.             new_connection.settings_dict["OPTIONS"][
    
  76.                 "isolation_level"
    
  77.             ] = self.other_isolation_level.upper()
    
  78.             self.assertEqual(
    
  79.                 self.get_isolation_level(new_connection),
    
  80.                 self.isolation_values[self.other_isolation_level],
    
  81.             )
    
  82. 
    
  83.     def test_default_isolation_level(self):
    
  84.         # If not specified in settings, the default is read committed.
    
  85.         with get_connection() as new_connection:
    
  86.             new_connection.settings_dict["OPTIONS"].pop("isolation_level", None)
    
  87.             self.assertEqual(
    
  88.                 self.get_isolation_level(new_connection),
    
  89.                 self.isolation_values[self.read_committed],
    
  90.             )
    
  91. 
    
  92.     def test_isolation_level_validation(self):
    
  93.         new_connection = connection.copy()
    
  94.         new_connection.settings_dict["OPTIONS"]["isolation_level"] = "xxx"
    
  95.         msg = (
    
  96.             "Invalid transaction isolation level 'xxx' specified.\n"
    
  97.             "Use one of 'read committed', 'read uncommitted', "
    
  98.             "'repeatable read', 'serializable', or None."
    
  99.         )
    
  100.         with self.assertRaisesMessage(ImproperlyConfigured, msg):
    
  101.             new_connection.cursor()
    
  102. 
    
  103. 
    
  104. @unittest.skipUnless(connection.vendor == "mysql", "MySQL tests")
    
  105. class Tests(TestCase):
    
  106.     @mock.patch.object(connection, "get_database_version")
    
  107.     def test_check_database_version_supported(self, mocked_get_database_version):
    
  108.         if connection.mysql_is_mariadb:
    
  109.             mocked_get_database_version.return_value = (10, 2)
    
  110.             msg = "MariaDB 10.3 or later is required (found 10.2)."
    
  111.         else:
    
  112.             mocked_get_database_version.return_value = (5, 6)
    
  113.             msg = "MySQL 5.7 or later is required (found 5.6)."
    
  114. 
    
  115.         with self.assertRaisesMessage(NotSupportedError, msg):
    
  116.             connection.check_database_version_supported()
    
  117.         self.assertTrue(mocked_get_database_version.called)