tests.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import unittest
  2. import warnings
  3. from unittest import mock
  4. from django.db import DatabaseError, connection
  5. from django.test import TestCase
  6. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL tests')
  7. class Tests(TestCase):
  8. def test_nodb_connection(self):
  9. """
  10. The _nodb_connection property fallbacks to the default connection
  11. database when access to the 'postgres' database is not granted.
  12. """
  13. def mocked_connect(self):
  14. if self.settings_dict['NAME'] is None:
  15. raise DatabaseError()
  16. return ''
  17. nodb_conn = connection._nodb_connection
  18. self.assertIsNone(nodb_conn.settings_dict['NAME'])
  19. # Now assume the 'postgres' db isn't available
  20. with warnings.catch_warnings(record=True) as w:
  21. with mock.patch('django.db.backends.base.base.BaseDatabaseWrapper.connect',
  22. side_effect=mocked_connect, autospec=True):
  23. warnings.simplefilter('always', RuntimeWarning)
  24. nodb_conn = connection._nodb_connection
  25. self.assertIsNotNone(nodb_conn.settings_dict['NAME'])
  26. self.assertEqual(nodb_conn.settings_dict['NAME'], connection.settings_dict['NAME'])
  27. # Check a RuntimeWarning has been emitted
  28. self.assertEqual(len(w), 1)
  29. self.assertEqual(w[0].message.__class__, RuntimeWarning)
  30. def test_connect_and_rollback(self):
  31. """
  32. PostgreSQL shouldn't roll back SET TIME ZONE, even if the first
  33. transaction is rolled back (#17062).
  34. """
  35. new_connection = connection.copy()
  36. try:
  37. # Ensure the database default time zone is different than
  38. # the time zone in new_connection.settings_dict. We can
  39. # get the default time zone by reset & show.
  40. with new_connection.cursor() as cursor:
  41. cursor.execute("RESET TIMEZONE")
  42. cursor.execute("SHOW TIMEZONE")
  43. db_default_tz = cursor.fetchone()[0]
  44. new_tz = 'Europe/Paris' if db_default_tz == 'UTC' else 'UTC'
  45. new_connection.close()
  46. # Invalidate timezone name cache, because the setting_changed
  47. # handler cannot know about new_connection.
  48. del new_connection.timezone_name
  49. # Fetch a new connection with the new_tz as default
  50. # time zone, run a query and rollback.
  51. with self.settings(TIME_ZONE=new_tz):
  52. new_connection.set_autocommit(False)
  53. new_connection.rollback()
  54. # Now let's see if the rollback rolled back the SET TIME ZONE.
  55. with new_connection.cursor() as cursor:
  56. cursor.execute("SHOW TIMEZONE")
  57. tz = cursor.fetchone()[0]
  58. self.assertEqual(new_tz, tz)
  59. finally:
  60. new_connection.close()
  61. def test_connect_non_autocommit(self):
  62. """
  63. The connection wrapper shouldn't believe that autocommit is enabled
  64. after setting the time zone when AUTOCOMMIT is False (#21452).
  65. """
  66. new_connection = connection.copy()
  67. new_connection.settings_dict['AUTOCOMMIT'] = False
  68. try:
  69. # Open a database connection.
  70. new_connection.cursor()
  71. self.assertFalse(new_connection.get_autocommit())
  72. finally:
  73. new_connection.close()
  74. def test_connect_isolation_level(self):
  75. """
  76. The transaction level can be configured with
  77. DATABASES ['OPTIONS']['isolation_level'].
  78. """
  79. import psycopg2
  80. from psycopg2.extensions import (
  81. ISOLATION_LEVEL_READ_COMMITTED as read_committed,
  82. ISOLATION_LEVEL_SERIALIZABLE as serializable,
  83. )
  84. # Since this is a django.test.TestCase, a transaction is in progress
  85. # and the isolation level isn't reported as 0. This test assumes that
  86. # PostgreSQL is configured with the default isolation level.
  87. # Check the level on the psycopg2 connection, not the Django wrapper.
  88. default_level = read_committed if psycopg2.__version__ < '2.7' else None
  89. self.assertEqual(connection.connection.isolation_level, default_level)
  90. new_connection = connection.copy()
  91. new_connection.settings_dict['OPTIONS']['isolation_level'] = serializable
  92. try:
  93. # Start a transaction so the isolation level isn't reported as 0.
  94. new_connection.set_autocommit(False)
  95. # Check the level on the psycopg2 connection, not the Django wrapper.
  96. self.assertEqual(new_connection.connection.isolation_level, serializable)
  97. finally:
  98. new_connection.close()
  99. def _select(self, val):
  100. with connection.cursor() as cursor:
  101. cursor.execute('SELECT %s', (val,))
  102. return cursor.fetchone()[0]
  103. def test_select_ascii_array(self):
  104. a = ['awef']
  105. b = self._select(a)
  106. self.assertEqual(a[0], b[0])
  107. def test_select_unicode_array(self):
  108. a = ['ᄲawef']
  109. b = self._select(a)
  110. self.assertEqual(a[0], b[0])
  111. def test_lookup_cast(self):
  112. from django.db.backends.postgresql.operations import DatabaseOperations
  113. do = DatabaseOperations(connection=None)
  114. lookups = (
  115. 'iexact', 'contains', 'icontains', 'startswith', 'istartswith',
  116. 'endswith', 'iendswith', 'regex', 'iregex',
  117. )
  118. for lookup in lookups:
  119. with self.subTest(lookup=lookup):
  120. self.assertIn('::text', do.lookup_cast(lookup))
  121. def test_correct_extraction_psycopg2_version(self):
  122. from django.db.backends.postgresql.base import psycopg2_version
  123. with mock.patch('psycopg2.__version__', '4.2.1 (dt dec pq3 ext lo64)'):
  124. self.assertEqual(psycopg2_version(), (4, 2, 1))
  125. with mock.patch('psycopg2.__version__', '4.2b0.dev1 (dt dec pq3 ext lo64)'):
  126. self.assertEqual(psycopg2_version(), (4, 2))