ProyectoGrupal/VerifactuMidAPI/internal/cert/validator_test.go

202 lines
6.1 KiB
Go
Raw Permalink Normal View History

package cert
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"testing"
"time"
"software.sslmate.com/src/go-pkcs12"
)
const testPassword = "test-password-123"
// generarP12 crea un certificado autofirmado y lo empaqueta en P12.
// notBefore y notAfter controlan la validez del certificado.
func generarP12(t *testing.T, cn string, notBefore, notAfter time.Time) []byte {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("generando clave RSA: %v", err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: cn},
NotBefore: notBefore,
NotAfter: notAfter,
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
t.Fatalf("creando certificado: %v", err)
}
x509Cert, err := x509.ParseCertificate(certDER)
if err != nil {
t.Fatalf("parseando certificado: %v", err)
}
p12Data, err := pkcs12.Modern.Encode(key, x509Cert, nil, testPassword)
if err != nil {
t.Fatalf("codificando P12: %v", err)
}
return p12Data
}
func TestValidateP12Bytes_CertificadoValido365Dias(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Valid 365 days", now.Add(-time.Hour), now.Add(365*24*time.Hour))
result := ValidateP12Bytes(p12, testPassword)
if !result.Valid {
t.Errorf("esperado válido, obtuvo error: %s", result.Error)
}
if result.Error != "" {
t.Errorf("no debería haber error, obtuvo: %s", result.Error)
}
if len(result.Warnings) > 0 {
t.Errorf("no debería haber warnings para cert de 365 días, obtuvo: %v", result.Warnings)
}
if result.CertInfo == nil {
t.Fatal("CertInfo no debería ser nil para cert válido")
}
if result.CertInfo.DaysUntilExpiry < 360 {
t.Errorf("días hasta expiración: esperado ~365, obtuvo %d", result.CertInfo.DaysUntilExpiry)
}
}
func TestValidateP12Bytes_CertificadoValido60Dias(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Valid 60 days", now.Add(-time.Hour), now.Add(60*24*time.Hour))
result := ValidateP12Bytes(p12, testPassword)
if !result.Valid {
t.Errorf("esperado válido, obtuvo error: %s", result.Error)
}
if len(result.Warnings) > 0 {
t.Errorf("no debería haber warnings para cert de 60 días, obtuvo: %v", result.Warnings)
}
if result.CertInfo.DaysUntilExpiry < 55 || result.CertInfo.DaysUntilExpiry > 65 {
t.Errorf("días hasta expiración: esperado ~60, obtuvo %d", result.CertInfo.DaysUntilExpiry)
}
}
func TestValidateP12Bytes_CertificadoExpirado(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Expired", now.Add(-20*24*time.Hour), now.Add(-5*24*time.Hour))
result := ValidateP12Bytes(p12, testPassword)
if result.Valid {
t.Error("esperado inválido para certificado expirado")
}
if result.Error != "certificate_expired" {
t.Errorf("error esperado: certificate_expired, obtuvo: %s", result.Error)
}
if result.CertInfo == nil || !result.CertInfo.Expired {
t.Error("CertInfo.Expired debería ser true")
}
}
func TestValidateP12Bytes_CertificadoCaducaProximamente(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Expiring Soon", now.Add(-time.Hour), now.Add(15*24*time.Hour))
result := ValidateP12Bytes(p12, testPassword)
if !result.Valid {
t.Errorf("esperado válido con warning, obtuvo error: %s", result.Error)
}
if len(result.Warnings) == 0 {
t.Error("esperado warning certificate_expiring_soon para cert que caduca en 15 días")
}
if len(result.Warnings) > 0 && result.Warnings[0] != "certificate_expiring_soon" {
t.Errorf("warning esperado: certificate_expiring_soon, obtuvo: %s", result.Warnings[0])
}
if result.CertInfo == nil || !result.CertInfo.ExpiringSoon {
t.Error("CertInfo.ExpiringSoon debería ser true")
}
}
func TestValidateP12Bytes_CertificadoAunNoValido(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Not Yet Valid", now.Add(30*24*time.Hour), now.Add(395*24*time.Hour))
result := ValidateP12Bytes(p12, testPassword)
if result.Valid {
t.Error("esperado inválido para certificado aún no vigente")
}
if result.Error != "certificate_not_yet_valid" {
t.Errorf("error esperado: certificate_not_yet_valid, obtuvo: %s", result.Error)
}
}
func TestValidateP12Bytes_ContraseñaIncorrecta(t *testing.T) {
now := time.Now()
p12 := generarP12(t, "Valid Cert", now.Add(-time.Hour), now.Add(365*24*time.Hour))
result := ValidateP12Bytes(p12, "contraseña-incorrecta")
if result.Valid {
t.Error("esperado inválido con contraseña incorrecta")
}
if result.Error != "invalid_password_or_format" {
t.Errorf("error esperado: invalid_password_or_format, obtuvo: %s", result.Error)
}
}
func TestValidateP12Bytes_BytesCorruptos(t *testing.T) {
result := ValidateP12Bytes([]byte("esto no es un P12 valido"), testPassword)
if result.Valid {
t.Error("esperado inválido para bytes corruptos")
}
if result.Error != "invalid_password_or_format" {
t.Errorf("error esperado: invalid_password_or_format, obtuvo: %s", result.Error)
}
}
func TestValidateP12Bytes_UmbralWarning30Dias(t *testing.T) {
now := time.Now()
// Exactamente en el umbral: 30 días → debe dar warning
p12Umbral := generarP12(t, "At threshold", now.Add(-time.Hour), now.Add(30*24*time.Hour))
resultUmbral := ValidateP12Bytes(p12Umbral, testPassword)
if !resultUmbral.Valid {
t.Errorf("cert en umbral debe ser válido, obtuvo error: %s", resultUmbral.Error)
}
if len(resultUmbral.Warnings) == 0 {
t.Error("cert con exactamente 30 días debe tener warning")
}
// 31 días → NO debe dar warning
p12Ok := generarP12(t, "Just above threshold", now.Add(-time.Hour), now.Add(31*24*time.Hour))
resultOk := ValidateP12Bytes(p12Ok, testPassword)
if !resultOk.Valid {
t.Errorf("cert con 31 días debe ser válido, obtuvo error: %s", resultOk.Error)
}
if len(resultOk.Warnings) > 0 {
t.Error("cert con 31 días NO debe tener warning")
}
}
func TestValidateP12_ArchivoNoExiste(t *testing.T) {
result := ValidateP12("/ruta/que/no/existe.p12", testPassword)
if result.Valid {
t.Error("esperado inválido para archivo inexistente")
}
if result.Error != "file_not_found" {
t.Errorf("error esperado: file_not_found, obtuvo: %s", result.Error)
}
}