Files
soft_usite/pkg/services/plantilla_import_test.go
Lizandro GuarnizoandClaude Opus 5 c4b0305112 feat(umind): los agentes también leen archivos, no solo audios e imágenes
Faltaba la tercera pata: audio pasaba por Whisper, imagen por OCR, y un PDF o
un Word adjunto se ignoraba en silencio. Ahora hay un interruptor por canal
—"Leer archivos adjuntos"— y los documentos que llegan por Telegram o WhatsApp
se convierten a texto antes de pasar al agente.

PDF se lee con stdlib cuando el documento es digital (facturas, cotizaciones,
lo exportado por cualquier programa) y cae al OCR si es un escaneo. Word .docx
es un zip con XML adentro; texto plano, CSV y JSON van directo.

El agente recibe el contenido con contexto ("el cliente adjuntó X, y escribió
Y"), no el chorizo pelado: sin eso el modelo contesta como si el cliente
hubiera tipeado una factura.

De paso la importación de plantillas gana PDF, que usa el mismo extractor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 18:50:59 -05:00

68 lines
1.8 KiB
Go

package services
import (
"archive/zip"
"bytes"
"strings"
"testing"
)
func docxDePrueba(t *testing.T, documentXML string) []byte {
t.Helper()
var buf bytes.Buffer
zw := zip.NewWriter(&buf)
w, err := zw.Create("word/document.xml")
if err != nil {
t.Fatal(err)
}
if _, err := w.Write([]byte(documentXML)); err != nil {
t.Fatal(err)
}
if err := zw.Close(); err != nil {
t.Fatal(err)
}
return buf.Bytes()
}
func TestExtraerTextoDePlantillaDocx(t *testing.T) {
xml := `<?xml version="1.0"?><w:document><w:body>` +
`<w:p><w:r><w:t>COTIZACI&amp;Oacute;N</w:t></w:r></w:p>` +
`<w:p><w:r><w:t>Cliente: </w:t></w:r><w:r><w:t>Acme SAS</w:t></w:r></w:p>` +
`<w:p><w:r><w:t>Total: $1.500.000</w:t></w:r></w:p>` +
`</w:body></w:document>`
got, err := ExtraerTextoDePlantilla("cotizacion.docx", docxDePrueba(t, xml))
if err != nil {
t.Fatalf("ExtraerTextoDePlantilla: %v", err)
}
// Los runs de un mismo párrafo quedan pegados; los párrafos, en líneas.
if !strings.Contains(got, "Cliente: Acme SAS") {
t.Errorf("falta la línea del cliente en:\n%s", got)
}
if !strings.Contains(got, "Total: $1.500.000") {
t.Errorf("falta el total en:\n%s", got)
}
if strings.Contains(got, "<w:") {
t.Errorf("quedaron etiquetas XML en:\n%s", got)
}
}
func TestExtraerTextoDePlantillaFormatoNoSoportado(t *testing.T) {
if _, err := ExtraerTextoDePlantilla("plantilla.xyz", []byte("x")); err == nil {
t.Error("una extensión desconocida debería devolver error")
}
}
func TestLimpiarCercaDeCodigo(t *testing.T) {
casos := map[string]string{
"```html\n<p>hola</p>\n```": "<p>hola</p>",
"```\n<p>hola</p>\n```": "<p>hola</p>",
"<p>hola</p>": "<p>hola</p>",
}
for in, want := range casos {
if got := limpiarCercaDeCodigo(in); got != want {
t.Errorf("limpiarCercaDeCodigo(%q) = %q, want %q", in, got, want)
}
}
}