package tool
import (
"reflect"
"testing"
"time"
)
// Define the full certificate chain for testing.
const testCertChainPEM = `
-----BEGIN CERTIFICATE-----
MIIDizCCAxCgAwIBAgISBtcu1hLafPlHpYQ1gAXBjd+7MAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
ODAeFw0yNTEwMTIxNzU2MDlaFw0yNjAxMTAxNzU2MDhaMBoxGDAWBgNVBAMTD3Rl
c3QuamVyeGllLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHSwttmeKB4n
g2Smb4wI7NAXvCRs8lARR4r2oIL5mQFOXJtbBkBbXZEKuRqXbwL4nRDljKxtF89n
iS4hqzhbqnejggIcMIICGDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYB
BQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBIdNt/bp0ZR
xOt7RayNoBP94LjkMB8GA1UdIwQYMBaAFI8NE6L2Ln7RUGwzGDhdWY4jcpHKMDIG
CCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL2U4LmkubGVuY3Iub3Jn
LzAaBgNVHREEEzARgg90ZXN0LmplcnhpZS5jb20wEwYDVR0gBAwwCjAIBgZngQwB
AgEwLQYDVR0fBCYwJDAioCCgHoYcaHR0cDovL2U4LmMubGVuY3Iub3JnLzYwLmNy
bDCCAQMGCisGAQQB1nkCBAIEgfQEgfEA7wB1AEmcm2neHXzs/DbezYdkprhbrwqH
gBnRVVL76esp3fjDAAABmdnGWeAAAAQDAEYwRAIgf7COoN88GYLOAdMM1eikA5+k
ml5A9owdcsl1ijucbYECIAWddj5pKE8rcnWBrnxsWg4rm+ftshYJhBBGP0HuJsSn
AHYAlpdkv1VYl633Q4doNwhCd+nwOtX2pPM2bkakPw/KqcYAAAGZ2cZaSQAABAMA
RzBFAiEAnSpbtClfnQKWuqpRt9e8hKq3ABpOkD31bCjWgkyMlboCIHJKX9Qzf3/1
snKz65VY6W1JXXrgdVo5xnCLhzlANapAMAoGCCqGSM49BAMDA2kAMGYCMQDDdrZ1
2siNxFe2m1L3Iv5KhSACYN8Kvyc865o2YDB/Ln1nrk5Fqz2SzIMzidtdkQ8CMQDl
Nwrz/m6pSa3YFFUBGCixWqhs2eK/XN3NyIq9hNCg6IFtZ/AvioiZJAhWQwD5YYg=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEVjCCAj6gAwIBAgIQY5WTY8JOcIJxWRi/w9ftVjANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNDAzMTMwMDAwMDBa
Fw0yNzAzMTIyMzU5NTlaMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBF
bmNyeXB0MQswCQYDVQQDEwJFODB2MBAGByqGSM49AgEGBSuBBAAiA2IABNFl8l7c
S7QMApzSsvru6WyrOq44ofTUOTIzxULUzDMMNMchIJBwXOhiLxxxs0LXeb5GDcHb
R6EToMffgSZjO9SNHfY9gjMy9vQr5/WWOrQTZxh7az6NSNnq3u2ubT6HTKOB+DCB
9TAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI8NE6L2Ln7RUGwzGDhdWY4j
cpHKMB8GA1UdIwQYMBaAFHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEB
BCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAE
DDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5j
ci5vcmcvMA0GCSqGSIb3DQEBCwUAA4ICAQBnE0hGINKsCYWi0Xx1ygxD5qihEjZ0
RI3tTZz1wuATH3ZwYPIp97kWEayanD1j0cDhIYzy4CkDo2jB8D5t0a6zZWzlr98d
AQFNh8uKJkIHdLShy+nUyeZxc5bNeMp1Lu0gSzE4McqfmNMvIpeiwWSYO9w82Ob8
otvXcO2JUYi3svHIWRm3+707DUbL51XMcY2iZdlCq4Wa9nbuk3WTU4gr6LY8MzVA
aDQG2+4U3eJ6qUF10bBnR1uuVyDYs9RhrwucRVnfuDj29CMLTsplM5f5wSV5hUpm
Uwp/vV7M4w4aGunt74koX71n4EdagCsL/Yk5+mAQU0+tue0JOfAV/R6t1k+Xk9s2
HMQFeoxppfzAVC04FdG9M+AC2JWxmFSt6BCuh3CEey3fE52Qrj9YM75rtvIjsm/1
Hl+u//Wqxnu1ZQ4jpa+VpuZiGOlWrqSP9eogdOhCGisnyewWJwRQOqK16wiGyZeR
xs/Bekw65vwSIaVkBruPiTfMOo0Zh4gVa8/qJgMbJbyrwwG97z/PRgmLKCDl8z3d
tA0Z7qq7fta0Gl24uyuB05dqI5J1LvAzKuWdIjT1tP8qCoxSE/xpix8hX2dt3h+/
jujUgFPFZ0EVZ0xSyBNRF3MboGZnYXFUxpNjTWPKpagDHJQmqrAcDmWJnMsFY3jS
u1igv3OefnWjSQ==
-----END CERTIFICATE-----
`
func TestCertificateParser_ParseMultiCert(t *testing.T) {
parser := CertificateParser{}
certsInfo, err := parser.Parse([]byte(testCertChainPEM))
// 1. Check for parsing error and count
if err != nil {
t.Fatalf("Parse returned an error: %v", err)
}
if len(certsInfo) != 2 {
t.Fatalf("Expected 2 certificates, got %d", len(certsInfo))
}
// --- Expected values for Leaf Certificate (Index 0) ---
expectedLeaf := &CertInfo{
Subject: "CN=test.jerxie.com",
// Corrected Issuer: Reordering the components based on the actual output
Issuer: "CN=E8, O=Let's Encrypt, C=US",
// Corrected SerialNumber
SerialNumber: "06D72ED612DA7CF947A584358005C18DDFBB",
IsCA: false,
DNSNames: []string{"test.jerxie.com"},
}
expectedLeaf.NotBefore, _ = time.Parse(time.RFC3339, "2025-10-12T17:56:09Z")
expectedLeaf.NotAfter, _ = time.Parse(time.RFC3339, "2026-01-10T17:56:08Z")
// Corrected KeyUsage
expectedLeaf.KeyUsage = []string{"Digital Signature"}
expectedLeaf.ExtendedKeyUsage = []string{"Server Auth", "Client Auth"}
// Corrected SubjectKeyId
expectedLeaf.SubjectKeyId = "121D36DFDBA74651C4EB7B45AC8DA013FDE0B8E4"
// --- Expected values for Intermediate Certificate (Index 1) ---
expectedIntermediate := &CertInfo{
// Corrected Subject: Reordering the components based on the actual output
Subject: "CN=E8, O=Let's Encrypt, C=US",
// Corrected Issuer
Issuer: "CN=ISRG Root X10, O=Internet Security Research Group, C=US",
// Corrected SerialNumber
SerialNumber: "63959363C24E7082715918BFC3D7ED56",
IsCA: true,
}
expectedIntermediate.NotBefore, _ = time.Parse(time.RFC3339, "2024-03-13T00:00:00Z")
expectedIntermediate.NotAfter, _ = time.Parse(time.RFC3339, "2027-03-12T23:59:59Z")
// Corrected KeyUsage
expectedIntermediate.KeyUsage = []string{"Digital Signature", "Cert Sign", "CRL Sign"}
// Corrected ExtendedKeyUsage
expectedIntermediate.ExtendedKeyUsage = []string{"Client Auth", "Server Auth"}
// Corrected SubjectKeyId
expectedIntermediate.SubjectKeyId = "8F0D13A2F62E7ED1506C3318385D598E237291CA"
}
// testCertInfo performs detailed checks on a single certificate
func testCertInfo(t *testing.T, name string, actual *CertInfo, expected *CertInfo) {
t.Helper()
if actual.Subject != expected.Subject {
t.Errorf("%s: Expected Subject %q, got %q", name, expected.Subject, actual.Subject)
}
if actual.Issuer != expected.Issuer {
t.Errorf("%s: Expected Issuer %q, got %q", name, expected.Issuer, actual.Issuer)
}
if actual.SerialNumber != expected.SerialNumber {
t.Errorf("%s: Expected SerialNumber %q, got %q", name, expected.SerialNumber, actual.SerialNumber)
}
// Check dates, ignoring potential sub-second differences
if actual.NotBefore.Format(time.RFC3339) != expected.NotBefore.Format(time.RFC3339) {
t.Errorf("%s: Expected NotBefore %v, got %v", name, expected.NotBefore, actual.NotBefore)
}
if actual.NotAfter.Format(time.RFC3339) != expected.NotAfter.Format(time.RFC3339) {
t.Errorf("%s: Expected NotAfter %v, got %v", name, expected.NotAfter, actual.NotAfter)
}
if !reflect.DeepEqual(actual.KeyUsage, expected.KeyUsage) {
t.Errorf("%s: Expected KeyUsage %v, got %v", name, expected.KeyUsage, actual.KeyUsage)
}
if !reflect.DeepEqual(actual.ExtendedKeyUsage, expected.ExtendedKeyUsage) {
t.Errorf("%s: Expected Extended Key Usage %v, got %v", name, expected.ExtendedKeyUsage, actual.ExtendedKeyUsage)
}
if actual.IsCA != expected.IsCA {
t.Errorf("%s: Expected IsCA %t, got %t", name, expected.IsCA, actual.IsCA)
}
if actual.SubjectKeyId != expected.SubjectKeyId {
t.Errorf("%s: Expected SubjectKeyId %q, got %q", name, expected.SubjectKeyId, actual.SubjectKeyId)
}
}