2019-04-10 19:23:17 -07:00
|
|
|
"""
|
2019-04-14 14:56:09 -07:00
|
|
|
.. moduleauthor:: Eric Torres
|
2019-04-10 19:23:17 -07:00
|
|
|
|
|
|
|
Tests for the rbackup.struct.hierarchy module.
|
|
|
|
"""
|
2019-04-16 22:49:01 -07:00
|
|
|
import shutil
|
2019-03-28 12:09:09 -07:00
|
|
|
import unittest
|
2019-04-16 22:49:01 -07:00
|
|
|
from pathlib import Path
|
|
|
|
from unittest.mock import DEFAULT, patch
|
2019-03-28 12:09:09 -07:00
|
|
|
|
|
|
|
from hypothesis import given
|
2019-04-16 22:49:01 -07:00
|
|
|
from hypothesis.strategies import from_regex
|
2019-04-10 18:03:58 -07:00
|
|
|
|
2019-04-09 17:45:38 -07:00
|
|
|
from rbackup.struct.hierarchy import Hierarchy
|
2019-03-13 03:13:32 -07:00
|
|
|
|
2019-03-13 20:38:22 -07:00
|
|
|
# ========== Constants ==========
|
2019-04-10 18:00:47 -07:00
|
|
|
TESTING_PACKAGE = "rbackup.struct"
|
|
|
|
TESTING_MODULE = f"{TESTING_PACKAGE}.hierarchy"
|
2019-03-28 12:09:09 -07:00
|
|
|
|
|
|
|
|
|
|
|
# ========== Tests ==========
|
|
|
|
class TestHierarchyPaths(unittest.TestCase):
|
2019-04-16 22:49:01 -07:00
|
|
|
def setUp(self):
|
|
|
|
self.patched_path = patch.multiple(
|
|
|
|
Path, exists=DEFAULT, mkdir=DEFAULT, symlink_to=DEFAULT, touch=DEFAULT
|
|
|
|
)
|
|
|
|
|
|
|
|
self.mocked_path = self.patched_path.start()
|
|
|
|
|
2019-04-14 12:40:10 -07:00
|
|
|
def test_retrieves_correct_metadata_filename(self):
|
2019-04-16 22:49:01 -07:00
|
|
|
self.assertEqual(Hierarchy("/tmp/backup").metadata_path.name, ".metadata")
|
2019-04-14 12:40:10 -07:00
|
|
|
|
2019-04-12 11:12:19 -07:00
|
|
|
@given(from_regex(r"[\w/._-]+", fullmatch=True))
|
|
|
|
def test_returns_absolute_path(self, dest):
|
2019-04-16 22:49:01 -07:00
|
|
|
try:
|
|
|
|
self.assertTrue(Hierarchy(dest).path.is_absolute())
|
|
|
|
except PermissionError:
|
|
|
|
pass
|
2019-03-28 12:09:09 -07:00
|
|
|
|
2019-04-10 18:17:21 -07:00
|
|
|
def test_raises_notimplemented_error(self):
|
2019-04-16 22:49:01 -07:00
|
|
|
h = Hierarchy("/tmp/backup")
|
2019-04-10 18:17:21 -07:00
|
|
|
with self.assertRaises(NotImplementedError):
|
2019-04-17 10:02:51 -07:00
|
|
|
h._gen_metadata()
|
2019-04-10 18:17:21 -07:00
|
|
|
|
2019-04-16 22:49:01 -07:00
|
|
|
def tearDown(self):
|
|
|
|
self.patched_path.stop()
|
2019-04-10 18:17:21 -07:00
|
|
|
|
|
|
|
|
2019-04-16 22:49:01 -07:00
|
|
|
class TestHierarchyMetadata(unittest.TestCase):
|
|
|
|
"""Only meant to check that data written is the same data that is read."""
|
2019-04-14 12:40:10 -07:00
|
|
|
|
2019-04-16 22:49:01 -07:00
|
|
|
def test_write_metadata(self):
|
|
|
|
data = ["test", "data"]
|
|
|
|
h = Hierarchy("/tmp/backup")
|
|
|
|
h.metadata_path.touch()
|
2019-04-10 18:17:21 -07:00
|
|
|
h.write_metadata(data)
|
|
|
|
|
2019-04-16 22:49:01 -07:00
|
|
|
self.assertEqual(data, h.read_metadata())
|
2019-04-10 18:17:21 -07:00
|
|
|
|
2019-04-16 22:49:01 -07:00
|
|
|
shutil.rmtree(h)
|
2019-04-17 10:02:51 -07:00
|
|
|
|
|
|
|
|
|
|
|
@unittest.skip("Fix call checks")
|
|
|
|
class TestHierarchyCleanup(unittest.TestCase):
|
|
|
|
"""Test that hierarchy cleanup works properly.
|
|
|
|
|
|
|
|
Test cases
|
|
|
|
----------
|
|
|
|
* Function stops if system is not symlink attack-resistant
|
|
|
|
* If symlink attack-resistant, then only delete metadata when all others false
|
|
|
|
* Function only deletes snapshots when told to
|
|
|
|
* Function only deletes repository directory when told to
|
|
|
|
"""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.patched_path = patch.multiple(
|
|
|
|
Path,
|
|
|
|
exists=DEFAULT,
|
|
|
|
mkdir=DEFAULT,
|
|
|
|
symlink_to=DEFAULT,
|
|
|
|
touch=DEFAULT,
|
|
|
|
unlink=DEFAULT,
|
|
|
|
)
|
|
|
|
self.patched_metadata = patch.multiple(
|
|
|
|
Hierarchy, read_metadata=DEFAULT, write_metadata=DEFAULT
|
|
|
|
)
|
|
|
|
self.patched_shutil = patch.multiple(f"{TESTING_MODULE}.shutil", rmtree=DEFAULT)
|
|
|
|
|
|
|
|
self.mocked_path = self.patched_path.start()
|
|
|
|
self.mocked_metadata = self.patched_metadata.start()
|
|
|
|
self.mocked_shutil = self.patched_shutil.start()
|
|
|
|
|
|
|
|
self.mocked_shutil["rmtree"].avoids_symlink_attacks = True
|
|
|
|
|
|
|
|
def test_stops_on_non_symlink_resistant(self):
|
|
|
|
self.mocked_shutil["rmtree"].avoids_symlink_attacks = True
|
|
|
|
h = Hierarchy("/tmp/backup")
|
|
|
|
|
|
|
|
h.cleanup(remove_snapshots=True)
|
|
|
|
|
|
|
|
self.mocked_path["unlink"].assert_not_called()
|
|
|
|
self.mocked_shutil["rmtree"].assert_not_called()
|
|
|
|
|
|
|
|
def test_removes_metadata_by_default(self):
|
|
|
|
h = Hierarchy("/tmp/backup")
|
|
|
|
|
|
|
|
h.cleanup()
|
|
|
|
|
|
|
|
self.mocked_path["unlink"].assert_called_once()
|
|
|
|
|
|
|
|
def test_removes_snapshots(self):
|
|
|
|
h = Hierarchy("/tmp/backup")
|
|
|
|
|
|
|
|
h.cleanup(remove_snapshots=True)
|
|
|
|
|
|
|
|
self.mocked_shutil.rmtree.assert_called_once()
|
|
|
|
|
|
|
|
def test_removes_repo_dir(self):
|
|
|
|
h = Hierarchy("/tmp/backup")
|
|
|
|
|
|
|
|
h.cleanup(remove_repo_dir=True)
|
|
|
|
|
|
|
|
self.mocked_shutil.rmtree.assert_called_once()
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.patched_metadata.stop()
|
|
|
|
self.patched_path.stop()
|
|
|
|
self.patched_shutil.stop()
|