tests.py 4.4 KB

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