fix(release): make deployment backup and rollback immutable
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SCRIPT = ROOT / "scripts" / "release_backup_snapshot.py"
|
||||
|
||||
|
||||
def load_snapshot_module():
|
||||
spec = importlib.util.spec_from_file_location("release_backup_snapshot_test", SCRIPT)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_snapshot_is_byte_complete_and_reuses_only_verified_backup_bytes(tmp_path: Path) -> None:
|
||||
snapshot = load_snapshot_module()
|
||||
source = tmp_path / "source"
|
||||
source.mkdir()
|
||||
(source / "same.bin").write_bytes(b"unchanged")
|
||||
(source / "changed.bin").write_bytes(b"before")
|
||||
(source / "empty").mkdir()
|
||||
|
||||
prior = tmp_path / "prior"
|
||||
prior_manifest = tmp_path / "prior.tsv"
|
||||
snapshot.create_snapshot(source, prior, prior_manifest, label="storage")
|
||||
snapshot.verify_snapshot(prior, prior_manifest)
|
||||
|
||||
(source / "changed.bin").write_bytes(b"after")
|
||||
current = tmp_path / "current"
|
||||
current_manifest = tmp_path / "current.tsv"
|
||||
snapshot.create_snapshot(
|
||||
source,
|
||||
current,
|
||||
current_manifest,
|
||||
label="storage",
|
||||
link_dest_snapshot=prior,
|
||||
link_dest_manifest=prior_manifest,
|
||||
)
|
||||
snapshot.verify_snapshot(current, current_manifest)
|
||||
|
||||
assert os.path.samefile(prior / "same.bin", current / "same.bin")
|
||||
assert not os.path.samefile(prior / "changed.bin", current / "changed.bin")
|
||||
assert (current / "changed.bin").read_bytes() == b"after"
|
||||
assert (current / "empty").is_dir()
|
||||
|
||||
|
||||
def test_snapshot_rejects_symlinked_content(tmp_path: Path) -> None:
|
||||
snapshot = load_snapshot_module()
|
||||
source = tmp_path / "source"
|
||||
source.mkdir()
|
||||
target = source / "target.bin"
|
||||
target.write_bytes(b"target")
|
||||
try:
|
||||
(source / "link.bin").symlink_to(target)
|
||||
except OSError:
|
||||
pytest.skip("Symlink creation is unavailable on this host")
|
||||
|
||||
with pytest.raises(RuntimeError, match="refuses symlinked content"):
|
||||
snapshot.create_snapshot(source, tmp_path / "snapshot", tmp_path / "manifest.tsv", label="storage")
|
||||
|
||||
|
||||
def test_snapshot_verification_rejects_changed_retained_bytes(tmp_path: Path) -> None:
|
||||
snapshot = load_snapshot_module()
|
||||
source = tmp_path / "source"
|
||||
source.mkdir()
|
||||
(source / "artifact.bin").write_bytes(b"retained")
|
||||
retained = tmp_path / "snapshot"
|
||||
manifest = tmp_path / "manifest.tsv"
|
||||
snapshot.create_snapshot(source, retained, manifest, label="storage")
|
||||
(retained / "artifact.bin").chmod(0o644)
|
||||
(retained / "artifact.bin").write_bytes(b"tampered")
|
||||
|
||||
with pytest.raises(RuntimeError, match="checksum differs"):
|
||||
snapshot.verify_snapshot(retained, manifest)
|
||||
Reference in New Issue
Block a user