test_postgresql_psycopg2.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import os
  2. import signal
  3. from unittest import mock
  4. from django.db.backends.postgresql.client import DatabaseClient
  5. from django.test import SimpleTestCase
  6. class PostgreSqlDbshellCommandTestCase(SimpleTestCase):
  7. def _run_it(self, dbinfo):
  8. """
  9. That function invokes the runshell command, while mocking
  10. subprocess.call. It returns a 2-tuple with:
  11. - The command line list
  12. - The content of the file pointed by environment PGPASSFILE, or None.
  13. """
  14. def _mock_subprocess_call(*args):
  15. self.subprocess_args = list(*args)
  16. if 'PGPASSFILE' in os.environ:
  17. with open(os.environ['PGPASSFILE']) as f:
  18. self.pgpass = f.read().strip() # ignore line endings
  19. else:
  20. self.pgpass = None
  21. return 0
  22. self.subprocess_args = None
  23. self.pgpass = None
  24. with mock.patch('subprocess.call', new=_mock_subprocess_call):
  25. DatabaseClient.runshell_db(dbinfo)
  26. return self.subprocess_args, self.pgpass
  27. def test_basic(self):
  28. self.assertEqual(
  29. self._run_it({
  30. 'database': 'dbname',
  31. 'user': 'someuser',
  32. 'password': 'somepassword',
  33. 'host': 'somehost',
  34. 'port': '444',
  35. }), (
  36. ['psql', '-U', 'someuser', '-h', 'somehost', '-p', '444', 'dbname'],
  37. 'somehost:444:dbname:someuser:somepassword',
  38. )
  39. )
  40. def test_nopass(self):
  41. self.assertEqual(
  42. self._run_it({
  43. 'database': 'dbname',
  44. 'user': 'someuser',
  45. 'host': 'somehost',
  46. 'port': '444',
  47. }), (
  48. ['psql', '-U', 'someuser', '-h', 'somehost', '-p', '444', 'dbname'],
  49. None,
  50. )
  51. )
  52. def test_column(self):
  53. self.assertEqual(
  54. self._run_it({
  55. 'database': 'dbname',
  56. 'user': 'some:user',
  57. 'password': 'some:password',
  58. 'host': '::1',
  59. 'port': '444',
  60. }), (
  61. ['psql', '-U', 'some:user', '-h', '::1', '-p', '444', 'dbname'],
  62. '\\:\\:1:444:dbname:some\\:user:some\\:password',
  63. )
  64. )
  65. def test_escape_characters(self):
  66. self.assertEqual(
  67. self._run_it({
  68. 'database': 'dbname',
  69. 'user': 'some\\user',
  70. 'password': 'some\\password',
  71. 'host': 'somehost',
  72. 'port': '444',
  73. }), (
  74. ['psql', '-U', 'some\\user', '-h', 'somehost', '-p', '444', 'dbname'],
  75. 'somehost:444:dbname:some\\\\user:some\\\\password',
  76. )
  77. )
  78. def test_accent(self):
  79. username = 'rôle'
  80. password = 'sésame'
  81. pgpass_string = 'somehost:444:dbname:%s:%s' % (username, password)
  82. self.assertEqual(
  83. self._run_it({
  84. 'database': 'dbname',
  85. 'user': username,
  86. 'password': password,
  87. 'host': 'somehost',
  88. 'port': '444',
  89. }), (
  90. ['psql', '-U', username, '-h', 'somehost', '-p', '444', 'dbname'],
  91. pgpass_string,
  92. )
  93. )
  94. def test_sigint_handler(self):
  95. """SIGINT is ignored in Python and passed to psql to abort quries."""
  96. def _mock_subprocess_call(*args):
  97. handler = signal.getsignal(signal.SIGINT)
  98. self.assertEqual(handler, signal.SIG_IGN)
  99. sigint_handler = signal.getsignal(signal.SIGINT)
  100. # The default handler isn't SIG_IGN.
  101. self.assertNotEqual(sigint_handler, signal.SIG_IGN)
  102. with mock.patch('subprocess.check_call', new=_mock_subprocess_call):
  103. DatabaseClient.runshell_db({})
  104. # dbshell restores the original handler.
  105. self.assertEqual(sigint_handler, signal.getsignal(signal.SIGINT))