cuando termines y verifica que esté todo subido

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 01:33:35 +02:00
parent e1e9bb7499
commit a90b7443e4
20 changed files with 1855 additions and 2 deletions
@@ -0,0 +1,118 @@
"""Tests para upsert_xlsx_sheet."""
import os
import sys
from openpyxl import Workbook, load_workbook
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
from functions.infra.upsert_xlsx_sheet import upsert_xlsx_sheet
def _build_initial_book(path):
"""Crea un libro con dos hojas: 'Datos' (gestionada con valor manual) y
'Personal' (trabajo del usuario que NO debe tocarse).
"""
wb = Workbook()
wb.remove(wb.active)
datos = wb.create_sheet("Datos")
# Cabecera de la hoja gestionada.
for c, name in enumerate(["Programa", "Clicks", "Ingreso", "EPC"], start=1):
datos.cell(row=1, column=c, value=name)
# Fila existente para Alpha con un valor manual en "Ingreso".
datos.cell(row=2, column=1, value="Alpha")
datos.cell(row=2, column=2, value=10)
datos.cell(row=2, column=3, value=999) # valor manual a preservar
personal = wb.create_sheet("Personal")
personal.cell(row=1, column=1, value="mis_notas")
personal.cell(row=2, column=1, value="no me toques")
personal.cell(row=2, column=2, value=1234)
wb.save(path)
def test_no_destructivo_y_preserva_trabajo_manual(tmp_path):
path = str(tmp_path / "libro.xlsx")
_build_initial_book(path)
result = upsert_xlsx_sheet(
xlsx_path=path,
sheet_name="Datos",
records=[
{"Programa": "Alpha", "Clicks": 100, "Ingreso": 50},
{"Programa": "Beta", "Clicks": 200, "Ingreso": 80},
{"Programa": "Gamma", "Clicks": 300, "Ingreso": 120},
],
columns=["Programa", "Clicks", "Ingreso", "EPC"],
key_col="Programa",
preserve_cols=["Ingreso"],
formulas={"EPC": {"f": '=IFERROR({Ingreso}{row}/{Clicks}{row},"")', "fmt": "0.00"}},
)
# El dict de retorno reporta lo esperado.
assert result["sheet"] == "Datos"
assert result["rows_written"] == 3
assert "Personal" in result["other_sheets_preserved"]
assert result["manual_cells_preserved"] == 1
assert result["backup_path"] == path + ".bak"
wb = load_workbook(path)
# (a) La hoja "Personal" sigue existiendo con sus valores intactos.
assert "Personal" in wb.sheetnames
personal = wb["Personal"]
assert personal.cell(row=1, column=1).value == "mis_notas"
assert personal.cell(row=2, column=1).value == "no me toques"
assert personal.cell(row=2, column=2).value == 1234
datos = wb["Datos"]
header = {datos.cell(row=1, column=c).value: c for c in range(1, datos.max_column + 1)}
# Localizar la fila de Alpha por la columna clave.
alpha_row = None
for r in range(2, datos.max_row + 1):
if datos.cell(row=r, column=header["Programa"]).value == "Alpha":
alpha_row = r
break
assert alpha_row is not None
# (b) El valor manual en "Ingreso" para Alpha (999) NO se pisó con 50.
assert datos.cell(row=alpha_row, column=header["Ingreso"]).value == 999
# (c) Se añadieron las filas nuevas (Beta y Gamma no existían antes).
programas = {
datos.cell(row=r, column=header["Programa"]).value
for r in range(2, datos.max_row + 1)
}
assert {"Alpha", "Beta", "Gamma"} <= programas
# (d) La columna de fórmula contiene una fórmula.
epc = datos.cell(row=alpha_row, column=header["EPC"]).value
assert isinstance(epc, str) and epc.startswith("=")
assert "IFERROR" in epc
def test_crea_libro_nuevo_si_no_existe(tmp_path):
path = str(tmp_path / "nuevo.xlsx")
assert not os.path.exists(path)
result = upsert_xlsx_sheet(
xlsx_path=path,
sheet_name="Hoja1",
records=[{"A": "x", "B": "y"}],
columns=["A", "B"],
)
assert os.path.exists(path)
# Sin archivo previo no hay backup.
assert result["backup_path"] == ""
assert result["rows_written"] == 1
wb = load_workbook(path)
assert wb.sheetnames == ["Hoja1"]
ws = wb["Hoja1"]
assert ws.cell(row=1, column=1).value == "A"
assert ws.cell(row=2, column=1).value == "x"