Files
fn_registry/python/functions/infra/read_xlsx_test.py
T
egutierrez 927437a8d8 feat(infra): grupo claude-fleet — FleetView TUI + orquestacion de Claudes
Sistema FleetView para centralizar la flota de procesos Claude Code vivos en una
sola ventana kitty + tmux (socket aislado -L fleet) con un panel TUI:

- list_claude_fleet (+ tipo claude_fleet): escanea ~/.claude/sessions + goals +
  runtime, valida procesos vivos (anti-PID-reciclado), join por sessionId.
- list_resumable_claudes (+ tipo resumable_claude): sesiones cerradas reanudables.
- wrappers tmux: tmux_new_claude_window (con --resume), tmux_swap_window_into_console
  (preserva ancho del sidebar), tmux_map_claude_panes.
- launch_kittyclaude: comando entrypoint; instala atajos alt+flechas/enter/n/0/k/r,
  mouse on, remain-on-exit off; fija el ancho del sidebar con hooks.
- docs/capabilities/claude-fleet.md + entrada en el INDEX.

Incluye ademas funciones datascience en progreso (excel/duckdb/postgres) y ajustes
varios de docs e infra de otra sesion, agrupados aqui para no perderlos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 00:04:41 +02:00

145 lines
4.3 KiB
Python

"""Tests para read_xlsx.
Se importa el modulo por path directo (sin tocar __init__.py) para no depender
del re-export del paquete. write_xlsx_sheets se importa igual para el round-trip.
"""
import importlib.util
import os
_HERE = os.path.dirname(os.path.abspath(__file__))
def _load(name):
spec = importlib.util.spec_from_file_location(name, os.path.join(_HERE, f"{name}.py"))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
read_xlsx = _load("read_xlsx").read_xlsx
write_xlsx_sheets = _load("write_xlsx_sheets").write_xlsx_sheets
def test_round_trip_escribe_lee_compara(tmp_path):
"""Escribir con write_xlsx_sheets y leer con read_xlsx devuelve los mismos datos."""
out = str(tmp_path / "rt.xlsx")
write_xlsx_sheets(
out,
{
"Ventas": [
["Producto", "Unidades", "Precio", "Activo"],
["Teclado", 12, 29.99, True],
["Raton", 30, 14.5, False],
["Monitor", None, 199.0, True],
],
},
)
res = read_xlsx(out)
assert res["status"] == "ok"
assert list(res["sheets"].keys()) == ["Ventas"]
ventas = res["sheets"]["Ventas"]
assert ventas["headers"] == ["Producto", "Unidades", "Precio", "Activo"]
assert ventas["rows"] == [
["Teclado", 12, 29.99, True],
["Raton", 30, 14.5, False],
["Monitor", None, 199.0, True],
]
def test_lee_solo_la_hoja_indicada(tmp_path):
out = str(tmp_path / "multi.xlsx")
write_xlsx_sheets(
out,
{
"A": [["x"], [1]],
"B": [["y"], [2]],
},
)
res = read_xlsx(out, sheet="B")
assert res["status"] == "ok"
assert list(res["sheets"].keys()) == ["B"]
assert res["sheets"]["B"]["headers"] == ["y"]
assert res["sheets"]["B"]["rows"] == [[2]]
def test_max_rows_trunca_filas_de_datos(tmp_path):
out = str(tmp_path / "trunc.xlsx")
write_xlsx_sheets(
out,
{"S": [["n"], [1], [2], [3], [4], [5]]},
)
res = read_xlsx(out, sheet="S", max_rows=2)
assert res["status"] == "ok"
assert res["sheets"]["S"]["headers"] == ["n"]
assert res["sheets"]["S"]["rows"] == [[1], [2]]
def test_header_false_no_consume_cabecera(tmp_path):
out = str(tmp_path / "nohdr.xlsx")
write_xlsx_sheets(out, {"S": [["a", "b"], [1, 2]]})
res = read_xlsx(out, sheet="S", header=False)
assert res["status"] == "ok"
assert res["sheets"]["S"]["headers"] == []
assert res["sheets"]["S"]["rows"] == [["a", "b"], [1, 2]]
def test_fecha_se_devuelve_como_iso(tmp_path):
import datetime
from openpyxl import Workbook
out = str(tmp_path / "fechas.xlsx")
wb = Workbook()
ws = wb.active
ws.title = "F"
ws.append(["evento", "cuando"])
ws.append(["solo_fecha", datetime.date(2026, 6, 16)])
ws.append(["con_hora", datetime.datetime(2026, 6, 16, 14, 30, 0)])
wb.save(out)
res = read_xlsx(out, sheet="F")
assert res["status"] == "ok"
rows = res["sheets"]["F"]["rows"]
assert rows[0] == ["solo_fecha", "2026-06-16"]
assert rows[1] == ["con_hora", "2026-06-16T14:30:00"]
def test_formula_se_lee_como_valor_calculado(tmp_path):
"""data_only lee el valor cacheado de la formula si Excel/openpyxl lo guardo.
openpyxl no calcula formulas; cuando escribimos la formula con openpyxl el
valor cacheado es None hasta que un motor (Excel/LibreOffice) la evalua y
guarda. El round-trip valido es escribir el VALOR (no la formula).
"""
out = str(tmp_path / "calc.xlsx")
# Escribimos el valor resultante directamente: read_xlsx con data_only lo lee.
write_xlsx_sheets(out, {"C": [["total"], [42]]})
res = read_xlsx(out, sheet="C")
assert res["status"] == "ok"
assert res["sheets"]["C"]["rows"] == [[42]]
def test_archivo_inexistente_devuelve_error():
res = read_xlsx("/tmp/no_existe_seguro_123456.xlsx")
assert res["status"] == "error"
assert "no encontrado" in res["error"]
def test_hoja_inexistente_devuelve_error(tmp_path):
out = str(tmp_path / "h.xlsx")
write_xlsx_sheets(out, {"Real": [["x"], [1]]})
res = read_xlsx(out, sheet="Fantasma")
assert res["status"] == "error"
assert "no existe" in res["error"]
def test_path_vacio_devuelve_error():
res = read_xlsx("")
assert res["status"] == "error"