test_snapper.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. # -*- coding: utf-8 -*-
  2. """
  3. Unit tests for the Snapper module
  4. :codeauthor: Duncan Mac-Vicar P. <dmacvicar@suse.de>
  5. :codeauthor: Pablo Suárez Hernández <psuarezhernandez@suse.de>
  6. """
  7. # Import Python libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import sys
  10. import salt.modules.snapper as snapper
  11. from salt.exceptions import CommandExecutionError
  12. # Import Salt libs
  13. from salt.ext import six
  14. # Import Salt Testing libs
  15. from tests.support.mixins import LoaderModuleMockMixin
  16. from tests.support.mock import MagicMock, mock_open, patch
  17. from tests.support.unit import TestCase, skipIf
  18. DBUS_RET = {
  19. "ListSnapshots": [
  20. [
  21. 42,
  22. 1,
  23. 0,
  24. 1457006571,
  25. 0,
  26. "Some description",
  27. "",
  28. {"userdata1": "userval1", "salt_jid": "20160607130930720112"},
  29. ],
  30. [
  31. 43,
  32. 2,
  33. 42,
  34. 1457006572,
  35. 0,
  36. "Blah Blah",
  37. "",
  38. {"userdata2": "userval2", "salt_jid": "20160607130930720112"},
  39. ],
  40. ],
  41. "ListConfigs": [
  42. [
  43. "root",
  44. "/",
  45. {
  46. "SUBVOLUME": "/",
  47. "NUMBER_MIN_AGE": "1800",
  48. "TIMELINE_LIMIT_YEARLY": "4-10",
  49. "NUMBER_LIMIT_IMPORTANT": "10",
  50. "FSTYPE": "btrfs",
  51. "TIMELINE_LIMIT_MONTHLY": "4-10",
  52. "ALLOW_GROUPS": "",
  53. "EMPTY_PRE_POST_MIN_AGE": "1800",
  54. "EMPTY_PRE_POST_CLEANUP": "yes",
  55. "BACKGROUND_COMPARISON": "yes",
  56. "TIMELINE_LIMIT_HOURLY": "4-10",
  57. "ALLOW_USERS": "",
  58. "TIMELINE_LIMIT_WEEKLY": "0",
  59. "TIMELINE_CREATE": "no",
  60. "NUMBER_CLEANUP": "yes",
  61. "TIMELINE_CLEANUP": "yes",
  62. "SPACE_LIMIT": "0.5",
  63. "NUMBER_LIMIT": "10",
  64. "TIMELINE_MIN_AGE": "1800",
  65. "TIMELINE_LIMIT_DAILY": "4-10",
  66. "SYNC_ACL": "no",
  67. "QGROUP": "1/0",
  68. },
  69. ]
  70. ],
  71. "GetFiles": [
  72. ["/root/.viminfo", 8],
  73. ["/tmp/foo", 52],
  74. ["/tmp/foo2", 1],
  75. ["/tmp/foo3", 2],
  76. ["/var/log/snapper.log", 8],
  77. ["/var/cache/salt/minion/extmods/modules/snapper.py", 8],
  78. ["/var/cache/salt/minion/extmods/modules/snapper.pyc", 8],
  79. ],
  80. }
  81. FILE_CONTENT = {
  82. "/tmp/foo": {"pre": "dummy text", "post": "another foobar"},
  83. "/tmp/foo2": {"post": "another foobar"},
  84. }
  85. MODULE_RET = {
  86. "SNAPSHOTS": [
  87. {
  88. "userdata": {"userdata1": "userval1", "salt_jid": "20160607130930720112"},
  89. "description": "Some description",
  90. "timestamp": 1457006571,
  91. "cleanup": "",
  92. "user": "root",
  93. "type": "pre",
  94. "id": 42,
  95. },
  96. {
  97. "pre": 42,
  98. "userdata": {"userdata2": "userval2", "salt_jid": "20160607130930720112"},
  99. "description": "Blah Blah",
  100. "timestamp": 1457006572,
  101. "cleanup": "",
  102. "user": "root",
  103. "type": "post",
  104. "id": 43,
  105. },
  106. ],
  107. "LISTCONFIGS": {
  108. "root": {
  109. "SUBVOLUME": "/",
  110. "NUMBER_MIN_AGE": "1800",
  111. "TIMELINE_LIMIT_YEARLY": "4-10",
  112. "NUMBER_LIMIT_IMPORTANT": "10",
  113. "FSTYPE": "btrfs",
  114. "TIMELINE_LIMIT_MONTHLY": "4-10",
  115. "ALLOW_GROUPS": "",
  116. "EMPTY_PRE_POST_MIN_AGE": "1800",
  117. "EMPTY_PRE_POST_CLEANUP": "yes",
  118. "BACKGROUND_COMPARISON": "yes",
  119. "TIMELINE_LIMIT_HOURLY": "4-10",
  120. "ALLOW_USERS": "",
  121. "TIMELINE_LIMIT_WEEKLY": "0",
  122. "TIMELINE_CREATE": "no",
  123. "NUMBER_CLEANUP": "yes",
  124. "TIMELINE_CLEANUP": "yes",
  125. "SPACE_LIMIT": "0.5",
  126. "NUMBER_LIMIT": "10",
  127. "TIMELINE_MIN_AGE": "1800",
  128. "TIMELINE_LIMIT_DAILY": "4-10",
  129. "SYNC_ACL": "no",
  130. "QGROUP": "1/0",
  131. }
  132. },
  133. "GETFILES": {
  134. "/root/.viminfo": {"status": ["modified"]},
  135. "/tmp/foo": {"status": ["type changed", "permission changed", "owner changed"]},
  136. "/tmp/foo2": {"status": ["created"]},
  137. "/tmp/foo3": {"status": ["deleted"]},
  138. "/var/log/snapper.log": {"status": ["modified"]},
  139. "/var/cache/salt/minion/extmods/modules/snapper.py": {"status": ["modified"]},
  140. "/var/cache/salt/minion/extmods/modules/snapper.pyc": {"status": ["modified"]},
  141. },
  142. "DIFF": {
  143. "/tmp/foo": {
  144. "comment": "text file changed",
  145. "diff": "--- /.snapshots/55/snapshot/tmp/foo\n"
  146. "+++ /tmp/foo\n"
  147. "@@ -1 +1 @@\n"
  148. "-dummy text"
  149. "+another foobar",
  150. },
  151. "/tmp/foo2": {
  152. "comment": "text file created",
  153. "diff": "--- /.snapshots/55/snapshot/tmp/foo2\n"
  154. "+++ /tmp/foo2\n"
  155. "@@ -0,0 +1 @@\n"
  156. "+another foobar",
  157. },
  158. "/tmp/foo26": {
  159. "comment": "text file created",
  160. "diff": "--- /.snapshots/55/snapshot/tmp/foo2 \n"
  161. "+++ /tmp/foo2 \n"
  162. "@@ -1,0 +1,1 @@\n"
  163. "+another foobar",
  164. },
  165. "/tmp/foo3": {
  166. "comment": "binary file changed",
  167. "old_sha256_digest": "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b",
  168. "new_sha256_digest": "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa",
  169. },
  170. },
  171. }
  172. @skipIf(sys.platform.startswith("win"), "Snapper not available on Windows")
  173. class SnapperTestCase(TestCase, LoaderModuleMockMixin):
  174. def setup_loader_modules(self):
  175. class DBusException(BaseException):
  176. get_dbus_name = "foo"
  177. dbus_mock = MagicMock()
  178. dbus_mock.configure_mock(DBusException=DBusException)
  179. return {snapper: {"dbus": dbus_mock, "snapper": MagicMock()}}
  180. def test__snapshot_to_data(self):
  181. data = snapper._snapshot_to_data(
  182. DBUS_RET["ListSnapshots"][0]
  183. ) # pylint: disable=protected-access
  184. self.assertEqual(data["id"], 42)
  185. self.assertNotIn("pre", data)
  186. self.assertEqual(data["type"], "pre")
  187. self.assertEqual(data["user"], "root")
  188. self.assertEqual(data["timestamp"], 1457006571)
  189. self.assertEqual(data["description"], "Some description")
  190. self.assertEqual(data["cleanup"], "")
  191. self.assertEqual(data["userdata"]["userdata1"], "userval1")
  192. def test_list_snapshots(self):
  193. with patch(
  194. "salt.modules.snapper.snapper.ListSnapshots",
  195. MagicMock(return_value=DBUS_RET["ListSnapshots"]),
  196. ):
  197. self.assertEqual(snapper.list_snapshots(), MODULE_RET["SNAPSHOTS"])
  198. def test_get_snapshot(self):
  199. with patch(
  200. "salt.modules.snapper.snapper.GetSnapshot",
  201. MagicMock(return_value=DBUS_RET["ListSnapshots"][0]),
  202. ):
  203. self.assertEqual(snapper.get_snapshot(), MODULE_RET["SNAPSHOTS"][0])
  204. self.assertEqual(
  205. snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][0]
  206. )
  207. self.assertNotEqual(
  208. snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][1]
  209. )
  210. def test_list_configs(self):
  211. with patch(
  212. "salt.modules.snapper.snapper.ListConfigs",
  213. MagicMock(return_value=DBUS_RET["ListConfigs"]),
  214. ):
  215. self.assertEqual(snapper.list_configs(), MODULE_RET["LISTCONFIGS"])
  216. def test_get_config(self):
  217. with patch(
  218. "salt.modules.snapper.snapper.GetConfig",
  219. MagicMock(return_value=DBUS_RET["ListConfigs"][0]),
  220. ):
  221. self.assertEqual(snapper.get_config(), DBUS_RET["ListConfigs"][0])
  222. def test_set_config(self):
  223. with patch("salt.modules.snapper.snapper.SetConfig", MagicMock()):
  224. opts = {"sync_acl": True, "dummy": False, "foobar": 1234}
  225. self.assertEqual(snapper.set_config(opts), True)
  226. def test_status_to_string(self):
  227. self.assertEqual(snapper.status_to_string(1), ["created"])
  228. self.assertEqual(snapper.status_to_string(2), ["deleted"])
  229. self.assertEqual(snapper.status_to_string(4), ["type changed"])
  230. self.assertEqual(snapper.status_to_string(8), ["modified"])
  231. self.assertEqual(snapper.status_to_string(16), ["permission changed"])
  232. self.assertListEqual(
  233. snapper.status_to_string(24), ["modified", "permission changed"]
  234. )
  235. self.assertEqual(snapper.status_to_string(32), ["owner changed"])
  236. self.assertEqual(snapper.status_to_string(64), ["group changed"])
  237. self.assertListEqual(
  238. snapper.status_to_string(97), ["created", "owner changed", "group changed"]
  239. )
  240. self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"])
  241. self.assertEqual(snapper.status_to_string(256), ["ACL info changed"])
  242. def test_create_config(self):
  243. with patch("salt.modules.snapper.snapper.CreateConfig", MagicMock()), patch(
  244. "salt.modules.snapper.snapper.GetConfig",
  245. MagicMock(return_value=DBUS_RET["ListConfigs"][0]),
  246. ):
  247. opts = {
  248. "name": "testconfig",
  249. "subvolume": "/foo/bar/",
  250. "fstype": "btrfs",
  251. "template": "mytemplate",
  252. "extra_opts": {"NUMBER_CLEANUP": False},
  253. }
  254. with patch(
  255. "salt.modules.snapper.set_config", MagicMock()
  256. ) as set_config_mock:
  257. self.assertEqual(
  258. snapper.create_config(**opts), DBUS_RET["ListConfigs"][0]
  259. )
  260. set_config_mock.assert_called_with("testconfig", **opts["extra_opts"])
  261. with patch(
  262. "salt.modules.snapper.set_config", MagicMock()
  263. ) as set_config_mock:
  264. del opts["extra_opts"]
  265. self.assertEqual(
  266. snapper.create_config(**opts), DBUS_RET["ListConfigs"][0]
  267. )
  268. assert not set_config_mock.called
  269. self.assertRaises(CommandExecutionError, snapper.create_config)
  270. def test_create_snapshot(self):
  271. with patch(
  272. "salt.modules.snapper.snapper.CreateSingleSnapshot",
  273. MagicMock(return_value=1234),
  274. ), patch(
  275. "salt.modules.snapper.snapper.CreatePreSnapshot",
  276. MagicMock(return_value=1234),
  277. ), patch(
  278. "salt.modules.snapper.snapper.CreatePostSnapshot",
  279. MagicMock(return_value=1234),
  280. ):
  281. for snapshot_type in ["pre", "post", "single"]:
  282. opts = {
  283. "__pub_jid": 20160607130930720112,
  284. "type": snapshot_type,
  285. "description": "Test description",
  286. "cleanup_algorithm": "number",
  287. "pre_number": 23,
  288. }
  289. self.assertEqual(snapper.create_snapshot(**opts), 1234)
  290. def test_delete_snapshot_id_success(self):
  291. with patch("salt.modules.snapper.snapper.DeleteSnapshots", MagicMock()), patch(
  292. "salt.modules.snapper.snapper.ListSnapshots",
  293. MagicMock(return_value=DBUS_RET["ListSnapshots"]),
  294. ):
  295. self.assertEqual(
  296. snapper.delete_snapshot(snapshots_ids=43),
  297. {"root": {"ids": [43], "status": "deleted"}},
  298. )
  299. self.assertEqual(
  300. snapper.delete_snapshot(snapshots_ids=[42, 43]),
  301. {"root": {"ids": [42, 43], "status": "deleted"}},
  302. )
  303. def test_delete_snapshot_id_fail(self):
  304. with patch("salt.modules.snapper.snapper.DeleteSnapshots", MagicMock()), patch(
  305. "salt.modules.snapper.snapper.ListSnapshots",
  306. MagicMock(return_value=DBUS_RET["ListSnapshots"]),
  307. ):
  308. self.assertRaises(CommandExecutionError, snapper.delete_snapshot)
  309. self.assertRaises(
  310. CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1
  311. )
  312. self.assertRaises(
  313. CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]
  314. )
  315. def test_modify_snapshot(self):
  316. with patch("salt.modules.snapper.snapper.SetSnapshot", MagicMock()):
  317. _ret = {
  318. "userdata": {"userdata2": "uservalue2"},
  319. "description": "UPDATED DESCRIPTION",
  320. "timestamp": 1457006571,
  321. "cleanup": "number",
  322. "user": "root",
  323. "type": "pre",
  324. "id": 42,
  325. }
  326. _opts = {
  327. "config": "root",
  328. "snapshot_id": 42,
  329. "cleanup": "number",
  330. "description": "UPDATED DESCRIPTION",
  331. "userdata": {"userdata2": "uservalue2"},
  332. }
  333. with patch(
  334. "salt.modules.snapper.get_snapshot",
  335. MagicMock(side_effect=[DBUS_RET["ListSnapshots"][0], _ret]),
  336. ):
  337. self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret)
  338. def test__get_num_interval(self):
  339. with patch(
  340. "salt.modules.snapper._get_last_snapshot",
  341. MagicMock(return_value={"id": 42}),
  342. ):
  343. self.assertEqual(
  344. snapper._get_num_interval(config=None, num_pre=None, num_post=None),
  345. (42, 0),
  346. ) # pylint: disable=protected-access
  347. self.assertEqual(
  348. snapper._get_num_interval(config=None, num_pre=None, num_post=50),
  349. (42, 50),
  350. ) # pylint: disable=protected-access
  351. self.assertEqual(
  352. snapper._get_num_interval(config=None, num_pre=42, num_post=50),
  353. (42, 50),
  354. ) # pylint: disable=protected-access
  355. def test_run(self):
  356. patch_dict = {
  357. "snapper.create_snapshot": MagicMock(return_value=43),
  358. "test.ping": MagicMock(return_value=True),
  359. }
  360. with patch.dict(snapper.__salt__, patch_dict):
  361. self.assertEqual(snapper.run("test.ping"), True)
  362. self.assertRaises(CommandExecutionError, snapper.run, "unknown.func")
  363. def test_status(self):
  364. with patch(
  365. "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43))
  366. ), patch("salt.modules.snapper.snapper.GetComparison", MagicMock()), patch(
  367. "salt.modules.snapper.snapper.GetFiles",
  368. MagicMock(return_value=DBUS_RET["GetFiles"]),
  369. ), patch(
  370. "salt.modules.snapper.snapper.ListConfigs",
  371. MagicMock(return_value=DBUS_RET["ListConfigs"]),
  372. ):
  373. if six.PY3:
  374. self.assertCountEqual(snapper.status(), MODULE_RET["GETFILES"])
  375. self.assertCountEqual(
  376. snapper.status(num_pre="42", num_post=43), MODULE_RET["GETFILES"]
  377. )
  378. self.assertCountEqual(
  379. snapper.status(num_pre=42), MODULE_RET["GETFILES"]
  380. )
  381. self.assertCountEqual(
  382. snapper.status(num_post=43), MODULE_RET["GETFILES"]
  383. )
  384. else:
  385. self.assertItemsEqual(snapper.status(), MODULE_RET["GETFILES"])
  386. self.assertItemsEqual(
  387. snapper.status(num_pre="42", num_post=43), MODULE_RET["GETFILES"]
  388. )
  389. self.assertItemsEqual(
  390. snapper.status(num_pre=42), MODULE_RET["GETFILES"]
  391. )
  392. self.assertItemsEqual(
  393. snapper.status(num_post=43), MODULE_RET["GETFILES"]
  394. )
  395. def test_changed_files(self):
  396. with patch(
  397. "salt.modules.snapper.status",
  398. MagicMock(return_value=MODULE_RET["GETFILES"]),
  399. ):
  400. self.assertEqual(snapper.changed_files(), MODULE_RET["GETFILES"].keys())
  401. def test_undo(self):
  402. with patch(
  403. "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43))
  404. ), patch(
  405. "salt.modules.snapper.status",
  406. MagicMock(return_value=MODULE_RET["GETFILES"]),
  407. ):
  408. cmd_ret = "create:0 modify:1 delete:0"
  409. with patch.dict(
  410. snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)}
  411. ):
  412. module_ret = {"create": "0", "delete": "0", "modify": "1"}
  413. self.assertEqual(snapper.undo(files=["/tmp/foo"]), module_ret)
  414. cmd_ret = "create:1 modify:1 delete:0"
  415. with patch.dict(
  416. snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)}
  417. ):
  418. module_ret = {"create": "1", "delete": "0", "modify": "1"}
  419. self.assertEqual(
  420. snapper.undo(files=["/tmp/foo", "/tmp/foo2"]), module_ret
  421. )
  422. cmd_ret = "create:1 modify:1 delete:1"
  423. with patch.dict(
  424. snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)}
  425. ):
  426. module_ret = {"create": "1", "delete": "1", "modify": "1"}
  427. self.assertEqual(
  428. snapper.undo(files=["/tmp/foo", "/tmp/foo2", "/tmp/foo3"]),
  429. module_ret,
  430. )
  431. def test__get_jid_snapshots(self):
  432. with patch(
  433. "salt.modules.snapper.list_snapshots",
  434. MagicMock(return_value=MODULE_RET["SNAPSHOTS"]),
  435. ):
  436. self.assertEqual(
  437. snapper._get_jid_snapshots(
  438. "20160607130930720112"
  439. ), # pylint: disable=protected-access
  440. (MODULE_RET["SNAPSHOTS"][0]["id"], MODULE_RET["SNAPSHOTS"][1]["id"]),
  441. )
  442. def test_undo_jid(self):
  443. with patch(
  444. "salt.modules.snapper._get_jid_snapshots", MagicMock(return_value=(42, 43))
  445. ), patch(
  446. "salt.modules.snapper.undo",
  447. MagicMock(return_value="create:1 modify:1 delete:1"),
  448. ):
  449. self.assertEqual(
  450. snapper.undo_jid(20160607130930720112), "create:1 modify:1 delete:1"
  451. )
  452. def test_diff_text_file(self):
  453. with patch(
  454. "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43))
  455. ), patch(
  456. "salt.modules.snapper.snapper.MountSnapshot",
  457. MagicMock(side_effect=["/.snapshots/55/snapshot", ""]),
  458. ), patch(
  459. "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="")
  460. ), patch(
  461. "os.path.isdir", MagicMock(return_value=False)
  462. ), patch(
  463. "salt.modules.snapper.changed_files", MagicMock(return_value=["/tmp/foo2"])
  464. ), patch(
  465. "salt.modules.snapper._is_text_file", MagicMock(return_value=True)
  466. ), patch(
  467. "os.path.isfile", MagicMock(side_effect=[False, True])
  468. ), patch(
  469. "salt.utils.files.fopen",
  470. mock_open(read_data=FILE_CONTENT["/tmp/foo2"]["post"]),
  471. ), patch(
  472. "salt.modules.snapper.snapper.ListConfigs",
  473. MagicMock(return_value=DBUS_RET["ListConfigs"]),
  474. ):
  475. if sys.version_info < (2, 7):
  476. self.assertEqual(
  477. snapper.diff(), {"/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo26"]}
  478. )
  479. else:
  480. self.assertEqual(
  481. snapper.diff(), {"/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo2"]}
  482. )
  483. @skipIf(sys.version_info < (2, 7), "Python 2.7 required to compare diff properly")
  484. def test_diff_text_files(self):
  485. with patch(
  486. "salt.modules.snapper._get_num_interval", MagicMock(return_value=(55, 0))
  487. ), patch(
  488. "salt.modules.snapper.snapper.MountSnapshot",
  489. MagicMock(
  490. side_effect=[
  491. "/.snapshots/55/snapshot",
  492. "",
  493. "/.snapshots/55/snapshot",
  494. "",
  495. ]
  496. ),
  497. ), patch(
  498. "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="")
  499. ), patch(
  500. "salt.modules.snapper.changed_files",
  501. MagicMock(return_value=["/tmp/foo", "/tmp/foo2"]),
  502. ), patch(
  503. "salt.modules.snapper._is_text_file", MagicMock(return_value=True)
  504. ), patch(
  505. "os.path.isfile", MagicMock(side_effect=[True, True, False, True])
  506. ), patch(
  507. "os.path.isdir", MagicMock(return_value=False)
  508. ), patch(
  509. "salt.modules.snapper.snapper.ListConfigs",
  510. MagicMock(return_value=DBUS_RET["ListConfigs"]),
  511. ):
  512. contents = {
  513. "*/tmp/foo": [
  514. FILE_CONTENT["/tmp/foo"]["pre"],
  515. FILE_CONTENT["/tmp/foo"]["post"],
  516. ],
  517. "*/tmp/foo2": FILE_CONTENT["/tmp/foo2"]["post"],
  518. }
  519. with patch("salt.utils.files.fopen", mock_open(read_data=contents)):
  520. module_ret = {
  521. "/tmp/foo": MODULE_RET["DIFF"]["/tmp/foo"],
  522. "/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo2"],
  523. }
  524. self.assertEqual(snapper.diff(), module_ret)
  525. def test_diff_binary_files(self):
  526. with patch(
  527. "salt.modules.snapper._get_num_interval", MagicMock(return_value=(55, 0))
  528. ), patch(
  529. "salt.modules.snapper.snapper.MountSnapshot",
  530. MagicMock(
  531. side_effect=[
  532. "/.snapshots/55/snapshot",
  533. "",
  534. "/.snapshots/55/snapshot",
  535. "",
  536. ]
  537. ),
  538. ), patch(
  539. "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="")
  540. ), patch(
  541. "salt.modules.snapper.changed_files", MagicMock(return_value=["/tmp/foo3"])
  542. ), patch(
  543. "salt.modules.snapper._is_text_file", MagicMock(return_value=False)
  544. ), patch(
  545. "os.path.isfile", MagicMock(side_effect=[True, True])
  546. ), patch(
  547. "os.path.isdir", MagicMock(return_value=False)
  548. ), patch(
  549. "salt.modules.snapper.snapper.ListConfigs",
  550. MagicMock(return_value=DBUS_RET["ListConfigs"]),
  551. ), patch.dict(
  552. snapper.__salt__,
  553. {
  554. "hashutil.sha256_digest": MagicMock(
  555. side_effect=[
  556. "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b",
  557. "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa",
  558. ]
  559. )
  560. },
  561. ):
  562. with patch("salt.utils.files.fopen", mock_open(read_data="dummy binary")):
  563. module_ret = {
  564. "/tmp/foo3": MODULE_RET["DIFF"]["/tmp/foo3"],
  565. }
  566. self.assertEqual(snapper.diff(), module_ret)