123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- """
- Unit tests for salt.utils.templates.py
- """
- import logging
- import os
- import sys
- from pathlib import PurePath, PurePosixPath
- import salt.utils.files
- import salt.utils.templates
- from tests.support import mock
- from tests.support.helpers import with_tempdir
- from tests.support.unit import TestCase, skipIf
- try:
- import Cheetah as _
- HAS_CHEETAH = True
- except ImportError:
- HAS_CHEETAH = False
- log = logging.getLogger(__name__)
- class RenderTestCase(TestCase):
- def setUp(self):
- # Default context for salt.utils.templates.render_*_tmpl to work
- self.context = {
- "opts": {"cachedir": "/D", "__cli": "salt"},
- "saltenv": None,
- }
- ### Tests for Jinja (whitespace-friendly)
- def test_render_jinja_sanity(self):
- tmpl = """OK"""
- res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- def test_render_jinja_evaluate(self):
- tmpl = """{{ "OK" }}"""
- res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- def test_render_jinja_evaluate_multi(self):
- tmpl = """{% if 1 -%}OK{%- endif %}"""
- res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- def test_render_jinja_variable(self):
- tmpl = """{{ var }}"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_jinja_tmpl(tmpl, ctx)
- self.assertEqual(res, "OK")
- ### Tests for mako template
- def test_render_mako_sanity(self):
- tmpl = """OK"""
- res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- def test_render_mako_evaluate(self):
- tmpl = """${ "OK" }"""
- res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- def test_render_mako_evaluate_multi(self):
- tmpl = """
- % if 1:
- OK
- % endif
- """
- res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context))
- stripped = res.strip()
- self.assertEqual(stripped, "OK")
- def test_render_mako_variable(self):
- tmpl = """${ var }"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_mako_tmpl(tmpl, ctx)
- self.assertEqual(res, "OK")
- ### Tests for wempy template
- @skipIf(
- sys.version_info > (3,),
- "The wempy module is currently unsupported under Python3",
- )
- def test_render_wempy_sanity(self):
- tmpl = """OK"""
- res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- @skipIf(
- sys.version_info > (3,),
- "The wempy module is currently unsupported under Python3",
- )
- def test_render_wempy_evaluate(self):
- tmpl = """{{="OK"}}"""
- res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- @skipIf(
- sys.version_info > (3,),
- "The wempy module is currently unsupported under Python3",
- )
- def test_render_wempy_evaluate_multi(self):
- tmpl = """{{if 1:}}OK{{pass}}"""
- res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- @skipIf(
- sys.version_info > (3,),
- "The wempy module is currently unsupported under Python3",
- )
- def test_render_wempy_variable(self):
- tmpl = """{{=var}}"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_wempy_tmpl(tmpl, ctx)
- self.assertEqual(res, "OK")
- ### Tests for genshi template (xml-based)
- def test_render_genshi_sanity(self):
- tmpl = """<RU>OK</RU>"""
- res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "<RU>OK</RU>")
- def test_render_genshi_evaluate(self):
- tmpl = """<RU>${ "OK" }</RU>"""
- res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "<RU>OK</RU>")
- def test_render_genshi_evaluate_condition(self):
- tmpl = """<RU xmlns:py="http://genshi.edgewall.org/" py:if="1">OK</RU>"""
- res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "<RU>OK</RU>")
- def test_render_genshi_variable(self):
- tmpl = """<RU>$var</RU>"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx)
- self.assertEqual(res, "<RU>OK</RU>")
- def test_render_genshi_variable_replace(self):
- tmpl = """<RU xmlns:py="http://genshi.edgewall.org/" py:content="var">not ok</RU>"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx)
- self.assertEqual(res, "<RU>OK</RU>")
- ### Tests for cheetah template (line-oriented and xml-friendly)
- @skipIf(not HAS_CHEETAH, "The Cheetah Python module is missing.")
- def test_render_cheetah_sanity(self):
- tmpl = """OK"""
- res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- @skipIf(not HAS_CHEETAH, "The Cheetah Python module is missing.")
- def test_render_cheetah_evaluate(self):
- tmpl = """<%="OK"%>"""
- res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context))
- self.assertEqual(res, "OK")
- @skipIf(not HAS_CHEETAH, "The Cheetah Python module is missing.")
- def test_render_cheetah_evaluate_xml(self):
- tmpl = """
- <% if 1: %>
- OK
- <% pass %>
- """
- res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context))
- stripped = res.strip()
- self.assertEqual(stripped, "OK")
- @skipIf(not HAS_CHEETAH, "The Cheetah Python module is missing.")
- def test_render_cheetah_evaluate_text(self):
- tmpl = """
- #if 1
- OK
- #end if
- """
- res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context))
- stripped = res.strip()
- self.assertEqual(stripped, "OK")
- @skipIf(not HAS_CHEETAH, "The Cheetah Python module is missing.")
- def test_render_cheetah_variable(self):
- tmpl = """$var"""
- ctx = dict(self.context)
- ctx["var"] = "OK"
- res = salt.utils.templates.render_cheetah_tmpl(tmpl, ctx)
- self.assertEqual(res.strip(), "OK")
- class MockRender:
- def __call__(self, tplstr, context, tmplpath=None):
- self.tplstr = tplstr
- self.context = context
- self.tmplpath = tmplpath
- return tplstr
- class WrapRenderTestCase(TestCase):
- def assertDictContainsAll(self, actual, **expected):
- """ Make sure dictionary contains at least all expected values"""
- actual = {key: actual[key] for key in expected if key in actual}
- self.assertEqual(expected, actual)
- def _test_generated_sls_context(self, tmplpath, sls, **expected):
- """ Generic SLS Context Test"""
- # DeNormalize tmplpath
- tmplpath = str(PurePath(PurePosixPath(tmplpath)))
- if tmplpath.startswith("\\"):
- tmplpath = "C:{}".format(tmplpath)
- expected["tplpath"] = tmplpath
- actual = salt.utils.templates._generate_sls_context(tmplpath, sls)
- self.assertDictContainsAll(actual, **expected)
- @mock.patch("salt.utils.templates.generate_sls_context")
- @with_tempdir()
- def test_sls_context_call(self, tempdir, generate_sls_context):
- """ Check that generate_sls_context is called with proper parameters"""
- sls = "foo.bar"
- tmplpath = "/tmp/foo/bar.sls"
- slsfile = os.path.join(tempdir, "foo")
- with salt.utils.files.fopen(slsfile, "w") as fp:
- fp.write("{{ slspath }}")
- context = {"opts": {}, "saltenv": "base", "sls": sls}
- render = MockRender()
- wrapped = salt.utils.templates.wrap_tmpl_func(render)
- res = wrapped(slsfile, context=context, tmplpath=tmplpath)
- generate_sls_context.assert_called_with(tmplpath, sls)
- @mock.patch("salt.utils.templates.generate_sls_context")
- @with_tempdir()
- def test_sls_context_no_call(self, tempdir, generate_sls_context):
- """ Check that generate_sls_context is not called if sls is not set"""
- sls = "foo.bar"
- tmplpath = "/tmp/foo/bar.sls"
- slsfile = os.path.join(tempdir, "foo")
- with salt.utils.files.fopen(slsfile, "w") as fp:
- fp.write("{{ slspath }}")
- context = {"opts": {}, "saltenv": "base"}
- render = MockRender()
- wrapped = salt.utils.templates.wrap_tmpl_func(render)
- res = wrapped(slsfile, context=context, tmplpath=tmplpath)
- generate_sls_context.assert_not_called()
- def test_generate_sls_context__top_level(self):
- """ generate_sls_context - top_level Use case"""
- self._test_generated_sls_context(
- "/tmp/boo.sls",
- "boo",
- tplfile="boo.sls",
- tpldir=".",
- tpldot="",
- slsdotpath="",
- slscolonpath="",
- sls_path="",
- slspath="",
- )
- def test_generate_sls_context__one_level_init_implicit(self):
- """ generate_sls_context - Basic one level with implicit init.sls """
- self._test_generated_sls_context(
- "/tmp/foo/init.sls",
- "foo",
- tplfile="foo/init.sls",
- tpldir="foo",
- tpldot="foo",
- slsdotpath="foo",
- slscolonpath="foo",
- sls_path="foo",
- slspath="foo",
- )
- def test_generate_sls_context__one_level_init_explicit(self):
- """ generate_sls_context - Basic one level with explicit init.sls """
- self._test_generated_sls_context(
- "/tmp/foo/init.sls",
- "foo.init",
- tplfile="foo/init.sls",
- tpldir="foo",
- tpldot="foo",
- slsdotpath="foo",
- slscolonpath="foo",
- sls_path="foo",
- slspath="foo",
- )
- def test_generate_sls_context__one_level(self):
- """ generate_sls_context - Basic one level with name"""
- self._test_generated_sls_context(
- "/tmp/foo/boo.sls",
- "foo.boo",
- tplfile="foo/boo.sls",
- tpldir="foo",
- tpldot="foo",
- slsdotpath="foo",
- slscolonpath="foo",
- sls_path="foo",
- slspath="foo",
- )
- def test_generate_sls_context__one_level_repeating(self):
- """ generate_sls_context - Basic one level with name same as dir
- (Issue #56410)
- """
- self._test_generated_sls_context(
- "/tmp/foo/foo.sls",
- "foo.foo",
- tplfile="foo/foo.sls",
- tpldir="foo",
- tpldot="foo",
- slsdotpath="foo",
- slscolonpath="foo",
- sls_path="foo",
- slspath="foo",
- )
- def test_generate_sls_context__two_level_init_implicit(self):
- """ generate_sls_context - Basic two level with implicit init.sls """
- self._test_generated_sls_context(
- "/tmp/foo/bar/init.sls",
- "foo.bar",
- tplfile="foo/bar/init.sls",
- tpldir="foo/bar",
- tpldot="foo.bar",
- slsdotpath="foo.bar",
- slscolonpath="foo:bar",
- sls_path="foo_bar",
- slspath="foo/bar",
- )
- def test_generate_sls_context__two_level_init_explicit(self):
- """ generate_sls_context - Basic two level with explicit init.sls """
- self._test_generated_sls_context(
- "/tmp/foo/bar/init.sls",
- "foo.bar.init",
- tplfile="foo/bar/init.sls",
- tpldir="foo/bar",
- tpldot="foo.bar",
- slsdotpath="foo.bar",
- slscolonpath="foo:bar",
- sls_path="foo_bar",
- slspath="foo/bar",
- )
- def test_generate_sls_context__two_level(self):
- """ generate_sls_context - Basic two level with name"""
- self._test_generated_sls_context(
- "/tmp/foo/bar/boo.sls",
- "foo.bar.boo",
- tplfile="foo/bar/boo.sls",
- tpldir="foo/bar",
- tpldot="foo.bar",
- slsdotpath="foo.bar",
- slscolonpath="foo:bar",
- sls_path="foo_bar",
- slspath="foo/bar",
- )
- def test_generate_sls_context__two_level_repeating(self):
- """ generate_sls_context - Basic two level with name same as dir
- (Issue #56410)
- """
- self._test_generated_sls_context(
- "/tmp/foo/foo/foo.sls",
- "foo.foo.foo",
- tplfile="foo/foo/foo.sls",
- tpldir="foo/foo",
- tpldot="foo.foo",
- slsdotpath="foo.foo",
- slscolonpath="foo:foo",
- sls_path="foo_foo",
- slspath="foo/foo",
- )
- @mock.patch(
- "salt.utils.templates._generate_sls_context_legacy", return_value="legacy"
- )
- @mock.patch("salt.utils.templates._generate_sls_context", return_value="new")
- @mock.patch("salt.utils.templates.features.get", return_value=True)
- def test_feature_flag_on(self, feature_get, new_impl, legacy_impl):
- """ Test feature flag selection with FF on"""
- tplpath = "tplpath"
- sls = "sls"
- self.assertEqual("new", salt.utils.templates.generate_sls_context(tplpath, sls))
- new_impl.assert_called_with(tplpath, sls)
- legacy_impl.assert_not_called()
- @mock.patch(
- "salt.utils.templates._generate_sls_context_legacy", return_value="legacy"
- )
- @mock.patch("salt.utils.templates._generate_sls_context", return_value="new")
- @mock.patch("salt.utils.templates.features.get", return_value=False)
- def test_feature_flag_off(self, feature_get, new_impl, legacy_impl):
- """ Test feature flag selection with FF on"""
- tplpath = "tplpath"
- sls = "sls"
- self.assertEqual(
- "legacy", salt.utils.templates.generate_sls_context(tplpath, sls)
- )
- new_impl.assert_not_called()
- legacy_impl.assert_called_with(tplpath, sls)
- @skipIf(sys.platform == "win32", "Backslash not possible under windows")
- def test_generate_sls_context__backslash_in_path(self):
- """ generate_sls_context - Handle backslash in path on non-windows
- """
- self._test_generated_sls_context(
- "/tmp/foo/foo\\foo.sls",
- "foo.foo\\foo",
- tplfile="foo/foo\\foo.sls",
- tpldir="foo",
- tpldot="foo",
- slsdotpath="foo",
- slscolonpath="foo",
- sls_path="foo",
- slspath="foo",
- )
|