feature/generador-instaladores-linux-windows
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
Pruebas de la transferencia SFTP al servidor remoto. Se mockea paramiko para no
|
||||
requerir un servidor SSH real; se valida la conversión de rutas y el flujo de subida.
|
||||
"""
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from app.transfer import sftp_copy
|
||||
@@ -32,12 +34,26 @@ def test_upload_origen_inexistente(tmp_path):
|
||||
sftp_copy.upload_to_remote(str(tmp_path / "noexiste.bak"), CFG)
|
||||
|
||||
|
||||
class FakeStat:
|
||||
def __init__(self, st_size):
|
||||
self.st_size = st_size
|
||||
|
||||
|
||||
class FakeSFTP:
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
|
||||
def put(self, local, remote):
|
||||
def put(self, local, remote, confirm=True):
|
||||
self.store["put"] = (local, remote)
|
||||
self.store["confirm"] = confirm
|
||||
# Registra el tamaño para que stat() (verificación de subida) lo confirme.
|
||||
self.store.setdefault("sizes", {})[remote] = os.path.getsize(local)
|
||||
|
||||
def stat(self, remote):
|
||||
sizes = self.store.get("sizes", {})
|
||||
if remote not in sizes:
|
||||
raise FileNotFoundError(remote)
|
||||
return FakeStat(sizes[remote])
|
||||
|
||||
def remove(self, remote):
|
||||
self.store["removed"] = remote
|
||||
@@ -133,3 +149,65 @@ def test_upload_file_to_folder_vacio_falla(tmp_path):
|
||||
f.write_bytes(b"x")
|
||||
with pytest.raises(SFTPCopyError, match="carpeta remota"):
|
||||
sftp_copy.upload_file_to_folder(str(f), CFG, " ")
|
||||
|
||||
|
||||
def test_upload_usa_confirm_false(tmp_path, monkeypatch):
|
||||
"""La subida no debe delegar la verificación al confirm inmediato de paramiko."""
|
||||
zf = tmp_path / "backup.zip"
|
||||
zf.write_bytes(b"zipdata")
|
||||
store: dict = {}
|
||||
monkeypatch.setattr(sftp_copy, "paramiko", _fake_paramiko(store))
|
||||
|
||||
sftp_copy.upload_file_to_folder(str(zf), CFG, "D:\\In")
|
||||
assert store["confirm"] is False
|
||||
|
||||
|
||||
def test_verify_remote_size_reintenta_stat_flaky(monkeypatch):
|
||||
"""Un stat transitoriamente fallido se reintenta y NO produce falso fallo."""
|
||||
monkeypatch.setattr(sftp_copy, "VERIFY_DELAY_SECONDS", 0)
|
||||
calls = {"n": 0}
|
||||
|
||||
class Flaky:
|
||||
def stat(self, remote):
|
||||
calls["n"] += 1
|
||||
if calls["n"] < 2:
|
||||
raise OSError("stat flaky")
|
||||
return FakeStat(100)
|
||||
|
||||
sftp_copy._verify_remote_size(Flaky(), "C:/In/x.zip", 100) # no debe lanzar
|
||||
assert calls["n"] == 2
|
||||
|
||||
|
||||
def test_verify_remote_size_tamano_incorrecto_falla(monkeypatch):
|
||||
"""Si el tamaño remoto nunca coincide, es un fallo genuino de entrega."""
|
||||
monkeypatch.setattr(sftp_copy, "VERIFY_DELAY_SECONDS", 0)
|
||||
|
||||
class Wrong:
|
||||
def stat(self, remote):
|
||||
return FakeStat(50)
|
||||
|
||||
with pytest.raises(SFTPCopyError, match="verificar"):
|
||||
sftp_copy._verify_remote_size(Wrong(), "C:/In/x.zip", 100)
|
||||
|
||||
|
||||
def test_upload_zip_parts_adjunta_uploaded_en_fallo(tmp_path, monkeypatch):
|
||||
"""Ante un fallo parcial, la excepción lleva las partes ya subidas para limpieza."""
|
||||
p1 = tmp_path / "big.zip.001"
|
||||
p2 = tmp_path / "big.zip.002"
|
||||
p1.write_bytes(b"a")
|
||||
p2.write_bytes(b"b")
|
||||
|
||||
calls = {"n": 0}
|
||||
|
||||
def fake_upload(local, cfg, remote_folder):
|
||||
calls["n"] += 1
|
||||
if calls["n"] == 1:
|
||||
return "D:/In/big.zip.001"
|
||||
raise SFTPCopyError("boom en la parte 2")
|
||||
|
||||
monkeypatch.setattr(sftp_copy, "upload_file_to_folder", fake_upload)
|
||||
|
||||
with pytest.raises(SFTPCopyError) as exc_info:
|
||||
sftp_copy.upload_zip_parts([str(p1), str(p2)], CFG, "D:\\In")
|
||||
|
||||
assert getattr(exc_info.value, "uploaded", None) == ["D:/In/big.zip.001"]
|
||||
|
||||
Reference in New Issue
Block a user