2016-06-02 13:26:39 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
2018-05-28 21:13:12 +00:00
|
|
|
:codeauthor: Nicole Thomas <nicole@saltstack.com>
|
2016-06-02 13:26:39 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
# Import Python libs
|
2017-12-15 18:14:18 +00:00
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
2016-08-11 16:48:23 +00:00
|
|
|
import os
|
2018-05-16 17:26:46 +00:00
|
|
|
import shutil
|
2016-08-25 18:43:53 +00:00
|
|
|
import tempfile
|
2016-06-02 13:26:39 +00:00
|
|
|
|
|
|
|
# Import Salt Testing libs
|
2017-02-27 15:59:04 +00:00
|
|
|
import tests.integration as integration
|
2017-02-27 13:58:07 +00:00
|
|
|
from tests.support.unit import TestCase, skipIf
|
2017-11-23 12:48:54 +00:00
|
|
|
from tests.support.mock import (
|
|
|
|
NO_MOCK,
|
|
|
|
NO_MOCK_REASON,
|
|
|
|
MagicMock,
|
|
|
|
patch)
|
2017-04-10 13:00:57 +00:00
|
|
|
from tests.support.mixins import AdaptedConfigurationTestCaseMixin
|
2018-05-16 17:26:46 +00:00
|
|
|
from tests.support.paths import BASE_FILES
|
2016-08-11 16:48:23 +00:00
|
|
|
|
2016-06-02 13:26:39 +00:00
|
|
|
# Import Salt libs
|
2016-08-11 16:48:23 +00:00
|
|
|
import salt.exceptions
|
2017-08-19 00:06:15 +00:00
|
|
|
import salt.state
|
2018-06-15 22:45:56 +00:00
|
|
|
from salt.utils.odict import OrderedDict
|
2017-11-22 17:16:31 +00:00
|
|
|
from salt.utils.decorators import state as statedecorators
|
2016-06-02 13:26:39 +00:00
|
|
|
|
2017-11-22 17:16:31 +00:00
|
|
|
try:
|
|
|
|
import pytest
|
|
|
|
except ImportError as err:
|
|
|
|
pytest = None
|
2016-06-02 13:26:39 +00:00
|
|
|
|
2017-11-23 11:03:05 +00:00
|
|
|
|
2016-06-02 13:26:39 +00:00
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
2017-04-10 13:00:57 +00:00
|
|
|
class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
2016-06-02 13:26:39 +00:00
|
|
|
'''
|
|
|
|
TestCase for the state compiler.
|
|
|
|
'''
|
|
|
|
|
|
|
|
def test_format_log_non_ascii_character(self):
|
|
|
|
'''
|
|
|
|
Tests running a non-ascii character through the state.format_log
|
|
|
|
function. See Issue #33605.
|
|
|
|
'''
|
|
|
|
# There is no return to test against as the format_log
|
|
|
|
# function doesn't return anything. However, we do want
|
|
|
|
# to make sure that the function doesn't stacktrace when
|
|
|
|
# called.
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 16:30:33 +00:00
|
|
|
ret = {'changes': {'Français': {'old': 'something old',
|
2016-06-02 18:32:21 +00:00
|
|
|
'new': 'something new'}},
|
|
|
|
'result': True}
|
2016-08-11 16:48:23 +00:00
|
|
|
salt.state.format_log(ret)
|
|
|
|
|
2017-04-10 13:00:57 +00:00
|
|
|
def test_render_error_on_invalid_requisite(self):
|
2016-08-11 16:48:23 +00:00
|
|
|
'''
|
|
|
|
Test that the state compiler correctly deliver a rendering
|
|
|
|
exception when a requisite cannot be resolved
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('salt.state.State._gather_pillar') as state_patch:
|
|
|
|
high_data = {
|
|
|
|
'git': OrderedDict([
|
|
|
|
('pkg', [
|
|
|
|
OrderedDict([
|
|
|
|
('require', [
|
|
|
|
OrderedDict([
|
|
|
|
('file', OrderedDict(
|
|
|
|
[('test1', 'test')]))])])]),
|
Update file state/execution modules and associated files with unicode_literals
This updates the file state and execution modules to use
unicode_literals. Since the serializers and the cmd module are touched
by the file state/exec module, those are also updated here, as well as
the cmd state module, for good measure.
Additionally, I found that salt.utils.data.decode_dict (and decode_list)
are misnamed for what they actually do. Since they *encode* the
contents, the functions should be named encode_dict and encode_list,
respectively. And we also should have counterparts which actually
decode, so I've added them. The compatibility functions from salt.utils
still use the old "decode" names to preserve backward compatibility, but
they now invoke the renamed "encode" functions in salt.utils.data. Note
that this means that the compatibility functions
salt.utils.decode_dict/list, and their cognates in salt.utils.data now
do different things, but since the move to salt.utils.data is also
happening in the Oxygen release this is as good a time as any to correct
this oversight.
I've updated the jinja filter docs to include information on the renamed
jinja filters, and also added a section on jinja filter renaming to the
Oxygen release notes. There was another filter that I renamed during the
process of moving functions from salt.utils which I did not properly
document in the release notes, so this is now included along with the
others.
2017-12-12 16:30:33 +00:00
|
|
|
'installed', {'order': 10000}]), ('__sls__', 'issue_35226'), ('__env__', 'base')])}
|
2017-04-10 13:00:57 +00:00
|
|
|
minion_opts = self.get_temp_config('minion')
|
|
|
|
minion_opts['pillar'] = {'git': OrderedDict([('test1', 'test')])}
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
with self.assertRaises(salt.exceptions.SaltRenderError):
|
|
|
|
state_obj.call_high(high_data)
|
|
|
|
|
|
|
|
|
|
|
|
class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
2016-08-25 18:43:53 +00:00
|
|
|
def setUp(self):
|
2017-04-10 13:00:57 +00:00
|
|
|
root_dir = tempfile.mkdtemp(dir=integration.TMP)
|
2018-05-16 17:26:46 +00:00
|
|
|
self.state_tree_dir = os.path.join(root_dir, 'state_tree')
|
2017-04-10 13:00:57 +00:00
|
|
|
cache_dir = os.path.join(root_dir, 'cachedir')
|
2018-05-16 17:26:46 +00:00
|
|
|
for dpath in (root_dir, self.state_tree_dir, cache_dir):
|
2017-04-10 13:00:57 +00:00
|
|
|
if not os.path.isdir(dpath):
|
|
|
|
os.makedirs(dpath)
|
|
|
|
|
|
|
|
overrides = {}
|
|
|
|
overrides['root_dir'] = root_dir
|
|
|
|
overrides['state_events'] = False
|
|
|
|
overrides['id'] = 'match'
|
|
|
|
overrides['file_client'] = 'local'
|
2018-05-16 17:26:46 +00:00
|
|
|
overrides['file_roots'] = dict(base=[self.state_tree_dir])
|
2017-04-10 13:00:57 +00:00
|
|
|
overrides['cachedir'] = cache_dir
|
|
|
|
overrides['test'] = False
|
|
|
|
self.config = self.get_temp_config('minion', **overrides)
|
|
|
|
self.addCleanup(delattr, self, 'config')
|
2016-08-25 19:09:07 +00:00
|
|
|
self.highstate = salt.state.HighState(self.config)
|
2017-04-10 13:00:57 +00:00
|
|
|
self.addCleanup(delattr, self, 'highstate')
|
2016-08-25 18:43:53 +00:00
|
|
|
self.highstate.push_active()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.highstate.pop_active()
|
|
|
|
|
|
|
|
def test_top_matches_with_list(self):
|
|
|
|
top = {'env': {'match': ['state1', 'state2'], 'nomatch': ['state3']}}
|
|
|
|
matches = self.highstate.top_matches(top)
|
|
|
|
self.assertEqual(matches, {'env': ['state1', 'state2']})
|
|
|
|
|
|
|
|
def test_top_matches_with_string(self):
|
|
|
|
top = {'env': {'match': 'state1', 'nomatch': 'state2'}}
|
|
|
|
matches = self.highstate.top_matches(top)
|
|
|
|
self.assertEqual(matches, {'env': ['state1']})
|
|
|
|
|
|
|
|
def test_matches_whitelist(self):
|
|
|
|
matches = {'env': ['state1', 'state2', 'state3']}
|
|
|
|
matches = self.highstate.matches_whitelist(matches, ['state2'])
|
|
|
|
self.assertEqual(matches, {'env': ['state2']})
|
|
|
|
|
|
|
|
def test_matches_whitelist_with_string(self):
|
|
|
|
matches = {'env': ['state1', 'state2', 'state3']}
|
|
|
|
matches = self.highstate.matches_whitelist(matches,
|
|
|
|
'state2,state3')
|
|
|
|
self.assertEqual(matches, {'env': ['state2', 'state3']})
|
|
|
|
|
2016-09-29 16:06:45 +00:00
|
|
|
def test_show_state_usage(self):
|
|
|
|
# monkey patch sub methods
|
|
|
|
self.highstate.avail = {
|
2016-09-30 09:07:52 +00:00
|
|
|
'base': ['state.a', 'state.b', 'state.c']
|
2016-09-29 16:06:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def verify_tops(*args, **kwargs):
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_top(*args, **kwargs):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def top_matches(*args, **kwargs):
|
|
|
|
return {'base': ['state.a', 'state.b']}
|
|
|
|
|
|
|
|
self.highstate.verify_tops = verify_tops
|
|
|
|
self.highstate.get_top = get_top
|
|
|
|
self.highstate.top_matches = top_matches
|
|
|
|
|
|
|
|
# get compile_state_usage() result
|
|
|
|
state_usage_dict = self.highstate.compile_state_usage()
|
|
|
|
|
|
|
|
self.assertEqual(state_usage_dict['base']['count_unused'], 1)
|
|
|
|
self.assertEqual(state_usage_dict['base']['count_used'], 2)
|
|
|
|
self.assertEqual(state_usage_dict['base']['count_all'], 3)
|
|
|
|
self.assertEqual(state_usage_dict['base']['used'], ['state.a', 'state.b'])
|
|
|
|
self.assertEqual(state_usage_dict['base']['unused'], ['state.c'])
|
|
|
|
|
2018-05-16 17:26:46 +00:00
|
|
|
def test_find_sls_ids_with_exclude(self):
|
|
|
|
'''
|
|
|
|
See https://github.com/saltstack/salt/issues/47182
|
|
|
|
'''
|
|
|
|
sls_dir = 'issue-47182'
|
|
|
|
shutil.copytree(
|
|
|
|
os.path.join(BASE_FILES, sls_dir),
|
|
|
|
os.path.join(self.state_tree_dir, sls_dir)
|
|
|
|
)
|
|
|
|
shutil.move(
|
|
|
|
os.path.join(self.state_tree_dir, sls_dir, 'top.sls'),
|
|
|
|
self.state_tree_dir
|
|
|
|
)
|
|
|
|
# Manually compile the high data. We don't have to worry about all of
|
|
|
|
# the normal error checking we do here since we know that all the SLS
|
|
|
|
# files exist and there is no whitelist/blacklist being used.
|
2018-05-16 20:13:54 +00:00
|
|
|
top = self.highstate.get_top() # pylint: disable=assignment-from-none
|
2018-05-16 17:26:46 +00:00
|
|
|
matches = self.highstate.top_matches(top)
|
|
|
|
high, _ = self.highstate.render_highstate(matches)
|
|
|
|
ret = salt.state.find_sls_ids('issue-47182.stateA.newer', high)
|
|
|
|
self.assertEqual(ret, [('somestuff', 'cmd')])
|
|
|
|
|
2016-08-25 18:43:53 +00:00
|
|
|
|
2017-11-22 17:16:31 +00:00
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
|
|
@skipIf(pytest is None, 'PyTest is missing')
|
2017-08-19 00:06:15 +00:00
|
|
|
class StateReturnsTestCase(TestCase):
|
|
|
|
'''
|
|
|
|
TestCase for code handling state returns.
|
|
|
|
'''
|
|
|
|
|
2017-11-22 17:17:27 +00:00
|
|
|
def test_state_output_check_changes_is_dict(self):
|
|
|
|
'''
|
|
|
|
Test that changes key contains a dictionary.
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = {'changes': []}
|
2018-01-17 13:13:25 +00:00
|
|
|
out = statedecorators.OutputUnifier('content_check')(lambda: data)()
|
|
|
|
assert "'Changes' should be a dictionary" in out['comment']
|
|
|
|
assert not out['result']
|
2017-11-22 17:17:27 +00:00
|
|
|
|
2017-11-22 17:17:53 +00:00
|
|
|
def test_state_output_check_return_is_dict(self):
|
|
|
|
'''
|
2017-11-22 17:30:48 +00:00
|
|
|
Test for the entire return is a dictionary
|
2017-11-22 17:17:53 +00:00
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = ['whatever']
|
2018-01-17 13:13:25 +00:00
|
|
|
out = statedecorators.OutputUnifier('content_check')(lambda: data)()
|
|
|
|
assert 'Malformed state return. Data must be a dictionary type' in out['comment']
|
|
|
|
assert not out['result']
|
2017-11-22 17:17:53 +00:00
|
|
|
|
2017-11-23 11:01:45 +00:00
|
|
|
def test_state_output_check_return_has_nrc(self):
|
2017-11-22 17:20:21 +00:00
|
|
|
'''
|
2017-11-22 17:30:48 +00:00
|
|
|
Test for name/result/comment keys are inside the return.
|
2017-11-22 17:20:21 +00:00
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = {'arbitrary': 'data', 'changes': {}}
|
2018-01-17 13:13:25 +00:00
|
|
|
out = statedecorators.OutputUnifier('content_check')(lambda: data)()
|
|
|
|
assert ' The following keys were not present in the state return: name, result, comment' in out['comment']
|
|
|
|
assert not out['result']
|
2017-11-22 17:20:21 +00:00
|
|
|
|
2017-12-08 17:40:24 +00:00
|
|
|
def test_state_output_unifier_comment_is_not_list(self):
|
2017-11-22 17:26:57 +00:00
|
|
|
'''
|
2017-11-22 17:30:48 +00:00
|
|
|
Test for output is unified so the comment is converted to a multi-line string
|
2017-11-22 17:26:57 +00:00
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': 'fantastic!'}
|
|
|
|
expected = {'comment': 'data\nin\nthe\nlist', 'changes': {}, 'name': None, 'result': True}
|
2018-01-17 13:13:25 +00:00
|
|
|
assert statedecorators.OutputUnifier('unify')(lambda: data)() == expected
|
2017-11-22 17:26:57 +00:00
|
|
|
|
2017-11-22 17:30:39 +00:00
|
|
|
data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': None}
|
|
|
|
expected = 'data\nin\nthe\nlist'
|
2018-01-17 13:13:25 +00:00
|
|
|
assert statedecorators.OutputUnifier('unify')(lambda: data)()['comment'] == expected
|
2017-11-22 17:30:39 +00:00
|
|
|
|
2017-12-08 17:40:24 +00:00
|
|
|
def test_state_output_unifier_result_converted_to_true(self):
|
2017-11-22 17:32:39 +00:00
|
|
|
'''
|
2017-11-22 17:33:43 +00:00
|
|
|
Test for output is unified so the result is converted to True
|
2017-11-22 17:32:39 +00:00
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': 'Fantastic'}
|
2018-01-17 13:13:25 +00:00
|
|
|
assert statedecorators.OutputUnifier('unify')(lambda: data)()['result'] is True
|
2017-11-22 17:32:39 +00:00
|
|
|
|
2017-12-08 17:40:24 +00:00
|
|
|
def test_state_output_unifier_result_converted_to_false(self):
|
2017-11-22 17:33:58 +00:00
|
|
|
'''
|
|
|
|
Test for output is unified so the result is converted to False
|
|
|
|
:return:
|
|
|
|
'''
|
|
|
|
data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': ''}
|
2018-01-17 13:13:25 +00:00
|
|
|
assert statedecorators.OutputUnifier('unify')(lambda: data)()['result'] is False
|
2017-11-22 17:33:58 +00:00
|
|
|
|
2017-11-23 12:48:54 +00:00
|
|
|
|
|
|
|
class StateFormatSlotsTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
|
|
|
|
'''
|
|
|
|
TestCase for code handling slots
|
|
|
|
'''
|
|
|
|
def setUp(self):
|
|
|
|
with patch('salt.state.State._gather_pillar'):
|
|
|
|
minion_opts = self.get_temp_config('minion')
|
|
|
|
self.state_obj = salt.state.State(minion_opts)
|
|
|
|
|
|
|
|
def test_format_slots_no_slots(self):
|
|
|
|
'''
|
|
|
|
Test the format slots keeps data without slots untouched.
|
|
|
|
'''
|
|
|
|
cdata = {
|
|
|
|
'args': [
|
|
|
|
'arg',
|
|
|
|
],
|
|
|
|
'kwargs': {
|
|
|
|
'key': 'val',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.state_obj.format_slots(cdata)
|
|
|
|
self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'val'}})
|
|
|
|
|
|
|
|
def test_format_slots_arg(self):
|
|
|
|
'''
|
|
|
|
Test the format slots is calling a slot specified in args with corresponding arguments.
|
|
|
|
'''
|
|
|
|
cdata = {
|
|
|
|
'args': [
|
|
|
|
'__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)',
|
|
|
|
],
|
|
|
|
'kwargs': {
|
|
|
|
'key': 'val',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mock = MagicMock(return_value='fun_return')
|
|
|
|
with patch.dict(self.state_obj.functions, {'mod.fun': mock}):
|
|
|
|
self.state_obj.format_slots(cdata)
|
|
|
|
mock.assert_called_once_with('fun_arg', fun_key='fun_val')
|
|
|
|
self.assertEqual(cdata, {'args': ['fun_return'], 'kwargs': {'key': 'val'}})
|
|
|
|
|
|
|
|
def test_format_slots_kwarg(self):
|
|
|
|
'''
|
|
|
|
Test the format slots is calling a slot specified in kwargs with corresponding arguments.
|
|
|
|
'''
|
|
|
|
cdata = {
|
|
|
|
'args': [
|
|
|
|
'arg',
|
|
|
|
],
|
|
|
|
'kwargs': {
|
|
|
|
'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mock = MagicMock(return_value='fun_return')
|
|
|
|
with patch.dict(self.state_obj.functions, {'mod.fun': mock}):
|
|
|
|
self.state_obj.format_slots(cdata)
|
|
|
|
mock.assert_called_once_with('fun_arg', fun_key='fun_val')
|
|
|
|
self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'fun_return'}})
|
|
|
|
|
|
|
|
def test_format_slots_multi(self):
|
|
|
|
'''
|
|
|
|
Test the format slots is calling all slots with corresponding arguments when multiple slots
|
|
|
|
specified.
|
|
|
|
'''
|
|
|
|
cdata = {
|
|
|
|
'args': [
|
|
|
|
'__slot__:salt:test_mod.fun_a(a_arg, a_key=a_kwarg)',
|
|
|
|
'__slot__:salt:test_mod.fun_b(b_arg, b_key=b_kwarg)',
|
|
|
|
],
|
|
|
|
'kwargs': {
|
|
|
|
'kw_key_1': '__slot__:salt:test_mod.fun_c(c_arg, c_key=c_kwarg)',
|
|
|
|
'kw_key_2': '__slot__:salt:test_mod.fun_d(d_arg, d_key=d_kwarg)',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mock_a = MagicMock(return_value='fun_a_return')
|
|
|
|
mock_b = MagicMock(return_value='fun_b_return')
|
|
|
|
mock_c = MagicMock(return_value='fun_c_return')
|
|
|
|
mock_d = MagicMock(return_value='fun_d_return')
|
|
|
|
with patch.dict(self.state_obj.functions, {'test_mod.fun_a': mock_a,
|
|
|
|
'test_mod.fun_b': mock_b,
|
|
|
|
'test_mod.fun_c': mock_c,
|
|
|
|
'test_mod.fun_d': mock_d}):
|
|
|
|
self.state_obj.format_slots(cdata)
|
|
|
|
mock_a.assert_called_once_with('a_arg', a_key='a_kwarg')
|
|
|
|
mock_b.assert_called_once_with('b_arg', b_key='b_kwarg')
|
|
|
|
mock_c.assert_called_once_with('c_arg', c_key='c_kwarg')
|
|
|
|
mock_d.assert_called_once_with('d_arg', d_key='d_kwarg')
|
|
|
|
self.assertEqual(cdata, {'args': ['fun_a_return',
|
|
|
|
'fun_b_return'],
|
|
|
|
'kwargs': {'kw_key_1': 'fun_c_return',
|
|
|
|
'kw_key_2': 'fun_d_return'}})
|
|
|
|
|
|
|
|
def test_format_slots_malformed(self):
|
|
|
|
'''
|
|
|
|
Test the format slots keeps malformed slots untouched.
|
|
|
|
'''
|
|
|
|
sls_data = {
|
|
|
|
'args': [
|
|
|
|
'__slot__:NOT_SUPPORTED:not.called()',
|
|
|
|
'__slot__:salt:not.called(',
|
|
|
|
'__slot__:salt:',
|
|
|
|
'__slot__:salt',
|
|
|
|
'__slot__:',
|
|
|
|
'__slot__',
|
|
|
|
],
|
|
|
|
'kwargs': {
|
|
|
|
'key3': '__slot__:NOT_SUPPORTED:not.called()',
|
|
|
|
'key4': '__slot__:salt:not.called(',
|
|
|
|
'key5': '__slot__:salt:',
|
|
|
|
'key6': '__slot__:salt',
|
|
|
|
'key7': '__slot__:',
|
|
|
|
'key8': '__slot__',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cdata = sls_data.copy()
|
|
|
|
mock = MagicMock(return_value='return')
|
|
|
|
with patch.dict(self.state_obj.functions, {'not.called': mock}):
|
|
|
|
self.state_obj.format_slots(cdata)
|
|
|
|
mock.assert_not_called()
|
|
|
|
self.assertEqual(cdata, sls_data)
|