You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
8.3 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2015 Agile Business Group <http://www.agilebg.com>
  3. # © 2015 Alessio Gerace <alesiso.gerace@agilebg.com>
  4. # © 2016 Grupo ESOC Ingeniería de Servicios, S.L.U. - Jairo Llopis
  5. # Copyright 2016 LasLabs Inc.
  6. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  7. import os
  8. import mock
  9. from datetime import datetime
  10. from contextlib import contextmanager
  11. from odoo.tests import common
  12. from odoo import exceptions, tools
  13. try:
  14. import pysftp
  15. except ImportError:
  16. pass
  17. model = 'odoo.addons.auto_backup.models.db_backup'
  18. class TestConnectionException(pysftp.ConnectionException):
  19. def __init__(self):
  20. super(TestConnectionException, self).__init__('test', 'test')
  21. class TestDbBackup(common.TransactionCase):
  22. def setUp(self):
  23. super(TestDbBackup, self).setUp()
  24. self.Model = self.env["db.backup"]
  25. @contextmanager
  26. def mock_assets(self):
  27. """ It provides mocked core assets """
  28. self.path_join_val = '/this/is/a/path'
  29. with mock.patch('%s.db' % model) as db:
  30. with mock.patch('%s.os' % model) as os:
  31. with mock.patch('%s.shutil' % model) as shutil:
  32. os.path.join.return_value = self.path_join_val
  33. yield {
  34. 'db': db,
  35. 'os': os,
  36. 'shutil': shutil,
  37. }
  38. @contextmanager
  39. def patch_filtered_sftp(self, record, mocks=None):
  40. """ It patches filtered record and provides a mock """
  41. if mocks is None:
  42. mocks = ['sftp_connection']
  43. mocks = {m: mock.DEFAULT for m in mocks}
  44. with mock.patch.object(record, 'filtered') as filtered:
  45. with mock.patch.object(record, 'backup_log'):
  46. with mock.patch.multiple(record, **mocks):
  47. filtered.side_effect = [], [record]
  48. yield filtered
  49. def new_record(self, method='sftp'):
  50. vals = {
  51. 'name': u'Têst backup',
  52. 'method': method,
  53. }
  54. if method == 'sftp':
  55. vals.update({
  56. 'sftp_host': 'test_host',
  57. 'sftp_port': '222',
  58. 'sftp_user': 'tuser',
  59. 'sftp_password': 'password',
  60. 'folder': '/folder/',
  61. })
  62. self.vals = vals
  63. return self.Model.create(vals)
  64. def test_compute_name_sftp(self):
  65. """ It should create proper SFTP URI """
  66. rec_id = self.new_record()
  67. self.assertEqual(
  68. 'sftp://%(user)s@%(host)s:%(port)s%(folder)s' % {
  69. 'user': self.vals['sftp_user'],
  70. 'host': self.vals['sftp_host'],
  71. 'port': self.vals['sftp_port'],
  72. 'folder': self.vals['folder'],
  73. },
  74. rec_id.name,
  75. )
  76. def test_check_folder(self):
  77. """ It should not allow recursive backups """
  78. rec_id = self.new_record('local')
  79. with self.assertRaises(exceptions.ValidationError):
  80. rec_id.write({
  81. 'folder': '%s/another/path' % tools.config.filestore(
  82. self.env.cr.dbname
  83. ),
  84. })
  85. @mock.patch('%s._' % model)
  86. def test_action_sftp_test_connection_success(self, _):
  87. """ It should raise connection succeeded warning """
  88. rec_id = self.new_record()
  89. with mock.patch.object(rec_id, 'sftp_connection'):
  90. with self.assertRaises(exceptions.Warning):
  91. rec_id.action_sftp_test_connection()
  92. _.assert_called_once_with("Connection Test Succeeded!")
  93. @mock.patch('%s._' % model)
  94. def test_action_sftp_test_connection_fail(self, _):
  95. """ It should raise connection fail warning """
  96. rec_id = self.new_record()
  97. with mock.patch.object(rec_id, 'sftp_connection') as conn:
  98. conn().__enter__.side_effect = TestConnectionException
  99. with self.assertRaises(exceptions.Warning):
  100. rec_id.action_sftp_test_connection()
  101. _.assert_called_once_with("Connection Test Failed!")
  102. def test_action_backup_local(self):
  103. """ It should backup local database """
  104. rec_id = self.new_record('local')
  105. filename = rec_id.filename(datetime.now())
  106. rec_id.action_backup()
  107. generated_backup = [f for f in os.listdir(rec_id.folder)
  108. if f >= filename]
  109. self.assertEqual(1, len(generated_backup))
  110. def test_action_backup_sftp_mkdirs(self):
  111. """ It should create remote dirs """
  112. rec_id = self.new_record()
  113. with self.mock_assets():
  114. with self.patch_filtered_sftp(rec_id):
  115. conn = rec_id.sftp_connection().__enter__()
  116. rec_id.action_backup()
  117. conn.makedirs.assert_called_once_with(rec_id.folder)
  118. def test_action_backup_sftp_mkdirs_conn_exception(self):
  119. """ It should guard from ConnectionException on remote.mkdirs """
  120. rec_id = self.new_record()
  121. with self.mock_assets():
  122. with self.patch_filtered_sftp(rec_id):
  123. conn = rec_id.sftp_connection().__enter__()
  124. conn.makedirs.side_effect = TestConnectionException
  125. rec_id.action_backup()
  126. # No error was raised, test pass
  127. self.assertTrue(True)
  128. def test_action_backup_sftp_remote_open(self):
  129. """ It should open remote file w/ proper args """
  130. rec_id = self.new_record()
  131. with self.mock_assets() as assets:
  132. with self.patch_filtered_sftp(rec_id):
  133. conn = rec_id.sftp_connection().__enter__()
  134. rec_id.action_backup()
  135. conn.open.assert_called_once_with(
  136. assets['os'].path.join(),
  137. 'wb'
  138. )
  139. def test_action_backup_sftp_remote_open(self):
  140. """ It should open remote file w/ proper args """
  141. rec_id = self.new_record()
  142. with self.mock_assets() as assets:
  143. with self.patch_filtered_sftp(rec_id):
  144. conn = rec_id.sftp_connection().__enter__()
  145. rec_id.action_backup()
  146. conn.open.assert_called_once_with(
  147. assets['os'].path.join(),
  148. 'wb'
  149. )
  150. def test_action_backup_all_search(self):
  151. """ It should search all records """
  152. rec_id = self.new_record()
  153. with mock.patch.object(rec_id, 'search'):
  154. rec_id.action_backup_all()
  155. rec_id.search.assert_called_once_with([])
  156. def test_action_backup_all_return(self):
  157. """ It should return result of backup operation """
  158. rec_id = self.new_record()
  159. with mock.patch.object(rec_id, 'search'):
  160. res = rec_id.action_backup_all()
  161. self.assertEqual(
  162. rec_id.search().action_backup(), res
  163. )
  164. @mock.patch('%s.pysftp' % model)
  165. def test_sftp_connection_init_passwd(self, pysftp):
  166. """ It should initiate SFTP connection w/ proper args and pass """
  167. rec_id = self.new_record()
  168. rec_id.sftp_connection()
  169. pysftp.Connection.assert_called_once_with(
  170. host=rec_id.sftp_host,
  171. username=rec_id.sftp_user,
  172. port=rec_id.sftp_port,
  173. password=rec_id.sftp_password,
  174. )
  175. @mock.patch('%s.pysftp' % model)
  176. def test_sftp_connection_init_key(self, pysftp):
  177. """ It should initiate SFTP connection w/ proper args and key """
  178. rec_id = self.new_record()
  179. rec_id.write({
  180. 'sftp_private_key': 'pkey',
  181. 'sftp_password': 'pkeypass',
  182. })
  183. rec_id.sftp_connection()
  184. pysftp.Connection.assert_called_once_with(
  185. host=rec_id.sftp_host,
  186. username=rec_id.sftp_user,
  187. port=rec_id.sftp_port,
  188. private_key=rec_id.sftp_private_key,
  189. private_key_pass=rec_id.sftp_password,
  190. )
  191. @mock.patch('%s.pysftp' % model)
  192. def test_sftp_connection_return(self, pysftp):
  193. """ It should return new sftp connection """
  194. rec_id = self.new_record()
  195. res = rec_id.sftp_connection()
  196. self.assertEqual(
  197. pysftp.Connection(), res,
  198. )
  199. def test_filename(self):
  200. """ It should not error and should return a .dump.zip file str """
  201. now = datetime.now()
  202. res = self.Model.filename(now)
  203. self.assertTrue(res.endswith(".dump.zip"))