import importlib.util import json from pathlib import Path import subprocess import tempfile import unittest from unittest.mock import patch spec=importlib.util.spec_from_file_location('backup',Path(__file__).parents[1]/'scripts/backup.py') b=importlib.util.module_from_spec(spec); spec.loader.exec_module(b) class BackupTests(unittest.TestCase): def test_restores_original_file_and_removes_new_file(self): with tempfile.TemporaryDirectory() as d: base=Path(d); original=base/'old.conf'; new=base/'new.conf' original.write_text('original') def ctl(*args): return subprocess.CompletedProcess(args, 1, stdout='') with patch.object(b,'PATHS',[str(original),str(new)]), patch.object(b,'ctl',side_effect=ctl): b.snapshot(base/'backup') original.write_text('changed'); new.write_text('new') b.restore(base/'backup') self.assertEqual(original.read_text(),'original') self.assertFalse(new.exists()) def test_preserves_symlinks(self): with tempfile.TemporaryDirectory() as d: base=Path(d); link=base/'service'; original=base/'target' original.write_text('original'); link.symlink_to(original) def ctl(*args): return subprocess.CompletedProcess(args, 1, stdout='') with patch.object(b,'PATHS',[str(link)]), patch.object(b,'ctl',side_effect=ctl): b.snapshot(base/'backup') link.unlink(); link.write_text('replaced') b.restore(base/'backup') self.assertTrue(link.is_symlink()) self.assertEqual(link.read_text(),'original') def test_rejects_snapshot_with_unexpected_file(self): with tempfile.TemporaryDirectory() as d: base=Path(d) (base/'manifest.json').write_text(json.dumps({'files':{'/unexpected':True},'units':{}})) with patch.object(b,'ctl') as ctl, self.assertRaises(ValueError): b.restore(base) ctl.assert_not_called() if __name__=='__main__': unittest.main()