Files
descarga_factura_transporte/tests/test_documentos.py
T
GeorgeandClaude Sonnet 5.5 1d6d869111 se reorganiza tests/ y se agrega suite pytest
Los scripts manuales pasan a tools/ y los correos de ejemplo a
tests/fixtures/. Suite de 95 pruebas: caracterización de la extracción sobre
los 46 correos (resultados esperados en tests/golden/, fuera de la carpeta
que descargar_correos_prueba borra), unitarias de draft_sap, notificaciones,
documentos, adjuntos y config_validator, y flujo completo de
_procesar_un_correo con SAP/HANA/Outlook simulados. pytest como dependencia
dev y configuración en pyproject.toml.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 22:03:38 -05:00

124 lines
5.1 KiB
Python

import logging
import os
import adjuntos
import documentos
# ============================================================================
# completar_guias
# ============================================================================
def test_completar_guias_desde_pdf_normaliza_strings():
xml_doc = {
"ruc_emisor": "20537929520", "tipo": "01", "serie": "F002", "numero": "486",
"lineas": [{"guias_remision": []}, {"guias_remision": [{"tipo": "09", "serie": "T001", "numero": "5"}]}],
}
pdf_cache = {"20537929520-01-F002-486.pdf": {"items": [
{"guias_remision": ["T001-00123"], "guia_transportista": "V002-0011"},
{"guias_remision": ["T001-999"]},
]}}
copiadas = documentos.completar_guias([xml_doc], pdf_cache)
assert copiadas == 1
assert xml_doc["lineas"][0]["guias_remision"] == [{"tipo": "09", "serie": "T001", "numero": "123"}]
assert xml_doc["lineas"][0]["guia_transportista"] == {"tipo": "31", "serie": "V002", "numero": "11"}
assert xml_doc["lineas"][1]["guias_remision"][0]["numero"] == "5" # la que ya tenía no se toca
def test_completar_guias_sin_pdf_no_hace_nada():
doc = {"ruc_emisor": "1", "tipo": "01", "serie": "F", "numero": "1", "lineas": [{"guias_remision": []}]}
assert documentos.completar_guias([doc], {}) == 0
assert doc["lineas"][0]["guias_remision"] == []
# ============================================================================
# validar_guias
# ============================================================================
def test_validar_guias_advierte_solo_las_invalidas(caplog):
doc = {"serie": "F002", "numero": "1", "lineas": [{
"guias_remision": [{"tipo": "09", "serie": "T001", "numero": "1"}, {"tipo": "09", "serie": "T001"}],
"guia_transportista": {"tipo": "31", "serie": "", "numero": "2"},
}]}
with caplog.at_level(logging.WARNING, logger="pipeline"):
documentos.validar_guias([doc])
mensajes = [r.getMessage() for r in caplog.records]
assert len(mensajes) == 2
assert any("guias_remision[1] inválida" in m for m in mensajes)
assert any("guia_transportista inválida" in m for m in mensajes)
assert all(r.levelno == logging.WARNING for r in caplog.records)
def test_validar_guias_sin_problemas_no_registra(caplog):
doc = {"serie": "F", "numero": "1", "lineas": [{"guias_remision": [{"tipo": "09", "serie": "T001", "numero": "1"}]}]}
with caplog.at_level(logging.WARNING, logger="pipeline"):
documentos.validar_guias([doc])
assert caplog.records == []
# ============================================================================
# copiar_a_red
# ============================================================================
def _comprobante(tmp_path, fecha="2026-03-05"):
xml = tmp_path / "a.xml"
pdf = tmp_path / "a.pdf"
xml.write_text("<x/>")
pdf.write_bytes(b"%PDF")
return {"ruc_emisor": "20111", "tipo": "01", "serie": "F002", "numero": "7",
"fecha_emision": fecha, "ruta_xml": str(xml), "ruta_pdf": str(pdf)}
def test_copiar_a_red_arma_la_estructura(tmp_path, monkeypatch):
red = tmp_path / "red"
monkeypatch.setenv("RUTA_RED_FACTURACION", str(red))
monkeypatch.setenv("RUC_EMPRESA", "20999")
data = _comprobante(tmp_path)
documentos.copiar_a_red(data)
destino = red / "2026" / "3" / "5" / "01" / "20999" / "20111" # mes y día sin ceros
assert (destino / "20999-01-F002-7.xml").exists()
assert (destino / "20999-01-F002-7_FormatoImpreso.pdf").exists()
assert data["ruta_red_xml"].endswith("\\20999-01-F002-7.xml") # estilo UNC con backslash
assert data["ruta_red_pdf"].endswith("\\20999-01-F002-7_FormatoImpreso.pdf")
def test_copiar_a_red_deshabilitada_si_no_hay_ruta(tmp_path, monkeypatch):
monkeypatch.setenv("RUTA_RED_FACTURACION", "")
data = _comprobante(tmp_path)
documentos.copiar_a_red(data)
assert "ruta_red_xml" not in data and "ruta_red_pdf" not in data
def test_copiar_a_red_fecha_invalida_no_falla(tmp_path, monkeypatch):
red = tmp_path / "red"
monkeypatch.setenv("RUTA_RED_FACTURACION", str(red))
monkeypatch.setenv("RUC_EMPRESA", "20999")
data = _comprobante(tmp_path, fecha="no-es-fecha")
documentos.copiar_a_red(data)
assert not red.exists() and "ruta_red_xml" not in data
# ============================================================================
# adjuntos.filtrar_y_limpiar
# ============================================================================
def test_filtrar_conserva_pdf_xml_y_borra_el_resto(tmp_path):
for nombre in ("a.pdf", "B.XML", "c.png"):
(tmp_path / nombre).write_text("x")
email = {"adjuntos": ["a.pdf", "B.XML", "c.png"]}
assert adjuntos.filtrar_y_limpiar(email, str(tmp_path)) is True
assert email["adjuntos"] == ["a.pdf", "B.XML"]
assert sorted(os.listdir(tmp_path)) == ["B.XML", "a.pdf"]
def test_filtrar_sin_pdf_ni_xml_borra_todo(tmp_path):
for nombre in ("c.png", "d.docx"):
(tmp_path / nombre).write_text("x")
email = {"adjuntos": ["c.png", "d.docx"]}
assert adjuntos.filtrar_y_limpiar(email, str(tmp_path)) is False
assert os.listdir(tmp_path) == []