package services import ( "os" "strings" "testing" ) func TestParsearItems(t *testing.T) { items, total := parsearItems(`[{"descripcion":"Licencia","cantidad":3,"precio":200000}]`) if len(items) != 1 || total != 600000 { t.Fatalf("items=%d total=%v, esperaba 1 ítem y 600000", len(items), total) } // Sin cantidad se asume 1: el modelo la omite seguido y un total en cero // en una cotización es peor que uno aproximado. items, total = parsearItems(`[{"descripcion":"Servicio","precio":50000}]`) if len(items) != 1 || items[0].Cantidad != 1 || total != 50000 { t.Errorf("cantidad omitida: items=%+v total=%v", items, total) } // JSON roto no puede tumbar la generación entera. if items, total := parsearItems(`{no es json`); items != nil || total != 0 { t.Errorf("JSON inválido debería dar vacío, dio %+v / %v", items, total) } if items, _ := parsearItems(""); items != nil { t.Error("sin ítems debería dar vacío") } } func TestValidarPlantillaHTML(t *testing.T) { if err := ValidarPlantillaHTML(`

{{.Cliente}}

{{range .Items}}

{{.Descripcion}}

{{end}}`); err != nil { t.Errorf("una plantilla válida fue rechazada: %v", err) } if err := ValidarPlantillaHTML(`

{{.Cliente}

`); err == nil { t.Error("una plantilla rota tiene que rechazarse antes de guardarse") } } // Cada PDF levanta un Chrome entero. Sin semáforo, cien clientes generando a // la vez son cien navegadores y el servidor se cae antes que por la IA. func TestGeneracionDePDFTieneSemaforo(t *testing.T) { b, err := os.ReadFile("umind_documento_tools.go") if err != nil { t.Fatal(err) } s := string(b) if cap(pdfEnCurso) == 0 || cap(pdfEnCurso) > 5 { t.Errorf("el semáforo de PDF tiene capacidad %d: debería ser chica y mayor que cero", cap(pdfEnCurso)) } i := strings.Index(s, "func renderPDFDesdePlantilla") cuerpo := s[i:] cuerpo = cuerpo[:strings.Index(cuerpo, "\n// guardarPDF")] if !strings.Contains(cuerpo, "pdfEnCurso <- struct{}{}") || !strings.Contains(cuerpo, "<-pdfEnCurso") { t.Error("RenderHTMLToPDF tiene que llamarse con el semáforo tomado y liberarlo después") } } // Las plantillas globales del staff no pueden salir en un documento de un // cliente, ni al revés. Es el mismo patrón que ya se corrigió en AiConfig. func TestPlantillaGlobalFiltraPorTenantNulo(t *testing.T) { b, err := os.ReadFile("../models/plantilla_documento.go") if err != nil { t.Fatal(err) } s := string(b) i := strings.Index(s, "func GetPlantillaDocumentoActiva") cuerpo := s[i:] cuerpo = cuerpo[:strings.Index(cuerpo, "\n// GetPlantillaDocumentoDeTenant")] if !strings.Contains(cuerpo, "tenant_id IS NULL") { t.Error("la plantilla global tiene que filtrar tenant_id IS NULL, si no puede devolver la de un cliente") } }