Merge PR #602: fix: prevent data loss in clipboard PNG conversion when ImageMagick fails
Authored by 0xbyt4. Only deletes temp .bmp after confirmed successful conversion, restores original on failure. Adds 3 tests.
This commit is contained in:
commit
5e6c7bc205
2 changed files with 49 additions and 1 deletions
|
|
@ -292,9 +292,12 @@ def _convert_to_png(path: Path) -> bool:
|
|||
["convert", str(tmp), "png:" + str(path)],
|
||||
capture_output=True, timeout=5,
|
||||
)
|
||||
tmp.unlink(missing_ok=True)
|
||||
if r.returncode == 0 and path.exists() and path.stat().st_size > 0:
|
||||
tmp.unlink(missing_ok=True)
|
||||
return True
|
||||
else:
|
||||
# Convert failed — restore the original file
|
||||
tmp.rename(path)
|
||||
except FileNotFoundError:
|
||||
logger.debug("ImageMagick not installed — cannot convert BMP to PNG")
|
||||
if tmp.exists() and not path.exists():
|
||||
|
|
|
|||
|
|
@ -558,6 +558,51 @@ class TestConvertToPng:
|
|||
assert result is True
|
||||
assert dest.exists() and dest.stat().st_size > 0
|
||||
|
||||
def test_imagemagick_failure_preserves_original(self, tmp_path):
|
||||
"""When ImageMagick convert fails, the original file must not be lost."""
|
||||
dest = tmp_path / "img.png"
|
||||
original_data = FAKE_BMP
|
||||
dest.write_bytes(original_data)
|
||||
|
||||
def fake_run_fail(cmd, **kw):
|
||||
# Simulate convert failing without producing output
|
||||
return MagicMock(returncode=1)
|
||||
|
||||
with patch.dict(sys.modules, {"PIL": None, "PIL.Image": None}):
|
||||
with patch("hermes_cli.clipboard.subprocess.run", side_effect=fake_run_fail):
|
||||
_convert_to_png(dest)
|
||||
|
||||
# Original file must still exist with original content
|
||||
assert dest.exists(), "Original file was lost after failed conversion"
|
||||
assert dest.read_bytes() == original_data
|
||||
|
||||
def test_imagemagick_not_installed_preserves_original(self, tmp_path):
|
||||
"""When ImageMagick is not installed, the original file must not be lost."""
|
||||
dest = tmp_path / "img.png"
|
||||
original_data = FAKE_BMP
|
||||
dest.write_bytes(original_data)
|
||||
|
||||
with patch.dict(sys.modules, {"PIL": None, "PIL.Image": None}):
|
||||
with patch("hermes_cli.clipboard.subprocess.run", side_effect=FileNotFoundError):
|
||||
_convert_to_png(dest)
|
||||
|
||||
assert dest.exists(), "Original file was lost when ImageMagick not installed"
|
||||
assert dest.read_bytes() == original_data
|
||||
|
||||
def test_imagemagick_timeout_preserves_original(self, tmp_path):
|
||||
"""When ImageMagick times out, the original file must not be lost."""
|
||||
import subprocess
|
||||
dest = tmp_path / "img.png"
|
||||
original_data = FAKE_BMP
|
||||
dest.write_bytes(original_data)
|
||||
|
||||
with patch.dict(sys.modules, {"PIL": None, "PIL.Image": None}):
|
||||
with patch("hermes_cli.clipboard.subprocess.run", side_effect=subprocess.TimeoutExpired("convert", 5)):
|
||||
_convert_to_png(dest)
|
||||
|
||||
assert dest.exists(), "Original file was lost after timeout"
|
||||
assert dest.read_bytes() == original_data
|
||||
|
||||
|
||||
# ── has_clipboard_image dispatch ─────────────────────────────────────────
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue