1"""
2:maintainer:    Alberto Planas <aplanas@suse.com>
3:platform:      Linux
4"""
5
6
7import salt.modules.freezer as freezer
8from salt.exceptions import CommandExecutionError
9from tests.support.mixins import LoaderModuleMockMixin
10from tests.support.mock import MagicMock, patch
11from tests.support.unit import TestCase
12
13
14class FreezerTestCase(TestCase, LoaderModuleMockMixin):
15    """
16    Test cases for salt.modules.freezer
17    """
18
19    def setup_loader_modules(self):
20        return {freezer: {"__salt__": {}, "__opts__": {"cachedir": ""}}}
21
22    @patch("os.path.isfile")
23    def test_status(self, isfile):
24        """
25        Test if a frozen state exist.
26        """
27        isfile.side_effect = (True, True)
28        self.assertTrue(freezer.status())
29
30        isfile.side_effect = (True, False)
31        self.assertFalse(freezer.status())
32
33    @patch("os.listdir")
34    @patch("os.path.isdir")
35    def test_list(self, isdir, listdir):
36        """
37        Test the listing of all frozen states.
38        """
39        # There is no freezer directory
40        isdir.return_value = False
41        self.assertEqual(freezer.list_(), [])
42
43        # There is freezer directory, but is empty
44        isdir.return_value = True
45        listdir.return_value = []
46        self.assertEqual(freezer.list_(), [])
47
48        # There is freezer directory with states
49        isdir.return_value = True
50        listdir.return_value = [
51            "freezer-pkgs.yml",
52            "freezer-reps.yml",
53            "state-pkgs.yml",
54            "state-reps.yml",
55            "random-file",
56        ]
57        self.assertEqual(freezer.list_(), ["freezer", "state"])
58
59    @patch("os.makedirs")
60    def test_freeze_fails_cache(self, makedirs):
61        """
62        Test to freeze a current installation
63        """
64        # Fails when creating the freeze cache directory
65        makedirs.side_effect = OSError()
66        self.assertRaises(CommandExecutionError, freezer.freeze)
67
68    @patch("salt.modules.freezer.status")
69    @patch("os.makedirs")
70    def test_freeze_fails_already_frozen(self, makedirs, status):
71        """
72        Test to freeze a current installation
73        """
74        # Fails when there is already a frozen state
75        status.return_value = True
76        self.assertRaises(CommandExecutionError, freezer.freeze)
77        makedirs.assert_called_once()
78
79    @patch("salt.utils.json.dump")
80    @patch("salt.modules.freezer.fopen")
81    @patch("salt.modules.freezer.status")
82    @patch("os.makedirs")
83    def test_freeze_success_two_freeze(self, makedirs, status, fopen, dump):
84        """
85        Test to freeze a current installation
86        """
87        # Freeze the current new state
88        status.return_value = False
89        salt_mock = {
90            "pkg.list_pkgs": MagicMock(return_value={}),
91            "pkg.list_repos": MagicMock(return_value={}),
92        }
93        with patch.dict(freezer.__salt__, salt_mock):
94            self.assertTrue(freezer.freeze("one"))
95            self.assertTrue(freezer.freeze("two"))
96
97            self.assertEqual(makedirs.call_count, 2)
98            self.assertEqual(salt_mock["pkg.list_pkgs"].call_count, 2)
99            self.assertEqual(salt_mock["pkg.list_repos"].call_count, 2)
100            fopen.assert_called()
101            dump.assert_called()
102
103    @patch("salt.utils.json.dump")
104    @patch("salt.modules.freezer.fopen")
105    @patch("salt.modules.freezer.status")
106    @patch("os.makedirs")
107    def test_freeze_success_new_state(self, makedirs, status, fopen, dump):
108        """
109        Test to freeze a current installation
110        """
111        # Freeze the current new state
112        status.return_value = False
113        salt_mock = {
114            "pkg.list_pkgs": MagicMock(return_value={}),
115            "pkg.list_repos": MagicMock(return_value={}),
116        }
117        with patch.dict(freezer.__salt__, salt_mock):
118            self.assertTrue(freezer.freeze())
119            makedirs.assert_called_once()
120            salt_mock["pkg.list_pkgs"].assert_called_once()
121            salt_mock["pkg.list_repos"].assert_called_once()
122            fopen.assert_called()
123            dump.assert_called()
124
125    @patch("salt.utils.json.dump")
126    @patch("salt.modules.freezer.fopen")
127    @patch("salt.modules.freezer.status")
128    @patch("os.makedirs")
129    def test_freeze_success_force(self, makedirs, status, fopen, dump):
130        """
131        Test to freeze a current installation
132        """
133        # Freeze the current old state
134        status.return_value = True
135        salt_mock = {
136            "pkg.list_pkgs": MagicMock(return_value={}),
137            "pkg.list_repos": MagicMock(return_value={}),
138        }
139        with patch.dict(freezer.__salt__, salt_mock):
140            self.assertTrue(freezer.freeze(force=True))
141            makedirs.assert_called_once()
142            salt_mock["pkg.list_pkgs"].assert_called_once()
143            salt_mock["pkg.list_repos"].assert_called_once()
144            fopen.assert_called()
145            dump.assert_called()
146
147    @patch("salt.modules.freezer.status")
148    def test_restore_fails_missing_state(self, status):
149        """
150        Test to restore an old state
151        """
152        # Fails if the state is not found
153        status.return_value = False
154        self.assertRaises(CommandExecutionError, freezer.restore)
155
156    @patch("salt.utils.json.load")
157    @patch("salt.modules.freezer.fopen")
158    @patch("salt.modules.freezer.status")
159    def test_restore_add_missing_repo(self, status, fopen, load):
160        """
161        Test to restore an old state
162        """
163        # Only a missing repo is installed
164        status.return_value = True
165        load.side_effect = ({}, {"missing-repo": {}})
166        salt_mock = {
167            "pkg.list_pkgs": MagicMock(return_value={}),
168            "pkg.list_repos": MagicMock(return_value={}),
169            "pkg.mod_repo": MagicMock(),
170        }
171        with patch.dict(freezer.__salt__, salt_mock):
172            self.assertEqual(
173                freezer.restore(),
174                {
175                    "pkgs": {"add": [], "remove": []},
176                    "repos": {"add": ["missing-repo"], "remove": []},
177                    "comment": [],
178                },
179            )
180            salt_mock["pkg.list_pkgs"].assert_called()
181            salt_mock["pkg.list_repos"].assert_called()
182            salt_mock["pkg.mod_repo"].assert_called_once()
183            fopen.assert_called()
184            load.assert_called()
185
186    @patch("salt.utils.json.load")
187    @patch("salt.modules.freezer.fopen")
188    @patch("salt.modules.freezer.status")
189    def test_restore_add_missing_package(self, status, fopen, load):
190        """
191        Test to restore an old state
192        """
193        # Only a missing package is installed
194        status.return_value = True
195        load.side_effect = ({"missing-package": {}}, {})
196        salt_mock = {
197            "pkg.list_pkgs": MagicMock(return_value={}),
198            "pkg.list_repos": MagicMock(return_value={}),
199            "pkg.install": MagicMock(),
200        }
201        with patch.dict(freezer.__salt__, salt_mock):
202            self.assertEqual(
203                freezer.restore(),
204                {
205                    "pkgs": {"add": ["missing-package"], "remove": []},
206                    "repos": {"add": [], "remove": []},
207                    "comment": [],
208                },
209            )
210            salt_mock["pkg.list_pkgs"].assert_called()
211            salt_mock["pkg.list_repos"].assert_called()
212            salt_mock["pkg.install"].assert_called_once()
213            fopen.assert_called()
214            load.assert_called()
215
216    @patch("salt.utils.json.load")
217    @patch("salt.modules.freezer.fopen")
218    @patch("salt.modules.freezer.status")
219    def test_restore_remove_extra_package(self, status, fopen, load):
220        """
221        Test to restore an old state
222        """
223        # Only an extra package is removed
224        status.return_value = True
225        load.side_effect = ({}, {})
226        salt_mock = {
227            "pkg.list_pkgs": MagicMock(return_value={"extra-package": {}}),
228            "pkg.list_repos": MagicMock(return_value={}),
229            "pkg.remove": MagicMock(),
230        }
231        with patch.dict(freezer.__salt__, salt_mock):
232            self.assertEqual(
233                freezer.restore(),
234                {
235                    "pkgs": {"add": [], "remove": ["extra-package"]},
236                    "repos": {"add": [], "remove": []},
237                    "comment": [],
238                },
239            )
240            salt_mock["pkg.list_pkgs"].assert_called()
241            salt_mock["pkg.list_repos"].assert_called()
242            salt_mock["pkg.remove"].assert_called_once()
243            fopen.assert_called()
244            load.assert_called()
245
246    @patch("salt.utils.json.load")
247    @patch("salt.modules.freezer.fopen")
248    @patch("salt.modules.freezer.status")
249    def test_restore_remove_extra_repo(self, status, fopen, load):
250        """
251        Test to restore an old state
252        """
253        # Only an extra repository is removed
254        status.return_value = True
255        load.side_effect = ({}, {})
256        salt_mock = {
257            "pkg.list_pkgs": MagicMock(return_value={}),
258            "pkg.list_repos": MagicMock(return_value={"extra-repo": {}}),
259            "pkg.del_repo": MagicMock(),
260        }
261        with patch.dict(freezer.__salt__, salt_mock):
262            self.assertEqual(
263                freezer.restore(),
264                {
265                    "pkgs": {"add": [], "remove": []},
266                    "repos": {"add": [], "remove": ["extra-repo"]},
267                    "comment": [],
268                },
269            )
270            salt_mock["pkg.list_pkgs"].assert_called()
271            salt_mock["pkg.list_repos"].assert_called()
272            salt_mock["pkg.del_repo"].assert_called_once()
273            fopen.assert_called()
274            load.assert_called()
275
276    @patch("os.remove")
277    @patch("salt.utils.json.load")
278    @patch("salt.modules.freezer.fopen")
279    @patch("salt.modules.freezer.status")
280    def test_restore_clean_yml(self, status, fopen, load, remove):
281        """
282        Test to restore an old state
283        """
284        status.return_value = True
285        salt_mock = {
286            "pkg.list_pkgs": MagicMock(return_value={}),
287            "pkg.list_repos": MagicMock(return_value={}),
288            "pkg.install": MagicMock(),
289        }
290        with patch.dict(freezer.__salt__, salt_mock):
291            self.assertEqual(
292                freezer.restore(clean=True),
293                {
294                    "pkgs": {"add": [], "remove": []},
295                    "repos": {"add": [], "remove": []},
296                    "comment": [],
297                },
298            )
299            salt_mock["pkg.list_pkgs"].assert_called()
300            salt_mock["pkg.list_repos"].assert_called()
301            fopen.assert_called()
302            load.assert_called()
303            self.assertEqual(remove.call_count, 2)
304