1"""
2    :codeauthor: Rahul Handay <rahulha@saltstack.com>
3"""
4
5import datetime
6import logging
7import os
8
9import pytest
10import salt.config
11import salt.loader
12import salt.modules.config as config
13import salt.modules.state as state
14import salt.state
15import salt.utils.args
16import salt.utils.files
17import salt.utils.hashutils
18import salt.utils.json
19import salt.utils.odict
20import salt.utils.platform
21import salt.utils.state
22from salt.exceptions import CommandExecutionError, SaltInvocationError
23from salt.utils.event import SaltEvent
24from tests.support.mock import MagicMock, Mock, mock_open, patch
25
26log = logging.getLogger(__name__)
27
28
29class MockState:
30    """
31    Mock class
32    """
33
34    def __init__(self):
35        pass
36
37    class State:
38        """
39        Mock state class
40        """
41
42        flag = None
43
44        def __init__(
45            self, opts, pillar_override=False, pillar_enc=None, initial_pillar=None
46        ):
47            pass
48
49        def verify_data(self, data):
50            """
51            Mock verify_data method
52            """
53            if self.flag:
54                return True
55            else:
56                return False
57
58        @staticmethod
59        def call(data):
60            """
61            Mock call method
62            """
63            return list
64
65        @staticmethod
66        def call_high(data, orchestration_jid=None):
67            """
68            Mock call_high method
69            """
70            return True
71
72        @staticmethod
73        def call_template_str(data):
74            """
75            Mock call_template_str method
76            """
77            return True
78
79        @staticmethod
80        def _mod_init(data):
81            """
82            Mock _mod_init method
83            """
84            return True
85
86        def verify_high(self, data):
87            """
88            Mock verify_high method
89            """
90            if self.flag:
91                return True
92            else:
93                return -1
94
95        @staticmethod
96        def compile_high_data(data):
97            """
98            Mock compile_high_data
99            """
100            return [{"__id__": "ABC"}]
101
102        @staticmethod
103        def call_chunk(data, data1, data2):
104            """
105            Mock call_chunk method
106            """
107            return {"": "ABC"}
108
109        @staticmethod
110        def call_chunks(data):
111            """
112            Mock call_chunks method
113            """
114            return True
115
116        @staticmethod
117        def call_listen(data, ret):
118            """
119            Mock call_listen method
120            """
121            return True
122
123        def requisite_in(self, data):  # pylint: disable=unused-argument
124            return data, []
125
126    class HighState:
127        """
128        Mock HighState class
129        """
130
131        flag = False
132        opts = {"state_top": "", "pillar": {}}
133
134        def __init__(self, opts, pillar_override=None, *args, **kwargs):
135            self.building_highstate = salt.utils.odict.OrderedDict
136            self.state = MockState.State(opts, pillar_override=pillar_override)
137
138        def render_state(self, sls, saltenv, mods, matches, local=False):
139            """
140            Mock render_state method
141            """
142            if self.flag:
143                return {}, True
144            else:
145                return {}, False
146
147        @staticmethod
148        def get_top():
149            """
150            Mock get_top method
151            """
152            return "_top"
153
154        def verify_tops(self, data):
155            """
156            Mock verify_tops method
157            """
158            if self.flag:
159                return ["a", "b"]
160            else:
161                return []
162
163        @staticmethod
164        def top_matches(data):
165            """
166            Mock top_matches method
167            """
168            return ["a", "b", "c"]
169
170        @staticmethod
171        def push_active():
172            """
173            Mock push_active method
174            """
175            return True
176
177        @staticmethod
178        def compile_highstate():
179            """
180            Mock compile_highstate method
181            """
182            return "A"
183
184        @staticmethod
185        def compile_state_usage():
186            """
187            Mock compile_state_usage method
188            """
189            return "A"
190
191        @staticmethod
192        def pop_active():
193            """
194            Mock pop_active method
195            """
196            return True
197
198        @staticmethod
199        def compile_low_chunks():
200            """
201            Mock compile_low_chunks method
202            """
203            return [{"__id__": "ABC", "__sls__": "abc"}]
204
205        def render_highstate(self, data):
206            """
207            Mock render_highstate method
208            """
209            if self.flag:
210                return ["a", "b"], True
211            else:
212                return ["a", "b"], False
213
214        @staticmethod
215        def call_highstate(
216            exclude,
217            cache,
218            cache_name,
219            force=None,
220            whitelist=None,
221            orchestration_jid=None,
222        ):
223            """
224            Mock call_highstate method
225            """
226            return True
227
228        def __enter__(self):
229            return self
230
231        def __exit__(self, *_):
232            pass
233
234
235class MockSerial:
236    """
237    Mock Class
238    """
239
240    @staticmethod
241    def load(data):
242        """
243        Mock load method
244        """
245        return {"A": "B"}
246
247    @staticmethod
248    def dump(data, data1):
249        """
250        Mock dump method
251        """
252        return True
253
254
255class MockTarFile:
256    """
257    Mock tarfile class
258    """
259
260    path = os.sep + "tmp"
261
262    def __init__(self):
263        pass
264
265    @staticmethod
266    def open(data, data1):
267        """
268        Mock open method
269        """
270        return MockTarFile
271
272    @staticmethod
273    def getmembers():
274        """
275        Mock getmembers method
276        """
277        return [MockTarFile]
278
279    @staticmethod
280    def extractall(data):
281        """
282        Mock extractall method
283        """
284        return True
285
286    @staticmethod
287    def close():
288        """
289        Mock close method
290        """
291        return True
292
293
294@pytest.fixture
295def configure_loader_modules(salt_minion_factory):
296    utils = salt.loader.utils(
297        salt_minion_factory.config.copy(),
298        whitelist=["state", "args", "systemd", "path", "platform"],
299    )
300    with patch("salt.modules.state.salt.state", MockState()):
301        yield {
302            state: {
303                "__opts__": {
304                    "cachedir": "/D",
305                    "saltenv": None,
306                    "sock_dir": "/var/run/salt/master",
307                    "transport": "zeromq",
308                    "__cli": "salt",
309                },
310                "__utils__": utils,
311                "__salt__": {
312                    "config.get": config.get,
313                    "config.option": MagicMock(return_value=""),
314                },
315            },
316            config: {"__opts__": {}, "__pillar__": {}},
317        }
318
319
320def test_running():
321    """
322    Test of checking i fthe state function is already running
323    """
324    assert state.running(True) == []
325
326    mock = MagicMock(
327        side_effect=[
328            [{"fun": "state.running", "pid": "4126", "jid": "20150325123407204096"}],
329            [],
330        ]
331    )
332    with patch.dict(state.__salt__, {"saltutil.is_running": mock}):
333        assert state.running() == [
334            'The function "state.running"'
335            " is running as PID 4126 and "
336            "was started at 2015, Mar 25 12:34:07."
337            "204096 with jid 20150325123407204096"
338        ]
339
340        assert state.running() == []
341
342
343def test_low():
344    """
345    Test of executing a single low data call
346    """
347    with patch.object(state, "_check_queue", side_effect=[False, None, None]):
348        assert not state.low({"state": "pkg", "fun": "installed", "name": "vi"})
349
350        MockState.State.flag = False
351        assert state.low({"state": "pkg", "fun": "installed", "name": "vi"}) == list
352
353        MockState.State.flag = True
354        assert state.low({"state": "pkg", "fun": "installed", "name": "vi"})
355
356
357def test_high():
358    """
359    Test for checking the state system
360    """
361    with patch.object(state, "_check_queue", side_effect=[False, None]):
362        assert not state.high({"vim": {"pkg": ["installed"]}})
363
364        with patch.object(
365            salt.utils.state, "get_sls_opts", return_value={"test": True}
366        ):
367            assert state.high({"vim": {"pkg": ["installed"]}})
368
369
370def test_template():
371    """
372    Test of executing the information
373    stored in a template file on the minion
374    """
375    with patch.object(state, "_check_queue", side_effect=[False, None, None]):
376        assert not state.template("/home/salt/salt.sls")
377
378        MockState.HighState.flag = True
379        assert state.template("/home/salt/salt.sls")
380
381        MockState.HighState.flag = False
382        assert state.template("/home/salt/salt.sls")
383
384
385def test_template_str():
386    """
387    Test for Executing the information
388    stored in a string from an sls template
389    """
390    with patch.object(state, "_check_queue", side_effect=[False, None]):
391        assert not state.template_str("Template String")
392
393        assert state.template_str("Template String")
394
395
396def test_apply_():
397    """
398    Test to apply states
399    """
400    with patch.object(state, "sls", return_value=True):
401        assert state.apply_(True)
402
403    with patch.object(state, "highstate", return_value=True):
404        assert state.apply_(None)
405
406
407def test_test():
408    """
409    Test to apply states in test mode
410    """
411    with patch.dict(state.__opts__, {"test": False}):
412        with patch.object(state, "sls", return_value=True) as mock:
413            assert state.test(True)
414            mock.assert_called_once_with(True, test=True)
415            assert state.__opts__["test"] is False
416
417        with patch.object(state, "highstate", return_value=True) as mock:
418            assert state.test(None)
419            mock.assert_called_once_with(test=True)
420            assert state.__opts__["test"] is False
421
422
423def test_list_disabled():
424    """
425    Test to list disabled states
426    """
427    mock = MagicMock(return_value=["A", "B", "C"])
428    with patch.dict(state.__salt__, {"grains.get": mock}):
429        assert state.list_disabled() == ["A", "B", "C"]
430
431
432def test_enable():
433    """
434    Test to Enable state function or sls run
435    """
436    mock = MagicMock(return_value=["A", "B"])
437    with patch.dict(state.__salt__, {"grains.get": mock}):
438        mock = MagicMock(return_value=[])
439        with patch.dict(state.__salt__, {"grains.setval": mock}):
440            mock = MagicMock(return_value=[])
441            with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}):
442                assert state.enable("A") == {
443                    "msg": "Info: A state enabled.",
444                    "res": True,
445                }
446
447                assert state.enable("Z") == {
448                    "msg": "Info: Z state already " "enabled.",
449                    "res": True,
450                }
451
452
453def test_disable():
454    """
455    Test to disable state run
456    """
457    mock = MagicMock(return_value=["C", "D"])
458    with patch.dict(state.__salt__, {"grains.get": mock}):
459        mock = MagicMock(return_value=[])
460        with patch.dict(state.__salt__, {"grains.setval": mock}):
461            mock = MagicMock(return_value=[])
462            with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}):
463                assert state.disable("C") == {
464                    "msg": "Info: C state " "already disabled.",
465                    "res": True,
466                }
467
468                assert state.disable("Z") == {
469                    "msg": "Info: Z state " "disabled.",
470                    "res": True,
471                }
472
473
474def test_clear_cache():
475    """
476    Test to clear out cached state file
477    """
478    mock = MagicMock(return_value=["A.cache.p", "B.cache.p", "C"])
479    with patch.object(os, "listdir", mock):
480        mock = MagicMock(return_value=True)
481        with patch.object(os.path, "isfile", mock):
482            mock = MagicMock(return_value=True)
483            with patch.object(os, "remove", mock):
484                assert state.clear_cache() == ["A.cache.p", "B.cache.p"]
485
486
487def test_single():
488    """
489    Test to execute single state function
490    """
491    ret = {"pkg_|-name=vim_|-name=vim_|-installed": list}
492    mock = MagicMock(side_effect=["A", None, None, None, None])
493    with patch.object(state, "_check_queue", mock):
494        assert state.single("pkg.installed", " name=vim") == "A"
495
496        assert state.single("pk", "name=vim") == "Invalid function passed"
497
498        with patch.dict(state.__opts__, {"test": "install"}):
499            mock = MagicMock(return_value={"test": ""})
500            with patch.object(salt.utils.state, "get_sls_opts", mock):
501                mock = MagicMock(return_value=True)
502                with patch.object(salt.utils.args, "test_mode", mock):
503                    pytest.raises(
504                        SaltInvocationError,
505                        state.single,
506                        "pkg.installed",
507                        "name=vim",
508                        pillar="A",
509                    )
510
511                    MockState.State.flag = True
512                    assert state.single("pkg.installed", "name=vim")
513
514                    MockState.State.flag = False
515                    assert state.single("pkg.installed", "name=vim") == ret
516
517
518def test_show_top():
519    """
520    Test to return the top data that the minion will use for a highstate
521    """
522    mock = MagicMock(side_effect=["A", None, None])
523    with patch.object(state, "_check_queue", mock):
524        assert state.show_top() == "A"
525
526        MockState.HighState.flag = True
527        assert state.show_top() == ["a", "b"]
528
529        MockState.HighState.flag = False
530        assert state.show_top() == ["a", "b", "c"]
531
532
533def test_run_request():
534    """
535    Test to Execute the pending state request
536    """
537    mock = MagicMock(
538        side_effect=[{}, {"name": "A"}, {"name": {"mods": "A", "kwargs": {}}}]
539    )
540    with patch.object(state, "check_request", mock):
541        assert state.run_request("A") == {}
542
543        assert state.run_request("A") == {}
544
545        mock = MagicMock(return_value=["True"])
546        with patch.object(state, "apply_", mock):
547            mock = MagicMock(return_value="")
548            with patch.object(os, "remove", mock):
549                assert state.run_request("name") == ["True"]
550
551
552def test_show_highstate():
553    """
554    Test to retrieve the highstate data from the salt master
555    """
556    mock = MagicMock(side_effect=["A", None, None])
557    with patch.object(state, "_check_queue", mock):
558        assert state.show_highstate() == "A"
559
560        pytest.raises(SaltInvocationError, state.show_highstate, pillar="A")
561
562        assert state.show_highstate() == "A"
563
564
565def test_show_lowstate():
566    """
567    Test to list out the low data that will be applied to this minion
568    """
569    mock = MagicMock(side_effect=["A", None])
570    with patch.object(state, "_check_queue", mock):
571        pytest.raises(AssertionError, state.show_lowstate)
572
573        assert state.show_lowstate()
574
575
576def test_show_state_usage():
577    """
578    Test to list out the state usage that will be applied to this minion
579    """
580
581    mock = MagicMock(side_effect=["A", None, None])
582    with patch.object(state, "_check_queue", mock):
583        assert state.show_state_usage() == "A"
584
585        pytest.raises(SaltInvocationError, state.show_state_usage, pillar="A")
586
587        assert state.show_state_usage() == "A"
588
589
590def test_show_states():
591    """
592    Test to display the low data from a specific sls
593    """
594    mock = MagicMock(side_effect=["A", None])
595    with patch.object(state, "_check_queue", mock):
596
597        assert state.show_low_sls("foo") == "A"
598        assert state.show_states("foo") == ["abc"]
599
600
601def test_show_states_missing_sls():
602    """
603    Test state.show_states when a sls file defined
604    in a top.sls file is missing
605    """
606    msg = ["No matching sls found for 'cloud' in evn 'base'"]
607    chunks_mock = MagicMock(side_effect=[msg])
608    mock = MagicMock(side_effect=["A", None])
609    with patch.object(state, "_check_queue", mock), patch(
610        "salt.state.HighState.compile_low_chunks", chunks_mock
611    ):
612        assert state.show_low_sls("foo") == "A"
613        assert state.show_states("foo") == [msg[0]]
614
615
616def test_sls_id():
617    """
618    Test to call a single ID from the
619    named module(s) and handle all requisites
620    """
621    mock = MagicMock(side_effect=["A", None, None, None])
622    with patch.object(state, "_check_queue", mock):
623        assert state.sls_id("apache", "http") == "A"
624
625        with patch.dict(state.__opts__, {"test": "A"}):
626            mock = MagicMock(return_value={"test": True, "saltenv": None})
627            with patch.object(salt.utils.state, "get_sls_opts", mock):
628                mock = MagicMock(return_value=True)
629                with patch.object(salt.utils.args, "test_mode", mock):
630                    MockState.State.flag = True
631                    MockState.HighState.flag = True
632                    assert state.sls_id("apache", "http") == 2
633
634                    MockState.State.flag = False
635                    assert state.sls_id("ABC", "http") == {"": "ABC"}
636                    pytest.raises(SaltInvocationError, state.sls_id, "DEF", "http")
637
638
639def test_show_low_sls():
640    """
641    Test to display the low data from a specific sls
642    """
643    mock = MagicMock(side_effect=["A", None, None])
644    with patch.object(state, "_check_queue", mock):
645        assert state.show_low_sls("foo") == "A"
646
647        with patch.dict(state.__opts__, {"test": "A"}):
648            mock = MagicMock(return_value={"test": True, "saltenv": None})
649            with patch.object(salt.utils.state, "get_sls_opts", mock):
650                MockState.State.flag = True
651                MockState.HighState.flag = True
652                assert state.show_low_sls("foo") == 2
653
654                MockState.State.flag = False
655                assert state.show_low_sls("foo") == [{"__id__": "ABC"}]
656
657
658def test_show_sls():
659    """
660    Test to display the state data from a specific sls
661    """
662    mock = MagicMock(side_effect=["A", None, None, None])
663    with patch.object(state, "_check_queue", mock):
664        assert state.show_sls("foo") == "A"
665
666        with patch.dict(state.__opts__, {"test": "A"}):
667            mock = MagicMock(return_value={"test": True, "saltenv": None})
668            with patch.object(salt.utils.state, "get_sls_opts", mock):
669                mock = MagicMock(return_value=True)
670                with patch.object(salt.utils.args, "test_mode", mock):
671                    pytest.raises(
672                        SaltInvocationError, state.show_sls, "foo", pillar="A"
673                    )
674
675                    MockState.State.flag = True
676                    assert state.show_sls("foo") == 2
677
678                    MockState.State.flag = False
679                    assert state.show_sls("foo") == ["a", "b"]
680
681
682def test_sls_exists():
683    """
684    Test of sls_exists
685    """
686    test_state = {}
687    test_missing_state = []
688
689    mock = MagicMock(return_value=test_state)
690    with patch.object(state, "show_sls", mock):
691        assert state.sls_exists("state_name")
692    mock = MagicMock(return_value=test_missing_state)
693    with patch.object(state, "show_sls", mock):
694        assert not state.sls_exists("missing_state")
695
696
697def test_id_exists():
698    """
699    Test of id_exists
700    """
701    test_state = [
702        {
703            "key1": "value1",
704            "name": "value1",
705            "state": "file",
706            "fun": "test",
707            "__env__": "base",
708            "__sls__": "test-sls",
709            "order": 10000,
710            "__id__": "state_id1",
711        },
712        {
713            "key2": "value2",
714            "name": "value2",
715            "state": "file",
716            "fun": "directory",
717            "__env__": "base",
718            "__sls__": "test-sls",
719            "order": 10001,
720            "__id__": "state_id2",
721        },
722    ]
723    mock = MagicMock(return_value=test_state)
724    with patch.object(state, "show_low_sls", mock):
725        assert state.id_exists("state_id1,state_id2", "test-sls")
726        assert not state.id_exists("invalid", "state_name")
727
728
729def test_top():
730    """
731    Test to execute a specific top file
732    """
733    ret = ["Pillar failed to render with the following messages:", "E"]
734    mock = MagicMock(side_effect=["A", None, None, None])
735    with patch.object(state, "_check_queue", mock):
736        assert state.top("reverse_top.sls") == "A"
737
738        mock = MagicMock(side_effect=[["E"], None, None])
739        with patch.object(state, "_get_pillar_errors", mock):
740            with patch.dict(state.__pillar__, {"_errors": ["E"]}):
741                assert state.top("reverse_top.sls") == ret
742
743            with patch.dict(state.__opts__, {"test": "A"}):
744                mock = MagicMock(return_value={"test": True})
745                with patch.object(salt.utils.state, "get_sls_opts", mock):
746                    mock = MagicMock(return_value=True)
747                    with patch.object(salt.utils.args, "test_mode", mock):
748                        pytest.raises(
749                            SaltInvocationError,
750                            state.top,
751                            "reverse_top.sls",
752                            pillar="A",
753                        )
754
755                        mock = MagicMock(return_value="salt://reverse_top.sls")
756                        with patch.object(os.path, "join", mock):
757                            mock = MagicMock(return_value=True)
758                            with patch.object(state, "_set_retcode", mock):
759                                assert state.top(
760                                    "reverse_top.sls " "exclude=exclude.sls"
761                                )
762
763
764def test_highstate():
765    """
766    Test to retrieve the state data from the
767    salt master for the minion and execute it
768    """
769    arg = "whitelist=sls1.sls"
770    mock = MagicMock(side_effect=[True, False, False, False])
771    with patch.object(state, "_disabled", mock):
772        assert state.highstate("whitelist=sls1.sls") == {
773            "comment": "Disabled",
774            "name": "Salt highstate run is disabled. "
775            "To re-enable, run state.enable highstate",
776            "result": "False",
777        }
778
779        mock = MagicMock(side_effect=["A", None, None])
780        with patch.object(state, "_check_queue", mock):
781            assert state.highstate("whitelist=sls1.sls") == "A"
782
783            with patch.dict(state.__opts__, {"test": "A"}):
784                mock = MagicMock(return_value={"test": True})
785                with patch.object(salt.utils.state, "get_sls_opts", mock):
786                    pytest.raises(
787                        SaltInvocationError,
788                        state.highstate,
789                        "whitelist=sls1.sls",
790                        pillar="A",
791                    )
792
793                    mock = MagicMock(return_value="A")
794                    with patch.object(state, "_filter_running", mock):
795                        mock = MagicMock(return_value=True)
796                        with patch.object(state, "_filter_running", mock):
797                            mock = MagicMock(return_value=True)
798                            with patch.object(salt.payload, "Serial", mock):
799                                with patch.object(os.path, "join", mock):
800                                    with patch.object(state, "_set" "_retcode", mock):
801                                        assert state.highstate(arg)
802
803
804def test_clear_request():
805    """
806    Test to clear out the state execution request without executing it
807    """
808    mock = MagicMock(return_value=True)
809    with patch.object(salt.payload, "Serial", mock):
810        mock = MagicMock(side_effect=[False, True, True])
811        with patch.object(os.path, "isfile", mock):
812            assert state.clear_request("A")
813
814            mock = MagicMock(return_value=True)
815            with patch.object(os, "remove", mock):
816                assert state.clear_request()
817
818            mock = MagicMock(return_value={})
819            with patch.object(state, "check_request", mock):
820                assert not state.clear_request("A")
821
822
823def test_check_request():
824    """
825    Test to return the state request information
826    """
827    with patch("salt.modules.state.salt.payload", MockSerial):
828        mock = MagicMock(side_effect=[True, True, False])
829        with patch.object(os.path, "isfile", mock):
830            with patch("salt.utils.files.fopen", mock_open(b"")):
831                assert state.check_request() == {"A": "B"}
832
833            with patch("salt.utils.files.fopen", mock_open("")):
834                assert state.check_request("A") == "B"
835
836            assert state.check_request() == {}
837
838
839def test_request():
840    """
841    Test to request the local admin execute a state run
842    """
843    mock = MagicMock(return_value=True)
844    with patch.object(state, "apply_", mock):
845        mock = MagicMock(return_value=True)
846        with patch.object(os.path, "join", mock):
847            mock = MagicMock(return_value={"test_run": "", "mods": "", "kwargs": ""})
848            with patch.object(state, "check_request", mock):
849                mock = MagicMock(return_value=True)
850                with patch.object(os, "umask", mock):
851                    with patch.object(salt.utils.platform, "is_windows", mock):
852                        with patch.dict(state.__salt__, {"cmd.run": mock}):
853                            with patch("salt.utils.files.fopen", mock_open()):
854                                mock = MagicMock(return_value=True)
855                                with patch.object(os, "umask", mock):
856                                    assert state.request("A")
857
858
859def test_sls():
860    """
861    Test to execute a set list of state files from an environment
862    """
863    arg = "core,edit.vim dev"
864    ret = ["Pillar failed to render with the following messages:", "E", "1"]
865    with patch.object(state, "running", return_value=True):
866        with patch.dict(state.__context__, {"retcode": 1}):
867            assert state.sls("core,edit.vim dev") is True
868
869    with patch.object(
870        state, "_wait", side_effect=[True, True, True, True, True, True]
871    ), patch.object(state, "_disabled", side_effect=[["A"], [], [], [], [], []]):
872        with patch.dict(state.__context__, {"retcode": 1}):
873            assert state.sls("core,edit.vim dev", None, None, True) == ["A"]
874
875        with patch.object(
876            state,
877            "_get_pillar_errors",
878            side_effect=[["E", "1"], None, None, None, None],
879        ):
880            with patch.dict(state.__context__, {"retcode": 5}), patch.dict(
881                state.__pillar__, {"_errors": ["E", "1"]}
882            ):
883                assert state.sls("core,edit.vim dev", None, None, True) == ret
884
885            with patch.dict(state.__opts__, {"test": None}), patch.object(
886                salt.utils.state,
887                "get_sls_opts",
888                return_value={"test": "", "saltenv": None},
889            ), patch.object(salt.utils.args, "test_mode", return_value=True):
890                pytest.raises(
891                    SaltInvocationError,
892                    state.sls,
893                    "core,edit.vim dev",
894                    None,
895                    None,
896                    True,
897                    pillar="A",
898                )
899                with patch.object(os.path, "join", return_value="/D/cache.cache.p"):
900                    with patch.object(os.path, "isfile", return_value=True), patch(
901                        "salt.utils.files.fopen", mock_open(b"")
902                    ):
903                        assert state.sls(arg, None, None, True, cache=True)
904
905                    MockState.HighState.flag = True
906                    assert state.sls("core,edit" ".vim dev", None, None, True)
907
908                    MockState.HighState.flag = False
909                    with patch.object(
910                        state, "_filter_" "running", return_value=True
911                    ), patch.object(os.path, "join", return_value=True), patch.object(
912                        os, "umask", return_value=True
913                    ), patch.object(
914                        salt.utils.platform, "is_windows", return_value=False
915                    ), patch.object(
916                        state, "_set_retcode", return_value=True
917                    ), patch.dict(
918                        state.__opts__, {"test": True}
919                    ), patch(
920                        "salt.utils.files.fopen", mock_open()
921                    ):
922                        assert state.sls("core,edit" ".vim dev", None, None, True)
923
924
925def test_get_test_value():
926    """
927    Test _get_test_value when opts contains different values
928    """
929    test_arg = "test"
930    with patch.dict(state.__opts__, {test_arg: True}):
931        assert state._get_test_value(
932            test=None
933        ), "Failure when {} is True in __opts__".format(test_arg)
934
935    with patch.dict(config.__pillar__, {test_arg: "blah"}):
936        assert not state._get_test_value(
937            test=None
938        ), "Failure when {} is blah in __opts__".format(test_arg)
939
940    with patch.dict(config.__pillar__, {test_arg: "true"}):
941        assert not state._get_test_value(
942            test=None
943        ), "Failure when {} is true in __opts__".format(test_arg)
944
945    with patch.dict(config.__opts__, {test_arg: False}):
946        assert not state._get_test_value(
947            test=None
948        ), "Failure when {} is False in __opts__".format(test_arg)
949
950    with patch.dict(config.__opts__, {}):
951        assert not state._get_test_value(
952            test=None
953        ), "Failure when {} does not exist in __opts__".format(test_arg)
954
955    with patch.dict(config.__pillar__, {test_arg: None}):
956        assert (
957            state._get_test_value(test=None) is None
958        ), "Failure when {} is None in __opts__".format(test_arg)
959
960    with patch.dict(config.__pillar__, {test_arg: True}):
961        assert state._get_test_value(
962            test=None
963        ), "Failure when {} is True in __pillar__".format(test_arg)
964
965    with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
966        assert state._get_test_value(
967            test=None
968        ), "Failure when {} is True in master __pillar__".format(test_arg)
969
970    with patch.dict(config.__pillar__, {"master": {test_arg: False}}):
971        with patch.dict(config.__pillar__, {test_arg: True}):
972            assert state._get_test_value(
973                test=None
974            ), "Failure when {} is False in master __pillar__ and True in pillar".format(
975                test_arg
976            )
977
978    with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
979        with patch.dict(config.__pillar__, {test_arg: False}):
980            assert not state._get_test_value(
981                test=None
982            ), "Failure when {} is True in master __pillar__ and False in pillar".format(
983                test_arg
984            )
985
986    with patch.dict(state.__opts__, {"test": False}):
987        assert not state._get_test_value(
988            test=None
989        ), "Failure when {} is False in __opts__".format(test_arg)
990
991    with patch.dict(state.__opts__, {"test": False}):
992        with patch.dict(config.__pillar__, {"master": {test_arg: True}}):
993            assert state._get_test_value(
994                test=None
995            ), "Failure when {} is False in __opts__".format(test_arg)
996
997    with patch.dict(state.__opts__, {}):
998        assert state._get_test_value(test=True), "Failure when test is True as arg"
999
1000
1001def test_sls_sync(subtests):
1002    """
1003    Test test.sls with the sync argument
1004
1005    We're only mocking the sync functions we expect to sync. If any other
1006    sync functions are run then they will raise a KeyError, which we want
1007    as it will tell us that we are syncing things we shouldn't.
1008    """
1009    expected_err_msg = "{} called {} time(s) (expected: {})"
1010    mock_empty_list = MagicMock(return_value=[])
1011    with patch.object(state, "running", mock_empty_list), patch.object(
1012        state, "_disabled", mock_empty_list
1013    ), patch.object(state, "_get_pillar_errors", mock_empty_list):
1014
1015        with subtests.test("sync_mods=modules,states"):
1016            sync_mocks = {
1017                "saltutil.sync_modules": Mock(),
1018                "saltutil.sync_states": Mock(),
1019            }
1020            if salt.utils.platform.is_windows():
1021                sync_mocks["cmd.run"] = Mock()
1022            with patch.dict(state.__salt__, sync_mocks):
1023                state.sls("foo", sync_mods="modules,states")
1024
1025            for key in sync_mocks:
1026                call_count = sync_mocks[key].call_count
1027                expected = 1
1028                assert call_count == expected, expected_err_msg.format(
1029                    key, call_count, expected
1030                )
1031
1032        with subtests.test("sync_mods=all"):
1033            # Test syncing all
1034            sync_mocks = {"saltutil.sync_all": Mock()}
1035            if salt.utils.platform.is_windows():
1036                sync_mocks["cmd.run"] = Mock()
1037            with patch.dict(state.__salt__, sync_mocks):
1038                state.sls("foo", sync_mods="all")
1039
1040            for key in sync_mocks:
1041                call_count = sync_mocks[key].call_count
1042                expected = 1
1043                assert call_count == expected, expected_err_msg.format(
1044                    key, call_count, expected
1045                )
1046
1047        with subtests.test("sync_mods=True"):
1048            # sync_mods=True should be interpreted as sync_mods=all
1049            sync_mocks = {"saltutil.sync_all": Mock()}
1050            if salt.utils.platform.is_windows():
1051                sync_mocks["cmd.run"] = Mock()
1052            with patch.dict(state.__salt__, sync_mocks):
1053                state.sls("foo", sync_mods=True)
1054
1055            for key in sync_mocks:
1056                call_count = sync_mocks[key].call_count
1057                expected = 1
1058                assert call_count == expected, expected_err_msg.format(
1059                    key, call_count, expected
1060                )
1061
1062        with subtests.test("sync_mods=modules,all"):
1063            # Test syncing all when "all" is passed along with module types.
1064            # This tests that we *only* run a sync_all and avoid unnecessary
1065            # extra syncing.
1066            sync_mocks = {"saltutil.sync_all": Mock()}
1067            if salt.utils.platform.is_windows():
1068                sync_mocks["cmd.run"] = Mock()
1069            with patch.dict(state.__salt__, sync_mocks):
1070                state.sls("foo", sync_mods="modules,all")
1071
1072            for key in sync_mocks:
1073                call_count = sync_mocks[key].call_count
1074                expected = 1
1075                assert call_count == expected, expected_err_msg.format(
1076                    key, call_count, expected
1077                )
1078
1079
1080def test_pkg():
1081    """
1082    Test to execute a packaged state run
1083    """
1084    tar_file = os.sep + os.path.join("tmp", "state_pkg.tgz")
1085    mock = MagicMock(
1086        side_effect=[False, True, True, True, True, True, True, True, True, True, True]
1087    )
1088    mock_json_loads_true = MagicMock(return_value=[True])
1089    mock_json_loads_dictlist = MagicMock(return_value=[{"test": ""}])
1090    with patch.object(os.path, "isfile", mock), patch(
1091        "salt.modules.state.tarfile", MockTarFile
1092    ), patch.object(salt.utils, "json", mock_json_loads_dictlist):
1093        assert state.pkg(tar_file, "", "md5") == {}
1094
1095        mock = MagicMock(side_effect=[False, 0, 0, 0, 0])
1096        with patch.object(salt.utils.hashutils, "get_hash", mock):
1097            # Verify hash
1098            assert state.pkg(tar_file, "", "md5") == {}
1099
1100            # Verify file outside intended root
1101            assert state.pkg(tar_file, 0, "md5") == {}
1102
1103            MockTarFile.path = ""
1104            with patch("salt.utils.files.fopen", mock_open()), patch.object(
1105                salt.utils.json, "loads", mock_json_loads_true
1106            ), patch.object(state, "_format_cached_grains", MagicMock()):
1107                assert state.pkg(tar_file, 0, "md5") is True
1108                state._format_cached_grains.assert_called_once()
1109
1110            MockTarFile.path = ""
1111            with patch("salt.utils.files.fopen", mock_open()):
1112                assert state.pkg(tar_file, 0, "md5")
1113
1114
1115def test_lock_saltenv():
1116    """
1117    Tests lock_saltenv in each function which accepts saltenv on the CLI
1118    """
1119    lock_msg = "lock_saltenv is enabled, saltenv cannot be changed"
1120    empty_list_mock = MagicMock(return_value=[])
1121    with patch.dict(state.__opts__, {"lock_saltenv": True}), patch.dict(
1122        state.__salt__, {"grains.get": empty_list_mock}
1123    ), patch.object(state, "running", empty_list_mock):
1124
1125        # Test high
1126        with pytest.raises(CommandExecutionError, match=lock_msg):
1127            state.high([{"vim": {"pkg": ["installed"]}}], saltenv="base")
1128
1129        # Test template
1130        with pytest.raises(CommandExecutionError, match=lock_msg):
1131            state.template("foo", saltenv="base")
1132
1133        # Test template_str
1134        with pytest.raises(CommandExecutionError, match=lock_msg):
1135            state.template_str("foo", saltenv="base")
1136
1137        # Test apply_ with SLS
1138        with pytest.raises(CommandExecutionError, match=lock_msg):
1139            state.apply_("foo", saltenv="base")
1140
1141        # Test apply_ with Highstate
1142        with pytest.raises(CommandExecutionError, match=lock_msg):
1143            state.apply_(saltenv="base")
1144
1145        # Test "test" with SLS
1146        with pytest.raises(CommandExecutionError, match=lock_msg):
1147            state.test("foo", saltenv="base")
1148
1149        # Test "test" with Highstate
1150        with pytest.raises(CommandExecutionError, match=lock_msg):
1151            state.test(saltenv="base")
1152
1153        # Test highstate
1154        with pytest.raises(CommandExecutionError, match=lock_msg):
1155            state.highstate(saltenv="base")
1156
1157        # Test sls
1158        with pytest.raises(CommandExecutionError, match=lock_msg):
1159            state.sls("foo", saltenv="base")
1160
1161        # Test top
1162        with pytest.raises(CommandExecutionError, match=lock_msg):
1163            state.top("foo.sls", saltenv="base")
1164
1165        # Test show_highstate
1166        with pytest.raises(CommandExecutionError, match=lock_msg):
1167            state.show_highstate(saltenv="base")
1168
1169        # Test show_lowstate
1170        with pytest.raises(CommandExecutionError, match=lock_msg):
1171            state.show_lowstate(saltenv="base")
1172
1173        # Test sls_id
1174        with pytest.raises(CommandExecutionError, match=lock_msg):
1175            state.sls_id("foo", "bar", saltenv="base")
1176
1177        # Test show_low_sls
1178        with pytest.raises(CommandExecutionError, match=lock_msg):
1179            state.show_low_sls("foo", saltenv="base")
1180
1181        # Test show_sls
1182        with pytest.raises(CommandExecutionError, match=lock_msg):
1183            state.show_sls("foo", saltenv="base")
1184
1185        # Test show_top
1186        with pytest.raises(CommandExecutionError, match=lock_msg):
1187            state.show_top(saltenv="base")
1188
1189        # Test single
1190        with pytest.raises(CommandExecutionError, match=lock_msg):
1191            state.single("foo.bar", name="baz", saltenv="base")
1192
1193        # Test pkg
1194        with pytest.raises(CommandExecutionError, match=lock_msg):
1195            state.pkg(
1196                "/tmp/salt_state.tgz",
1197                "760a9353810e36f6d81416366fc426dc",
1198                "md5",
1199                saltenv="base",
1200            )
1201
1202
1203def test_get_pillar_errors_CC():
1204    """
1205    Test _get_pillar_errors function.
1206    CC: External clean, Internal clean
1207    :return:
1208    """
1209    for int_pillar, ext_pillar in [
1210        ({"foo": "bar"}, {"fred": "baz"}),
1211        ({"foo": "bar"}, None),
1212        ({}, {"fred": "baz"}),
1213    ]:
1214        with patch("salt.modules.state.__pillar__", int_pillar):
1215            for opts, res in [
1216                ({"force": True}, None),
1217                ({"force": False}, None),
1218                ({}, None),
1219            ]:
1220                assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar)
1221
1222
1223def test_get_pillar_errors_EC():
1224    """
1225    Test _get_pillar_errors function.
1226    EC: External erroneous, Internal clean
1227    :return:
1228    """
1229    errors = ["failure", "everywhere"]
1230    for int_pillar, ext_pillar in [
1231        ({"foo": "bar"}, {"fred": "baz", "_errors": errors}),
1232        ({}, {"fred": "baz", "_errors": errors}),
1233    ]:
1234        with patch("salt.modules.state.__pillar__", int_pillar):
1235            for opts, res in [
1236                ({"force": True}, None),
1237                ({"force": False}, errors),
1238                ({}, errors),
1239            ]:
1240                assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar)
1241
1242
1243def test_get_pillar_errors_EE():
1244    """
1245    Test _get_pillar_errors function.
1246    CC: External erroneous, Internal erroneous
1247    :return:
1248    """
1249    errors = ["failure", "everywhere"]
1250    for int_pillar, ext_pillar in [
1251        ({"foo": "bar", "_errors": errors}, {"fred": "baz", "_errors": errors})
1252    ]:
1253        with patch("salt.modules.state.__pillar__", int_pillar):
1254            for opts, res in [
1255                ({"force": True}, None),
1256                ({"force": False}, errors),
1257                ({}, errors),
1258            ]:
1259                assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar)
1260
1261
1262def test_get_pillar_errors_CE():
1263    """
1264    Test _get_pillar_errors function.
1265    CC: External clean, Internal erroneous
1266    :return:
1267    """
1268    errors = ["failure", "everywhere"]
1269    for int_pillar, ext_pillar in [
1270        ({"foo": "bar", "_errors": errors}, {"fred": "baz"}),
1271        ({"foo": "bar", "_errors": errors}, None),
1272    ]:
1273        with patch("salt.modules.state.__pillar__", int_pillar):
1274            for opts, res in [
1275                ({"force": True}, None),
1276                ({"force": False}, errors),
1277                ({}, errors),
1278            ]:
1279                assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar)
1280
1281
1282def test_event():
1283    """
1284    test state.event runner
1285    """
1286    event_returns = {
1287        "data": {
1288            "body": b'{"text": "Hello World"}',
1289            "_stamp": "2021-01-08T00:12:32.320928",
1290        },
1291        "tag": "salt/engines/hook/test",
1292    }
1293
1294    _expected = '"body": "{\\"text\\": \\"Hello World\\"}"'
1295    with patch.object(SaltEvent, "get_event", return_value=event_returns):
1296        print_cli_mock = MagicMock()
1297        with patch.object(salt.utils.stringutils, "print_cli", print_cli_mock):
1298            found = False
1299            state.event(count=1)
1300            for x in print_cli_mock.mock_calls:
1301                if _expected in x.args[0]:
1302                    found = True
1303            assert found is True
1304
1305    now = datetime.datetime.now().isoformat()
1306    event_returns = {
1307        "data": {"date": now, "_stamp": "2021-01-08T00:12:32.320928"},
1308        "tag": "a_event_tag",
1309    }
1310
1311    _expected = '"date": "{}"'.format(now)
1312    with patch.object(SaltEvent, "get_event", return_value=event_returns):
1313        print_cli_mock = MagicMock()
1314        with patch.object(salt.utils.stringutils, "print_cli", print_cli_mock):
1315            found = False
1316            state.event(count=1)
1317            for x in print_cli_mock.mock_calls:
1318                if _expected in x.args[0]:
1319                    found = True
1320            assert found is True
1321