浏览代码

Add tests for new code.

Dan Helfman 4 天之前
父节点
当前提交
627898cc23
共有 3 个文件被更改,包括 68 次插入5 次删除
  1. 2 2
      borgmatic/actions/restore.py
  2. 3 3
      borgmatic/hooks/data_source/dump.py
  3. 63 0
      tests/unit/hooks/data_source/test_dump.py

+ 2 - 2
borgmatic/actions/restore.py

@@ -305,13 +305,13 @@ def collect_dumps_from_archive(
 
     # If we've successfully loaded any dumps metadata, we're done.
     if dumps_from_archive:
-        logger.debug(f'Collecting database dumps from archive data source dumps metadata files')
+        logger.debug('Collecting database dumps from archive data source dumps metadata files')
 
         return dumps_from_archive
 
     # No dumps metadata files were found, so for backwards compatibility, fall back to parsing the
     # paths of dumps found in the archive to get their respective dump metadata.
-    logger.debug(f'Collecting database dumps from archive data source dump paths (fallback)')
+    logger.debug('Collecting database dumps from archive data source dump paths (fallback)')
     borgmatic_source_directory = str(
         pathlib.Path(borgmatic.config.paths.get_borgmatic_source_directory(config)),
     )

+ 3 - 3
borgmatic/hooks/data_source/dump.py

@@ -48,7 +48,7 @@ def write_data_source_dumps_metadata(borgmatic_runtime_directory, hook_name, dum
     dumps_metadata_path = os.path.join(borgmatic_runtime_directory, hook_name, 'dumps.json')
 
     try:
-        with open(dumps_metadata_path, 'w') as metadata_file:
+        with open(dumps_metadata_path, 'w', encoding='utf-8') as metadata_file:
             json.dump([dump._asdict() for dump in dumps_metadata], metadata_file, sort_keys=True)
     except OSError as error:
         raise ValueError(f'Error writing to dumps metadata at {dumps_metadata_path}: {error}')
@@ -56,8 +56,8 @@ def write_data_source_dumps_metadata(borgmatic_runtime_directory, hook_name, dum
 
 def parse_data_source_dumps_metadata(dumps_json, dumps_metadata_path):
     '''
-    Given a dumps metadata JSON string as extracted from an archive, parse it into a tuple of
-    borgmatic.actions.restore.Dump instances and return them.
+    Given a dumps metadata JSON string as extracted from an archive and its path within the archive,
+    parse it into a tuple of borgmatic.actions.restore.Dump instances and return them.
 
     Raise ValueError if parsing the JSON results in a JSON decode error or the data does not have
     the expected keys.

+ 63 - 0
tests/unit/hooks/data_source/test_dump.py

@@ -1,3 +1,6 @@
+import io
+import sys
+
 import pytest
 from flexmock import flexmock
 
@@ -31,6 +34,66 @@ def test_make_data_source_dump_filename_with_invalid_name_raises():
         module.make_data_source_dump_filename('databases', 'invalid/name')
 
 
+def test_write_data_source_dumps_metadata_writes_json_to_file():
+    dumps_metadata = [
+        module.borgmatic.actions.restore.Dump('databases', 'foo'),
+        module.borgmatic.actions.restore.Dump('databases', 'bar'),
+    ]
+    dumps_stream = io.StringIO('password')
+    dumps_stream.name = '/run/borgmatic/databases/dumps.json'
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_receive('open').with_args(dumps_stream.name, 'w', encoding='utf-8').and_return(
+        dumps_stream
+    )
+    flexmock(dumps_stream).should_receive('close')  # Prevent close() so getvalue() below works.
+
+    module.write_data_source_dumps_metadata('/run/borgmatic', 'databases', dumps_metadata)
+
+    assert (
+        dumps_stream.getvalue()
+        == '[{"data_source_name": "foo", "hook_name": "databases", "hostname": "localhost", "port": null}, {"data_source_name": "bar", "hook_name": "databases", "hostname": "localhost", "port": null}]'
+    )
+
+
+def test_write_data_source_dumps_metadata_with_operating_system_error_raises():
+    dumps_metadata = [
+        module.borgmatic.actions.restore.Dump('databases', 'foo'),
+        module.borgmatic.actions.restore.Dump('databases', 'bar'),
+    ]
+    dumps_stream = io.StringIO('password')
+    dumps_stream.name = '/run/borgmatic/databases/dumps.json'
+    builtins = flexmock(sys.modules['builtins'])
+    builtins.should_receive('open').with_args(dumps_stream.name, 'w', encoding='utf-8').and_raise(
+        OSError
+    )
+
+    with pytest.raises(ValueError):
+        module.write_data_source_dumps_metadata('/run/borgmatic', 'databases', dumps_metadata)
+
+
+def test_parse_data_source_dumps_metadata_converts_json_to_dump_instances():
+    dumps_json = '[{"data_source_name": "foo", "hook_name": "databases", "hostname": "localhost", "port": null}, {"data_source_name": "bar", "hook_name": "databases", "hostname": "example.org", "port": 1234}]'
+
+    assert module.parse_data_source_dumps_metadata(
+        dumps_json, 'borgmatic/databases/dumps.json'
+    ) == (
+        module.borgmatic.actions.restore.Dump('databases', 'foo'),
+        module.borgmatic.actions.restore.Dump('databases', 'bar', 'example.org', 1234),
+    )
+
+
+def test_parse_data_source_dumps_metadata_with_invalid_json_raises():
+    with pytest.raises(ValueError):
+        module.parse_data_source_dumps_metadata('[{', 'borgmatic/databases/dumps.json')
+
+
+def test_parse_data_source_dumps_metadata_with_unknown_keys_raises():
+    dumps_json = '[{"data_source_name": "foo", "hook_name": "databases", "wtf": "is this"}]'
+
+    with pytest.raises(ValueError):
+        module.parse_data_source_dumps_metadata(dumps_json, 'borgmatic/databases/dumps.json')
+
+
 def test_create_parent_directory_for_dump_does_not_raise():
     flexmock(module.os).should_receive('makedirs')