OCA reporting engine fork for dev and update.
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.

260 lines
11 KiB

8 years ago
8 years ago
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 ACSONE SA/NV
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).).
  4. import base64
  5. from base64 import b64decode
  6. import mock
  7. import os
  8. import pkg_resources
  9. import shutil
  10. import tempfile
  11. from contextlib import contextmanager
  12. from py3o.formats import Formats
  13. from odoo import tools
  14. from odoo.tests.common import TransactionCase
  15. from odoo.exceptions import ValidationError
  16. from ..models.py3o_report import TemplateNotFound
  17. from base64 import b64encode
  18. @contextmanager
  19. def temporary_copy(path):
  20. filname, ext = os.path.splitext(path)
  21. tmp_filename = tempfile.mktemp(suffix='.' + ext)
  22. try:
  23. shutil.copy2(path, tmp_filename)
  24. yield tmp_filename
  25. finally:
  26. os.unlink(tmp_filename)
  27. class TestReportPy3o(TransactionCase):
  28. def setUp(self):
  29. super(TestReportPy3o, self).setUp()
  30. self.report = self.env.ref("report_py3o.res_users_report_py3o")
  31. self.py3o_report = self.env['py3o.report'].create({
  32. 'ir_actions_report_xml_id': self.report.id})
  33. def test_no_local_fusion_without_fusion_server(self):
  34. self.assertTrue(self.report.py3o_is_local_fusion)
  35. with self.assertRaises(ValidationError) as e:
  36. self.report.py3o_is_local_fusion = False
  37. self.assertEqual(
  38. e.exception.name,
  39. "Can not use not native format in local fusion. "
  40. "Please specify a Fusion Server")
  41. def test_no_native_format_without_fusion_server(self):
  42. report = self.env.ref("report_py3o.res_users_report_py3o")
  43. formats = Formats()
  44. is_native = formats.get_format(report.py3o_filetype).native
  45. self.assertTrue(is_native)
  46. new_format = None
  47. for name in formats.get_known_format_names():
  48. format = formats.get_format(name)
  49. if not format.native:
  50. new_format = name
  51. break
  52. self.assertTrue(new_format)
  53. with self.assertRaises(ValidationError) as e:
  54. report.py3o_filetype = new_format
  55. self.assertEqual(
  56. e.exception.name,
  57. "Can not use not native format in local fusion. "
  58. "Please specify a Fusion Server")
  59. def test_required_py3_filetype(self):
  60. self.assertEqual(self.report.report_type, "py3o")
  61. with self.assertRaises(ValidationError) as e:
  62. self.report.py3o_filetype = False
  63. self.assertEqual(
  64. e.exception.name,
  65. "Field 'Output Format' is required for Py3O report")
  66. def test_reports(self):
  67. py3o_report = self.env['py3o.report']
  68. with mock.patch.object(
  69. py3o_report.__class__, '_create_single_report') as patched_pdf:
  70. result = tempfile.mktemp('.txt')
  71. with open(result, 'w') as fp:
  72. fp.write('dummy')
  73. patched_pdf.return_value = result
  74. # test the call the the create method inside our custom parser
  75. self.report.render_report(self.env.user.ids,
  76. self.report.report_name,
  77. {})
  78. self.assertEqual(1, patched_pdf.call_count)
  79. # generated files no more exists
  80. self.assertFalse(os.path.exists(result))
  81. res = self.report.render_report(
  82. self.env.user.ids, self.report.report_name, {})
  83. self.assertTrue(res)
  84. py3o_server = self.env['py3o.server'].create({"url": "http://dummy"})
  85. # check the call to the fusion server
  86. self.report.write({"py3o_filetype": "pdf",
  87. "py3o_server_id": py3o_server.id})
  88. with mock.patch('requests.post') as patched_post:
  89. magick_response = mock.MagicMock()
  90. magick_response.status_code = 200
  91. patched_post.return_value = magick_response
  92. magick_response.iter_content.return_value = "test result"
  93. res = self.report.render_report(
  94. self.env.user.ids, self.report.report_name, {})
  95. self.assertEqual(('test result', 'pdf'), res)
  96. def test_report_load_from_attachment(self):
  97. py3o_report = self.env['py3o.report']
  98. with mock.patch.object(
  99. py3o_report.__class__, '_create_single_report') as patched_pdf:
  100. result = tempfile.mktemp('.txt')
  101. with open(result, 'w') as fp:
  102. fp.write('dummy')
  103. patched_pdf.return_value = result
  104. # test the call the the create method inside our custom parser
  105. self.report.render_report(self.env.user.ids,
  106. self.report.report_name,
  107. {})
  108. self.assertEqual(1, patched_pdf.call_count)
  109. # generated files no more exists
  110. self.assertFalse(os.path.exists(result))
  111. res = self.report.render_report(
  112. self.env.user.ids, self.report.report_name, {})
  113. self.assertTrue(res)
  114. py3o_server = self.env['py3o.server'].create({"url": "http://dummy"})
  115. # check the call to the fusion server
  116. self.report.write({"py3o_filetype": "pdf",
  117. "py3o_server_id": py3o_server.id,
  118. "attachment_use": True,
  119. "attachment": "'my_saved_report'"})
  120. attachments = self.env['ir.attachment'].search([])
  121. with mock.patch('requests.post') as patched_post:
  122. magick_response = mock.MagicMock()
  123. magick_response.status_code = 200
  124. patched_post.return_value = magick_response
  125. magick_response.iter_content.return_value = "test result"
  126. res = self.report.render_report(
  127. self.env.user.ids, self.report.report_name, {})
  128. self.assertEqual(('test result', 'pdf'), res)
  129. new_attachments = self.env['ir.attachment'].search([])
  130. created_attachement = new_attachments - attachments
  131. self.assertEqual(1, len(created_attachement))
  132. content = b64decode(created_attachement.datas)
  133. self.assertEqual("test result", content)
  134. # put a new content into tha attachement and check that the next
  135. # time we ask the report we received the saved attachment not a newly
  136. # generated document
  137. created_attachement.datas = base64.encodestring("new content")
  138. res = self.report.render_report(
  139. self.env.user.ids, self.report.report_name, {})
  140. self.assertEqual(('new content', 'pdf'), res)
  141. def test_report_post_process(self):
  142. """
  143. By default the post_process method is in charge to save the
  144. generated report into an ir.attachment if requested.
  145. """
  146. report = self.env.ref("report_py3o.res_users_report_py3o")
  147. report.attachment = "object.name + '.txt'"
  148. py3o_server = self.env['py3o.server'].create({"url": "http://dummy"})
  149. # check the call to the fusion server
  150. report.write({"py3o_filetype": "pdf",
  151. "py3o_server_id": py3o_server.id})
  152. ir_attachment = self.env['ir.attachment']
  153. attachements = ir_attachment.search([(1, '=', 1)])
  154. with mock.patch('requests.post') as patched_post:
  155. magick_response = mock.MagicMock()
  156. magick_response.status_code = 200
  157. patched_post.return_value = magick_response
  158. magick_response.iter_content.return_value = "test result"
  159. res = report.render_report(
  160. self.env.user.ids, report.report_name, {})
  161. self.assertEqual(('test result', 'pdf'), res)
  162. attachements = ir_attachment.search([(1, '=', 1)]) - attachements
  163. self.assertEqual(1, len(attachements.ids))
  164. self.assertEqual(self.env.user.name + '.txt', attachements.name)
  165. self.assertEqual(self.env.user._name, attachements.res_model)
  166. self.assertEqual(self.env.user.id, attachements.res_id)
  167. self.assertEqual('test result', b64decode(attachements.datas))
  168. def test_report_template_configs(self):
  169. # the demo template is specified with a relative path in in the module
  170. # path
  171. tmpl_name = self.report.py3o_template_fallback
  172. flbk_filename = pkg_resources.resource_filename(
  173. "odoo.addons.%s" % self.report.module,
  174. tmpl_name)
  175. self.assertTrue(os.path.exists(flbk_filename))
  176. res = self.report.render_report(
  177. self.env.user.ids, self.report.report_name, {})
  178. self.assertTrue(res)
  179. # The generation fails if the tempalte is not found
  180. self.report.module = False
  181. with self.assertRaises(TemplateNotFound), self.env.cr.savepoint():
  182. self.report.render_report(
  183. self.env.user.ids, self.report.report_name, {})
  184. # the template can also be provided as an abspath if it's root path
  185. # is trusted
  186. self.report.py3o_template_fallback = flbk_filename
  187. with self.assertRaises(TemplateNotFound):
  188. self.report.render_report(
  189. self.env.user.ids, self.report.report_name, {})
  190. with temporary_copy(flbk_filename) as tmp_filename:
  191. self.report.py3o_template_fallback = tmp_filename
  192. tools.config.misc['report_py3o'] = {
  193. 'root_tmpl_path': os.path.dirname(tmp_filename)}
  194. res = self.report.render_report(
  195. self.env.user.ids, self.report.report_name, {})
  196. self.assertTrue(res)
  197. # the tempalte can also be provided as a binay field
  198. self.report.py3o_template_fallback = False
  199. with open(flbk_filename) as tmpl_file:
  200. tmpl_data = b64encode(tmpl_file.read())
  201. py3o_template = self.env['py3o.template'].create({
  202. 'name': 'test_template',
  203. 'py3o_template_data': tmpl_data,
  204. 'filetype': 'odt'})
  205. self.report.py3o_template_id = py3o_template
  206. self.report.py3o_template_fallback = flbk_filename
  207. res = self.report.render_report(
  208. self.env.user.ids, self.report.report_name, {})
  209. self.assertTrue(res)
  210. def test_report_template_fallback_validity(self):
  211. tmpl_name = self.report.py3o_template_fallback
  212. flbk_filename = pkg_resources.resource_filename(
  213. "odoo.addons.%s" % self.report.module,
  214. tmpl_name)
  215. # an exising file in a native format is a valid template if it's
  216. self.assertTrue(self.py3o_report._get_template_from_path(
  217. tmpl_name))
  218. self.report.module = None
  219. # a directory is not a valid template..
  220. self.assertFalse(self.py3o_report._get_template_from_path('/etc/'))
  221. self.assertFalse(self.py3o_report._get_template_from_path('.'))
  222. # an vaild template outside the root_tmpl_path is not a valid template
  223. # path
  224. # located in trusted directory
  225. self.report.py3o_template_fallback = flbk_filename
  226. self.assertFalse(self.py3o_report._get_template_from_path(
  227. flbk_filename))
  228. with temporary_copy(flbk_filename) as tmp_filename:
  229. self.assertTrue(self.py3o_report._get_template_from_path(
  230. tmp_filename))
  231. # check security
  232. self.assertFalse(self.py3o_report._get_template_from_path(
  233. 'rm -rf . & %s' % flbk_filename))
  234. # a file in a non native LibreOffice format is not a valid template
  235. with tempfile.NamedTemporaryFile(suffix='.toto')as f:
  236. self.assertFalse(self.py3o_report._get_template_from_path(
  237. f.name))
  238. # non exising files are not valid template
  239. self.assertFalse(self.py3o_report._get_template_from_path(
  240. '/etc/test.odt'))