perf(delivery): streamline complete recovery inventories

This commit is contained in:
2026-10-28 09:51:18 +08:00
parent db834dfc73
commit 9f4a8c9a68
6 changed files with 114 additions and 16 deletions
+45
View File
@@ -302,3 +302,48 @@ def test_delayed_filesystem_write_error_is_not_success(tmp_path, monkeypatch):
monkeypatch.setattr(backup_objects.ctypes, "CDLL", lambda *_, **__: Library())
with pytest.raises(policy.DeliveryError, match="filesystem synchronization failed"):
backup_objects.sync_filesystem(tmp_path, time.monotonic() + 10)
@pytest.mark.parametrize("missing", ["releases/older", "releases/older/nested"])
def test_every_directory_ancestor_is_required_even_when_file_parent_exists(snapshot, missing):
value = object_format(snapshot)
value["directories"] = value["directories"] + [
{"path": path, "uid": 0, "gid": 0, "mode": 0o700}
for path in ("releases/older", "releases/older/nested")
if path != missing
]
value["files"] = [
{**row, "path": "releases/older/nested/file"} if row["path"] == "releases/first" else row
for row in value["files"]
]
with pytest.raises(policy.DeliveryError, match="private backup parent missing"):
backup_manifest.validate(value)
def test_v4_complete_version_inventory_keeps_an_explicit_logical_entry_bound(snapshot):
value = object_format(snapshot)
count = backup_manifest.MAX_LOGICAL_ENTRIES - len(value["directories"])
template = {**value["files"][0], "bytes": 1, "sha256": hashlib.sha256(b"x").hexdigest()}
value["files"] = [{**template, "path": row["path"]} for row in value["files"][:2]] + [
{**template, "path": "releases/item-" + str(i)} for i in range(count - 2)
]
value.update(logical_bytes=count, payload_bytes=1)
backup_manifest.validate(value)
assert len(value["files"]) + len(value["directories"]) == 98304
assert backup_manifest.MAX_BYTES == 7 * 1024**3
assert backup_manifest.MAX_LOGICAL_BYTES == 12 * 1024**3
old = {**value, "schema_version": 3, "format": backup_manifest.FORMAT, "payload_bytes": count}
old.pop("logical_bytes")
with pytest.raises(policy.DeliveryError, match="inventory count exceeds bound"):
backup_manifest.validate(old)
value["files"].append({**template, "path": "releases/one-too-many"})
value["logical_bytes"] += 1
with pytest.raises(policy.DeliveryError, match="inventory count exceeds bound"):
backup_manifest.validate(value)
def test_private_metadata_rejects_a_file_above_the_explicit_byte_limit(tmp_path):
path = tmp_path / "metadata.json"
path.write_bytes(b" " * (backup_manifest.MAX_METADATA_BYTES + 1))
with pytest.raises(policy.DeliveryError, match="metadata byte limit"):
backup_manifest.read_metadata(path)
+26
View File
@@ -509,3 +509,29 @@ def test_grouped_real_copy_workers_join_and_return_independent_verified_files(
path, uid, gid, mode = destination(row)
assert path.read_bytes() == b"shared"
restore.unchanged_file(path, result[row["path"]], uid, gid, mode)
@pytest.mark.parametrize(
"name", ["releases/../private", "factory//requests", "unknown/file", "releases/.ssh/key"]
)
def test_untrusted_restore_target_names_still_require_complete_validation(tmp_path, name):
record = {"path": name, "uid": 0, "gid": 0, "mode": 0o600, "bytes": 0}
with pytest.raises(DeliveryError, match="private backup member"):
restore.target(record, tmp_path)
def test_pending_copy_refuses_a_dangling_symlink_without_creating_its_target(tmp_path):
source = tmp_path / "source"
source.write_bytes(b"trusted bytes")
target = tmp_path / "target"
missing = tmp_path / "missing"
target.symlink_to(missing)
item = {
"bytes": source.stat().st_size,
"sha256": hashlib.sha256(source.read_bytes()).hexdigest(),
}
with pytest.raises(DeliveryError, match="existing recovery destination differs"):
restore_io.pending_file(
source, target, item, os.getuid(), os.getgid(), 0o600, time.monotonic() + 10
)
assert target.is_symlink() and not missing.exists()