@ -6,10 +6,13 @@ from base64 import b64decode
import mock
import mock
import os
import os
import pkg_resources
import pkg_resources
import shutil
import tempfile
import tempfile
from contextlib import contextmanager
from py3o.formats import Formats
from py3o.formats import Formats
from odoo import tools
from odoo.tests.common import TransactionCase
from odoo.tests.common import TransactionCase
from odoo.exceptions import ValidationError
from odoo.exceptions import ValidationError
@ -17,13 +20,29 @@ from ..models.py3o_report import TemplateNotFound
from base64 import b64encode
from base64 import b64encode
@contextmanager
def temporary_copy ( path ) :
filname , ext = os . path . splitext ( path )
tmp_filename = tempfile . mktemp ( suffix = ' . ' + ext )
try :
shutil . copy2 ( path , tmp_filename )
yield tmp_filename
finally :
os . unlink ( tmp_filename )
class TestReportPy3o ( TransactionCase ) :
class TestReportPy3o ( TransactionCase ) :
def setUp ( self ) :
super ( TestReportPy3o , self ) . setUp ( )
self . report = self . env . ref ( " report_py3o.res_users_report_py3o " )
self . py3o_report = self . env [ ' py3o.report ' ] . create ( {
' ir_actions_report_xml_id ' : self . report . id } )
def test_no_local_fusion_without_fusion_server ( self ) :
def test_no_local_fusion_without_fusion_server ( self ) :
report = self . env . ref ( " report_py3o.res_users_report_py3o " )
self . assertTrue ( report . py3o_is_local_fusion )
self . assertTrue ( self . report . py3o_is_local_fusion )
with self . assertRaises ( ValidationError ) as e :
with self . assertRaises ( ValidationError ) as e :
report . py3o_is_local_fusion = False
self . report . py3o_is_local_fusion = False
self . assertEqual (
self . assertEqual (
e . exception . name ,
e . exception . name ,
" Can not use not native format in local fusion. "
" Can not use not native format in local fusion. "
@ -49,17 +68,15 @@ class TestReportPy3o(TransactionCase):
" Please specify a Fusion Server " )
" Please specify a Fusion Server " )
def test_required_py3_filetype ( self ) :
def test_required_py3_filetype ( self ) :
report = self . env . ref ( " report_py3o.res_users_report_py3o " )
self . assertEqual ( report . report_type , " py3o " )
self . assertEqual ( self . report . report_type , " py3o " )
with self . assertRaises ( ValidationError ) as e :
with self . assertRaises ( ValidationError ) as e :
report . py3o_filetype = False
self . report . py3o_filetype = False
self . assertEqual (
self . assertEqual (
e . exception . name ,
e . exception . name ,
" Field ' Output Format ' is required for Py3O report " )
" Field ' Output Format ' is required for Py3O report " )
def test_reports ( self ) :
def test_reports ( self ) :
py3o_report = self . env [ ' py3o.report ' ]
py3o_report = self . env [ ' py3o.report ' ]
report = self . env . ref ( " report_py3o.res_users_report_py3o " )
with mock . patch . object (
with mock . patch . object (
py3o_report . __class__ , ' _create_single_report ' ) as patched_pdf :
py3o_report . __class__ , ' _create_single_report ' ) as patched_pdf :
result = tempfile . mktemp ( ' .txt ' )
result = tempfile . mktemp ( ' .txt ' )
@ -67,26 +84,26 @@ class TestReportPy3o(TransactionCase):
fp . write ( ' dummy ' )
fp . write ( ' dummy ' )
patched_pdf . return_value = result
patched_pdf . return_value = result
# test the call the the create method inside our custom parser
# test the call the the create method inside our custom parser
report . render_report ( self . env . user . ids ,
report . report_name ,
self . report . render_report ( self . env . user . ids ,
self . report . report_name ,
{ } )
{ } )
self . assertEqual ( 1 , patched_pdf . call_count )
self . assertEqual ( 1 , patched_pdf . call_count )
# generated files no more exists
# generated files no more exists
self . assertFalse ( os . path . exists ( result ) )
self . assertFalse ( os . path . exists ( result ) )
res = report . render_report (
self . env . user . ids , report . report_name , { } )
res = self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
self . assertTrue ( res )
self . assertTrue ( res )
py3o_server = self . env [ ' py3o.server ' ] . create ( { " url " : " http://dummy " } )
py3o_server = self . env [ ' py3o.server ' ] . create ( { " url " : " http://dummy " } )
# check the call to the fusion server
# check the call to the fusion server
report . write ( { " py3o_filetype " : " pdf " ,
self . report . write ( { " py3o_filetype " : " pdf " ,
" py3o_server_id " : py3o_server . id } )
" py3o_server_id " : py3o_server . id } )
with mock . patch ( ' requests.post ' ) as patched_post :
with mock . patch ( ' requests.post ' ) as patched_post :
magick_response = mock . MagicMock ( )
magick_response = mock . MagicMock ( )
magick_response . status_code = 200
magick_response . status_code = 200
patched_post . return_value = magick_response
patched_post . return_value = magick_response
magick_response . iter_content . return_value = " test result "
magick_response . iter_content . return_value = " test result "
res = report . render_report (
self . env . user . ids , report . report_name , { } )
res = self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
self . assertEqual ( ( ' test result ' , ' pdf ' ) , res )
self . assertEqual ( ( ' test result ' , ' pdf ' ) , res )
def test_report_post_process ( self ) :
def test_report_post_process ( self ) :
@ -118,31 +135,38 @@ class TestReportPy3o(TransactionCase):
self . assertEqual ( ' test result ' , b64decode ( attachements . datas ) )
self . assertEqual ( ' test result ' , b64decode ( attachements . datas ) )
def test_report_template_configs ( self ) :
def test_report_template_configs ( self ) :
report = self . env . ref ( " report_py3o.res_users_report_py3o " )
# the demo template is specified with a relative path in in the module
# the demo template is specified with a relative path in in the module
# path
# path
tmpl_name = report . py3o_template_fallback
tmpl_name = self . report . py3o_template_fallback
flbk_filename = pkg_resources . resource_filename (
flbk_filename = pkg_resources . resource_filename (
" odoo.addons. %s " % report . module ,
" odoo.addons. %s " % self . report . module ,
tmpl_name )
tmpl_name )
self . assertTrue ( os . path . exists ( flbk_filename ) )
self . assertTrue ( os . path . exists ( flbk_filename ) )
res = report . render_report (
self . env . user . ids , report . report_name , { } )
res = self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
self . assertTrue ( res )
self . assertTrue ( res )
# The generation fails if the tempalte is not found
# The generation fails if the tempalte is not found
report . module = False
self . report . module = False
with self . assertRaises ( TemplateNotFound ) , self . env . cr . savepoint ( ) :
with self . assertRaises ( TemplateNotFound ) , self . env . cr . savepoint ( ) :
report . render_report (
self . env . user . ids , report . report_name , { } )
self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
# the template can also be provided as an abspaath
report . py3o_template_fallback = flbk_filename
res = report . render_report (
self . env . user . ids , report . report_name , { } )
# the template can also be provided as an abspath if it's root path
# is trusted
self . report . py3o_template_fallback = flbk_filename
with self . assertRaises ( TemplateNotFound ) :
self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
with temporary_copy ( flbk_filename ) as tmp_filename :
self . report . py3o_template_fallback = tmp_filename
tools . config . misc [ ' report_py3o ' ] = {
' root_tmpl_path ' : os . path . dirname ( tmp_filename ) }
res = self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
self . assertTrue ( res )
self . assertTrue ( res )
# the tempalte can also be provided as a binay field
# the tempalte can also be provided as a binay field
report . py3o_template_fallback = False
self . report . py3o_template_fallback = False
with open ( flbk_filename ) as tmpl_file :
with open ( flbk_filename ) as tmpl_file :
tmpl_data = b64encode ( tmpl_file . read ( ) )
tmpl_data = b64encode ( tmpl_file . read ( ) )
@ -150,8 +174,40 @@ class TestReportPy3o(TransactionCase):
' name ' : ' test_template ' ,
' name ' : ' test_template ' ,
' py3o_template_data ' : tmpl_data ,
' py3o_template_data ' : tmpl_data ,
' filetype ' : ' odt ' } )
' filetype ' : ' odt ' } )
report . py3o_template_id = py3o_template
report . py3o_template_fallback = flbk_filename
res = report . render_report (
self . env . user . ids , report . report_name , { } )
self . report . py3o_template_id = py3o_template
self . report . py3o_template_fallback = flbk_filename
res = self . report . render_report (
self . env . user . ids , self . report . report_name , { } )
self . assertTrue ( res )
self . assertTrue ( res )
def test_report_template_fallback_validity ( self ) :
tmpl_name = self . report . py3o_template_fallback
flbk_filename = pkg_resources . resource_filename (
" odoo.addons. %s " % self . report . module ,
tmpl_name )
# an exising file in a native format is a valid template if it's
self . assertTrue ( self . py3o_report . _get_template_from_path (
tmpl_name ) )
self . report . module = None
# a directory is not a valid template..
self . assertFalse ( self . py3o_report . _get_template_from_path ( ' /etc/ ' ) )
self . assertFalse ( self . py3o_report . _get_template_from_path ( ' . ' ) )
# an vaild template outside the root_tmpl_path is not a valid template
# path
# located in trusted directory
self . report . py3o_template_fallback = flbk_filename
self . assertFalse ( self . py3o_report . _get_template_from_path (
flbk_filename ) )
with temporary_copy ( flbk_filename ) as tmp_filename :
self . assertTrue ( self . py3o_report . _get_template_from_path (
tmp_filename ) )
# check security
self . assertFalse ( self . py3o_report . _get_template_from_path (
' rm -rf . & %s ' % flbk_filename ) )
# a file in a non native LibreOffice format is not a valid template
with tempfile . NamedTemporaryFile ( suffix = ' .toto ' ) as f :
self . assertFalse ( self . py3o_report . _get_template_from_path (
f . name ) )
# non exising files are not valid template
self . assertFalse ( self . py3o_report . _get_template_from_path (
' /etc/test.odt ' ) )