package internal import ( "testing" ) func facturaValida() *InvoiceInput { return &InvoiceInput{ Tipo: "alta", Factura: FacturaInput{ EmisorNIF: "A1234567B", // [A-Z0-9] + 7 dígitos + [A-Z] EmisorNombre: "Empresa Test SL", NumSerie: "INV-2026-001", FechaExpedicion: "28-05-2026", TipoFactura: "F1", Descripcion: "Servicios", IVA: []IVAInput{{Base: 100, Cuota: 21, Tipo: 21}}, ImporteTotal: 121.0, }, Sistema: SistemaInput{ Nombre: "MiSistema", NIFProveedor: "B9876543C", Version: "1.0", }, } } func TestValidateInvoiceInput_EntradaValida(t *testing.T) { errs := ValidateInvoiceInput(facturaValida()) if len(errs) != 0 { t.Errorf("entrada válida no debería tener errores, obtuvo: %v", errs) } } func TestValidateInvoiceInput_TipoVacio(t *testing.T) { in := facturaValida() in.Tipo = "" errs := ValidateInvoiceInput(in) if !contieneError(errs, "tipo") { t.Error("debería fallar con tipo vacío") } } func TestValidateInvoiceInput_TipoInvalido(t *testing.T) { in := facturaValida() in.Tipo = "modificacion" errs := ValidateInvoiceInput(in) if !contieneError(errs, "tipo") { t.Error("debería fallar con tipo inválido") } } func TestValidateInvoiceInput_NIFVacio(t *testing.T) { in := facturaValida() in.Factura.EmisorNIF = "" errs := ValidateInvoiceInput(in) if !contieneError(errs, "emisor_nif") { t.Error("debería fallar con NIF vacío") } } func TestValidateInvoiceInput_NIFFormatoInvalido(t *testing.T) { casos := []string{ "12345678", // 8 dígitos sin letra final "ABCDEFGHI", // todo letras "A1234567", // empresa sin último carácter "123456789", // 9 dígitos sin letra "1234567R", // 7 dígitos + letra (autónomo necesita 8 dígitos) "A123456BC", // empresa con dos letras al final "", } for _, nif := range casos { in := facturaValida() in.Factura.EmisorNIF = nif errs := ValidateInvoiceInput(in) if !contieneError(errs, "emisor_nif") { t.Errorf("NIF %q debería ser inválido", nif) } } } func TestValidateInvoiceInput_NIFFormatos_Validos(t *testing.T) { casos := []string{ "A1234567B", // empresa, letra final letra "B9876543C", // empresa, letra final letra "A12345678", // empresa, letra final dígito "53950250R", // autónomo (8 dígitos + letra) "12345678Z", // autónomo genérico "X1234567L", // NIE (letra inicial + 7 dígitos + letra) } for _, nif := range casos { in := facturaValida() in.Factura.EmisorNIF = nif errs := ValidateInvoiceInput(in) if contieneError(errs, "emisor_nif") { t.Errorf("NIF %q debería ser válido", nif) } } } func TestValidateInvoiceInput_FechaFormatoInvalido(t *testing.T) { casos := []string{"2026-05-28", "28/05/2026", "2026/05/28", "28-5-2026"} for _, fecha := range casos { in := facturaValida() in.Factura.FechaExpedicion = fecha errs := ValidateInvoiceInput(in) if !contieneError(errs, "fecha_expedicion") { t.Errorf("fecha %q debería ser inválida (esperado dd-mm-yyyy)", fecha) } } } func TestValidateInvoiceInput_TipoFacturaInvalido(t *testing.T) { in := facturaValida() in.Factura.TipoFactura = "X9" errs := ValidateInvoiceInput(in) if !contieneError(errs, "tipo_factura") { t.Error("debería fallar con tipo de factura inválido") } } func TestValidateInvoiceInput_TiposFacturaValidos(t *testing.T) { for _, tipo := range []string{"F1", "F2", "R1", "R2", "R3", "R4", "R5"} { in := facturaValida() in.Factura.TipoFactura = tipo errs := ValidateInvoiceInput(in) if contieneError(errs, "tipo_factura") { t.Errorf("tipo de factura %q debería ser válido", tipo) } } } func TestValidateInvoiceInput_IVAVacio(t *testing.T) { in := facturaValida() in.Factura.IVA = nil errs := ValidateInvoiceInput(in) if !contieneError(errs, "iva") { t.Error("debería fallar sin líneas de IVA") } } func TestValidateInvoiceInput_ImporteNegativo(t *testing.T) { in := facturaValida() in.Factura.ImporteTotal = -1.0 errs := ValidateInvoiceInput(in) if !contieneError(errs, "importe_total") { t.Error("debería fallar con importe negativo") } } func TestValidateInvoiceInput_SistemaVacio(t *testing.T) { in := facturaValida() in.Sistema = SistemaInput{} errs := ValidateInvoiceInput(in) if len(errs) < 3 { t.Errorf("debería fallar con sistema vacío, obtuvo %d errores", len(errs)) } } func contieneError(errs []error, campo string) bool { for _, e := range errs { if ve, ok := e.(*ValidationError); ok && ve.Field != "" { if len(ve.Field) >= len(campo) { for i := 0; i <= len(ve.Field)-len(campo); i++ { if ve.Field[i:i+len(campo)] == campo { return true } } } } } return false }