Skip to content

Commit

Permalink
[LINT] test_mail: backport test linting from master
Browse files Browse the repository at this point in the history
Purpose is to try to keep coherency between tests in main versions, easing
forward port and comparisons. This commit is a partial backport of
odoo/odoo@709a659 :

  * add some tagged in tests helping debugging / choosing tests to execute;
  * in a test about mail generation with server action: correctly check
    body_html field, not body which comes from the mail.message inheritance
    (currently filled due to a side effect but actual field to check is the
    html one);
  * move code translation update from specific tests into main test class
    (code update due to new translations, followup of f8c2b02);
  * reorder mail_render tests, remove some duplicated tests;
  * reorder mail_template tests, remove some duplicated tests;

Part-of: odoo#108289
  • Loading branch information
tde-banana-odoo committed Dec 19, 2022
1 parent c9dd160 commit 0053d0d
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 193 deletions.
12 changes: 10 additions & 2 deletions addons/mail/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from odoo.addons.mail.models.mail_notification import MailNotification
from odoo.tests import common, new_test_user
from odoo.tools import formataddr, pycompat
from odoo.tools.translate import code_translations

mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True,
'mail_create_nosubscribe': True,
Expand Down Expand Up @@ -1119,9 +1120,16 @@ def _activate_multi_lang(cls, lang_code='es_ES', layout_arch_db=None, test_recor
if test_record:
cls.env['ir.model']._get(test_record._name).with_context(lang=lang_code).name = 'Spanish description'

# Translate some code strings used in mailing
code_translations.python_translations[('test_mail', 'es_ES')] = {'TestStuff': 'TestSpanishStuff'}
cls.addClassCleanup(code_translations.python_translations.pop, ('test_mail', 'es_ES'))
code_translations.python_translations[('mail', 'es_ES')] = {'View %s': 'SpanishView %s'}
cls.addClassCleanup(code_translations.python_translations.pop, ('mail', 'es_ES'))

# Prepare some translated value for template if given
test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}'
test_template.with_context(lang=lang_code).body_html = '<p>SpanishBody for <t t-out="object.name" /></p>'
if test_template:
test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}'
test_template.with_context(lang=lang_code).body_html = '<p>SpanishBody for <t t-out="object.name" /></p>'

# create a custom layout for email notification
if not layout_arch_db:
Expand Down
276 changes: 147 additions & 129 deletions addons/mail/tests/test_mail_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
from odoo.tests import tagged, users


@tagged('mail_render')
class TestMailRender(common.MailCommon):
class TestMailRenderCommon(common.MailCommon):

@classmethod
def setUpClass(cls):
super(TestMailRender, cls).setUpClass()
super(TestMailRenderCommon, cls).setUpClass()

# activate multi language support
cls.env['res.lang']._activate_lang('fr_FR')
Expand All @@ -40,10 +39,7 @@ def setUpClass(cls):
<p>{{ 13 + 13 }}</p>
<h1>This is a test</h1>
""",
"""
<b>Test</b>
{{ '' if True else '<b>Code not executed</b>' }}
""",
"""<b>Test</b>{{ '' if True else '<b>Code not executed</b>' }}""",
]
cls.base_inline_template_bits_fr = [
'<p>Bonjour</p>',
Expand Down Expand Up @@ -136,6 +132,10 @@ def setUpClass(cls):
)
cls.user_rendering_restricted.groups_id -= cls.env.ref('mail.group_mail_template_editor')


@tagged('mail_render')
class TestMailRender(TestMailRenderCommon):

@users('employee')
def test_evaluation_context(self):
""" Test evaluation context and various ways of tweaking it. """
Expand Down Expand Up @@ -164,6 +164,15 @@ def test_evaluation_context(self):
)[partner.id]
self.assertEqual(expected, result)

@users('employee')
def test_prepend_preview_inline_template_to_qweb(self):
body = 'body'
preview = 'foo{{"false" if 1 > 2 else "true"}}bar'
result = self.env['mail.render.mixin']._prepend_preview(Markup(body), preview)
self.assertEqual(result, '''<div style="display:none;font-size:1px;height:0px;width:0px;opacity:0;">
foo<t t-out="&#34;false&#34; if 1 &gt; 2 else &#34;true&#34;"/>bar
</div>body''')

@users('employee')
def test_render_field(self):
template = self.env['mail.template'].browse(self.test_template.ids)
Expand All @@ -176,6 +185,10 @@ def test_render_field(self):
)[partner.id]
self.assertEqual(rendered, expected)

@users('employee')
def test_render_field_lang(self):
""" Test translation in french """
template = self.env['mail.template'].browse(self.test_template.ids)
partner = self.env['res.partner'].browse(self.render_object_fr.ids)
for fname, expected in zip(['subject', 'body_html'], self.base_rendered_fr):
rendered = template._render_field(
Expand All @@ -197,23 +210,6 @@ def test_render_template_inline_template(self):
)[partner.id]
self.assertEqual(rendered, expected)

@users('employee')
def test_render_template_local_links(self):
local_links_template_bits = [
'<div style="background-image:url(/web/path?a=a&b=b);"/>',
'<div style="background-image:url(\'/web/path?a=a&b=b\');"/>',
'<div style="background-image:url(&#34;/web/path?a=a&b=b&#34;);"/>',
]
base_url = self.env['mail.render.mixin'].get_base_url()
rendered_local_links = [
'<div style="background-image:url(%s/web/path?a=a&b=b);"/>' % base_url,
'<div style="background-image:url(\'%s/web/path?a=a&b=b\');"/>' % base_url,
'<div style="background-image:url(&#34;%s/web/path?a=a&b=b&#34;);"/>' % base_url
]
for source, expected in zip(local_links_template_bits, rendered_local_links):
rendered = self.env['mail.render.mixin']._replace_local_links(source)
self.assertEqual(rendered, expected)

@users('employee')
def test_render_template_qweb(self):
partner = self.env['res.partner'].browse(self.render_object.ids)
Expand All @@ -239,77 +235,7 @@ def test_render_template_qweb_view(self):
self.assertEqual(rendered, expected)

@users('employee')
def test_template_rendering_impersonate(self):
""" Test that the use of SUDO do not change the current user. """
partner = self.env['res.partner'].browse(self.render_object.ids)
src = '{{ user.name }} - {{ object.name }}'
expected = '%s - %s' % (self.env.user.name, partner.name)
result = self.env['mail.render.mixin'].sudo()._render_template_inline_template(
src, partner._name, partner.ids
)[partner.id]
self.assertIn(expected, result)

@users('user_rendering_restricted')
def test_template_rendering_function_call(self):
"""Test the case when the template call a custom function.
This function should not be called when the template is not rendered.
"""
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
partner = self.env[model].browse(res_ids)
MailRenderMixin = self.env['mail.render.mixin']

def cust_function():
# Can not use "MagicMock" in a Jinja sand-boxed environment
# so create our own function
cust_function.call = True
return 'return value'

cust_function.call = False

src = """<h1>This is a test</h1>
<p>{{ cust_function() }}</p>"""
expected = """<h1>This is a test</h1>
<p>return value</p>"""
context = {'cust_function': cust_function}

result = self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_inline_template(
src, partner._name, partner.ids,
add_context=context
)[partner.id]
self.assertEqual(expected, result)
self.assertTrue(cust_function.call)

with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
MailRenderMixin._render_template_inline_template(src, model, res_ids, add_context=context)

@users('user_rendering_restricted')
def test_template_render_static(self):
"""Test that we render correctly static templates (without placeholders)."""
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
MailRenderMixin = self.env['mail.render.mixin']

result = MailRenderMixin._render_template_inline_template(self.base_inline_template_bits[0], model, res_ids)[res_ids[0]]
self.assertEqual(result, self.base_inline_template_bits[0])

@users('user_rendering_restricted')
def test_template_rendering_restricted(self):
"""Test if we correctly detect static template."""
res_ids = self.env['res.partner'].search([], limit=1).ids
with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[3], 'res.partner', res_ids)

@users('employee')
def test_template_rendering_unrestricted(self):
"""Test if we correctly detect static template."""
res_ids = self.env['res.partner'].search([], limit=1).ids
result = self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[3], 'res.partner', res_ids)[res_ids[0]]
self.assertIn('26', result, 'Template Editor should be able to render inline_template code')

@users('employee')
def test_template_rendering_various(self):
def test_render_template_various(self):
""" Test static rendering """
partner = self.env['res.partner'].browse(self.render_object.ids)
MailRenderMixin = self.env['mail.render.mixin']
Expand Down Expand Up @@ -376,6 +302,131 @@ def test_template_rendering_various(self):
)[partner.id]
self.assertEqual(result, expected)

@users('employee')
def test_replace_local_links(self):
local_links_template_bits = [
'<div style="background-image:url(/web/path?a=a&b=b);"/>',
'<div style="background-image:url(\'/web/path?a=a&b=b\');"/>',
'<div style="background-image:url(&#34;/web/path?a=a&b=b&#34;);"/>',
]
base_url = self.env['mail.render.mixin'].get_base_url()
rendered_local_links = [
'<div style="background-image:url(%s/web/path?a=a&b=b);"/>' % base_url,
'<div style="background-image:url(\'%s/web/path?a=a&b=b\');"/>' % base_url,
'<div style="background-image:url(&#34;%s/web/path?a=a&b=b&#34;);"/>' % base_url
]
for source, expected in zip(local_links_template_bits, rendered_local_links):
rendered = self.env['mail.render.mixin']._replace_local_links(source)
self.assertEqual(rendered, expected)


@tagged('mail_render')
class TestMailRenderSecurity(TestMailRenderCommon):
""" Test security of rendering, based on qweb finding + restricted rendering
group usage. """

@users('employee')
def test_render_inline_template_impersonate(self):
""" Test that the use of SUDO do not change the current user. """
partner = self.env['res.partner'].browse(self.render_object.ids)
src = '{{ user.name }} - {{ object.name }}'
expected = '%s - %s' % (self.env.user.name, partner.name)
result = self.env['mail.render.mixin'].sudo()._render_template_inline_template(
src, partner._name, partner.ids
)[partner.id]
self.assertIn(expected, result)

@users('user_rendering_restricted')
def test_render_inline_template_restricted(self):
"""Test if we correctly detect static template."""
res_ids = self.env['res.partner'].search([], limit=1).ids
with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
self.env['mail.render.mixin']._render_template_inline_template(
self.base_inline_template_bits[3],
'res.partner',
res_ids
)

src = """<h1>This is a static template</h1>"""
result = self.env['mail.render.mixin']._render_template_inline_template(
src,
'res.partner',
res_ids
)[res_ids[0]]
self.assertEqual(src, str(result))

@users('user_rendering_restricted')
def test_render_inline_template_restricted_static(self):
"""Test that we render correctly static templates (without placeholders)."""
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
MailRenderMixin = self.env['mail.render.mixin']

result = MailRenderMixin._render_template_inline_template(
self.base_inline_template_bits[0],
model,
res_ids
)[res_ids[0]]
self.assertEqual(result, self.base_inline_template_bits[0])

@users('employee')
def test_render_inline_template_unrestricted(self):
""" Test if we correctly detect static template. """
res_ids = self.env['res.partner'].search([], limit=1).ids
result = self.env['mail.render.mixin']._render_template_inline_template(
self.base_inline_template_bits[3],
'res.partner',
res_ids
)[res_ids[0]]
self.assertIn('26', result, 'Template Editor should be able to render inline_template code')

@users('user_rendering_restricted')
def test_render_template_qweb_restricted(self):
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
partner = self.env[model].browse(res_ids)

src = """<h1>This is a static template</h1>"""

result = self.env['mail.render.mixin']._render_template_qweb(src, model, res_ids)[
partner.id]
self.assertEqual(src, str(result))

@users('user_rendering_restricted')
def test_security_function_call(self):
"""Test the case when the template call a custom function.
This function should not be called when the template is not rendered.
"""
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
partner = self.env[model].browse(res_ids)
MailRenderMixin = self.env['mail.render.mixin']

def cust_function():
# Can not use "MagicMock" in a Jinja sand-boxed environment
# so create our own function
cust_function.call = True
return 'return value'

cust_function.call = False

src = """<h1>This is a test</h1>
<p>{{ cust_function() }}</p>"""
expected = """<h1>This is a test</h1>
<p>return value</p>"""
context = {'cust_function': cust_function}

result = self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_inline_template(
src, partner._name, partner.ids,
add_context=context
)[partner.id]
self.assertEqual(expected, result)
self.assertTrue(cust_function.call)

with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
MailRenderMixin._render_template_inline_template(src, model, res_ids, add_context=context)

@users('user_rendering_restricted')
def test_security_inline_template_restricted(self):
"""Test if we correctly detect condition block (which might contains code)."""
Expand All @@ -384,7 +435,7 @@ def test_security_inline_template_restricted(self):
self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids)

@users('employee')
def test_is_inline_template_condition_block_unrestricted(self):
def test_security_inline_template_unrestricted(self):
"""Test if we correctly detect condition block (which might contains code)."""
res_ids = self.env['res.partner'].search([], limit=1).ids
result = self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids)[res_ids[0]]
Expand Down Expand Up @@ -417,36 +468,3 @@ def test_security_qweb_template_unrestricted(self):
res_ids = self.env['res.partner'].search([], limit=1).ids
result = self.env['mail.render.mixin']._render_template_qweb(self.base_qweb_bits[1], 'res.partner', res_ids)[res_ids[0]]
self.assertNotIn('Code not executed', result, 'The condition block did not work')

@users('user_rendering_restricted')
def test_template_rendering_static_inline_template(self):
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
partner = self.env[model].browse(res_ids)

src = """<h1>This is a static template</h1>"""

result = self.env['mail.render.mixin']._render_template_inline_template(
src, model, res_ids)[partner.id]
self.assertEqual(src, str(result))

@users('user_rendering_restricted')
def test_template_rendering_static_qweb(self):
model = 'res.partner'
res_ids = self.env[model].search([], limit=1).ids
partner = self.env[model].browse(res_ids)

src = """<h1>This is a static template</h1>"""

result = self.env['mail.render.mixin']._render_template_qweb(src, model, res_ids)[
partner.id]
self.assertEqual(src, str(result))

@users('employee')
def test_prepend_preview_inline_template_to_qweb(self):
body = 'body'
preview = 'foo{{"false" if 1 > 2 else "true"}}bar'
result = self.env['mail.render.mixin']._prepend_preview(Markup(body), preview)
self.assertEqual(result, '''<div style="display:none;font-size:1px;height:0px;width:0px;opacity:0;">
foo<t t-out="&#34;false&#34; if 1 &gt; 2 else &#34;true&#34;"/>bar
</div>body''')
4 changes: 3 additions & 1 deletion addons/mail/tests/test_mail_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from odoo.addons.mail.tests.common import MailCommon
from odoo.exceptions import AccessError, UserError
from odoo.modules.module import get_module_resource
from odoo.tests import Form, users
from odoo.tests import Form, tagged, users
from odoo.tools import convert_file


@tagged('mail_template')
class TestMailTemplate(MailCommon):

@classmethod
Expand Down Expand Up @@ -147,6 +148,7 @@ def test_server_archived_usage_protection(self):
self.assertFalse(server.active)


@tagged('mail_template')
class TestMailTemplateReset(MailCommon):

def _load(self, module, *args):
Expand Down
Loading

0 comments on commit 0053d0d

Please sign in to comment.