feature/generador-instaladores-linux-windows
This commit is contained in:
157
tests/test_retention.py
Normal file
157
tests/test_retention.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
Pruebas de la retención por nodo (Procesados/) y por antigüedad (Fallados/).
|
||||
|
||||
Se mockea el acceso a la BD (JobRepository/EventRepository) y se opera sobre carpetas reales
|
||||
en tmp_path. Las fechas-carpeta de Procesados/ se derivan con el mismo helper que usa el
|
||||
limpiador para que la prueba sea independiente de la zona horaria del runner.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from app.engine.retention import RetentionCleaner
|
||||
|
||||
|
||||
class FakeJob:
|
||||
def __init__(self, job_id, node_name, source_name, finished_at):
|
||||
self.job_id = job_id
|
||||
self.node_name = node_name
|
||||
self.source_name = source_name
|
||||
self.finished_at = finished_at
|
||||
|
||||
|
||||
def _config(tmp_path, *, days=2, failed_days=7, dry_run=False):
|
||||
return {
|
||||
"paths": {
|
||||
"processed_folder": str(tmp_path / "processed"),
|
||||
"failed_folder": str(tmp_path / "failed"),
|
||||
},
|
||||
"retention": {"days": days, "failed_days": failed_days, "dry_run": dry_run},
|
||||
}
|
||||
|
||||
|
||||
def _date_folder(base, iso):
|
||||
"""Crea (si falta) la carpeta-fecha local correspondiente a un finished_at ISO UTC."""
|
||||
local_date = RetentionCleaner._local_date_from_iso(iso)
|
||||
folder = base / local_date.isoformat()
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
return folder
|
||||
|
||||
|
||||
def test_procesados_borra_obsoletos_conserva_reciente_y_nodo_unico(monkeypatch, tmp_path):
|
||||
processed = tmp_path / "processed"
|
||||
ref_iso = "2026-07-24T10:00:00"
|
||||
obsolete_iso = "2026-07-20T10:00:00"
|
||||
node_b_iso = "2026-07-22T10:00:00"
|
||||
|
||||
# NODO_A: copia obsoleta (a borrar) + copia más reciente (a conservar).
|
||||
obsolete_file = _date_folder(processed, obsolete_iso) / "NODO_A.zip"
|
||||
obsolete_file.write_bytes(b"viejo")
|
||||
recent_file = _date_folder(processed, ref_iso) / "NODO_A.zip"
|
||||
recent_file.write_bytes(b"nuevo")
|
||||
# NODO_B: una sola restauración (nunca se toca).
|
||||
node_b_file = _date_folder(processed, node_b_iso) / "NODO_B.zip"
|
||||
node_b_file.write_bytes(b"unico")
|
||||
|
||||
obsolete_job = FakeJob("job-a-old", "NODO_A", "NODO_A.zip", obsolete_iso)
|
||||
|
||||
purged: list[str] = []
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_obsolete_completed_by_node",
|
||||
lambda days: [obsolete_job],
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_latest_completed_per_node",
|
||||
lambda: {"NODO_A": ref_iso, "NODO_B": node_b_iso},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.mark_purged", lambda job_id: purged.append(job_id)
|
||||
)
|
||||
monkeypatch.setattr("app.engine.retention.EventRepository.create", lambda *a, **k: None)
|
||||
|
||||
result = RetentionCleaner(_config(tmp_path)).run()
|
||||
|
||||
assert not obsolete_file.exists() # obsoleto borrado
|
||||
assert recent_file.exists() # más reciente intacto
|
||||
assert node_b_file.exists() # nodo de una sola copia intacto
|
||||
assert purged == ["job-a-old"]
|
||||
assert result.deleted_files == 1
|
||||
|
||||
|
||||
def test_procesados_dry_run_no_borra(monkeypatch, tmp_path):
|
||||
processed = tmp_path / "processed"
|
||||
obsolete_iso = "2026-07-20T10:00:00"
|
||||
obsolete_file = _date_folder(processed, obsolete_iso) / "NODO_A.zip"
|
||||
obsolete_file.write_bytes(b"viejo")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_obsolete_completed_by_node",
|
||||
lambda days: [FakeJob("job-a-old", "NODO_A", "NODO_A.zip", obsolete_iso)],
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_latest_completed_per_node",
|
||||
lambda: {"NODO_A": "2026-07-24T10:00:00"},
|
||||
)
|
||||
purged: list[str] = []
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.mark_purged", lambda job_id: purged.append(job_id)
|
||||
)
|
||||
monkeypatch.setattr("app.engine.retention.EventRepository.create", lambda *a, **k: None)
|
||||
|
||||
result = RetentionCleaner(_config(tmp_path, dry_run=True)).run()
|
||||
|
||||
assert obsolete_file.exists() # dry-run no borra
|
||||
assert purged == [] # ni marca purgado
|
||||
assert result.dry_run is True
|
||||
assert result.deleted_files == 1 # sí lo contabiliza como "se borraría"
|
||||
|
||||
|
||||
def test_fallados_borra_por_antiguedad_absoluta(monkeypatch, tmp_path):
|
||||
failed = tmp_path / "failed"
|
||||
old_folder = failed / "2026-07-10" # < (hoy - 7)
|
||||
recent_folder = failed / "2026-07-20" # >= (hoy - 7)
|
||||
old_folder.mkdir(parents=True)
|
||||
recent_folder.mkdir(parents=True)
|
||||
old_file = old_folder / "VIEJO.zip"
|
||||
old_file.write_bytes(b"x")
|
||||
recent_file = recent_folder / "RECIENTE.zip"
|
||||
recent_file.write_bytes(b"y")
|
||||
|
||||
# Sin obsoletos en Procesados; solo probamos Fallados.
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_obsolete_completed_by_node", lambda days: []
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_latest_completed_per_node", lambda: {}
|
||||
)
|
||||
monkeypatch.setattr("app.engine.retention.EventRepository.create", lambda *a, **k: None)
|
||||
|
||||
cleaner = RetentionCleaner(
|
||||
_config(tmp_path, failed_days=7), clock=lambda: datetime(2026, 7, 24, 12, 0, 0)
|
||||
)
|
||||
cleaner.run()
|
||||
|
||||
assert not old_file.exists() # carpeta-fecha vieja borrada
|
||||
assert recent_file.exists() # dentro de la ventana, se conserva
|
||||
|
||||
|
||||
def test_fallados_ignora_carpetas_no_fecha(monkeypatch, tmp_path):
|
||||
failed = tmp_path / "failed"
|
||||
weird = failed / "no-es-fecha"
|
||||
weird.mkdir(parents=True)
|
||||
keep = weird / "algo.zip"
|
||||
keep.write_bytes(b"z")
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_obsolete_completed_by_node", lambda days: []
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"app.engine.retention.JobRepository.get_latest_completed_per_node", lambda: {}
|
||||
)
|
||||
monkeypatch.setattr("app.engine.retention.EventRepository.create", lambda *a, **k: None)
|
||||
|
||||
RetentionCleaner(
|
||||
_config(tmp_path), clock=lambda: datetime(2026, 7, 24, 12, 0, 0)
|
||||
).run()
|
||||
|
||||
assert keep.exists() # nombre que no es fecha: no se toca
|
||||
Reference in New Issue
Block a user