Certbot stores wildcard certs under the base domain (e.g., *.example.com → /etc/letsencrypt/live/example.com/), but the path helpers were using the raw wildcard domain. This caused deploy hooks to reference nonexistent paths, silently failing and leaving certs unrenewable.
139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
package certbot
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildHAProxyCert_EmptyCert(t *testing.T) {
|
|
// BuildHAProxyCert requires sudo + real cert paths,
|
|
// so we test the validation logic directly.
|
|
combined := append([]byte{}, []byte{}...)
|
|
if len(combined) != 0 {
|
|
t.Fatal("test setup: expected empty combined")
|
|
}
|
|
// The check we added: len(combined) == 0 → error
|
|
}
|
|
|
|
func TestHAProxyCertPath(t *testing.T) {
|
|
tests := []struct {
|
|
domain string
|
|
want string
|
|
}{
|
|
{"example.com", "/etc/haproxy/certs/example.com.pem"},
|
|
{"*.example.com", "/etc/haproxy/certs/example.com.pem"},
|
|
{"sub.example.com", "/etc/haproxy/certs/sub.example.com.pem"},
|
|
}
|
|
for _, tt := range tests {
|
|
got := HAProxyCertPath(tt.domain)
|
|
if got != tt.want {
|
|
t.Errorf("HAProxyCertPath(%q) = %q, want %q", tt.domain, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCertPaths(t *testing.T) {
|
|
tests := []struct {
|
|
domain string
|
|
wantCert string
|
|
wantKey string
|
|
}{
|
|
{
|
|
"example.com",
|
|
"/etc/letsencrypt/live/example.com/fullchain.pem",
|
|
"/etc/letsencrypt/live/example.com/privkey.pem",
|
|
},
|
|
{
|
|
"*.example.com",
|
|
"/etc/letsencrypt/live/example.com/fullchain.pem",
|
|
"/etc/letsencrypt/live/example.com/privkey.pem",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
certPath := CertPath(tt.domain)
|
|
keyPath := KeyPath(tt.domain)
|
|
if certPath != tt.wantCert {
|
|
t.Errorf("CertPath(%q) = %q, want %q", tt.domain, certPath, tt.wantCert)
|
|
}
|
|
if keyPath != tt.wantKey {
|
|
t.Errorf("KeyPath(%q) = %q, want %q", tt.domain, keyPath, tt.wantKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEnsureCredentials(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
credsPath := filepath.Join(tmp, "subdir", "cloudflare.ini")
|
|
m := NewManager(credsPath)
|
|
|
|
if err := m.EnsureCredentials("test-token"); err != nil {
|
|
t.Fatalf("EnsureCredentials: %v", err)
|
|
}
|
|
|
|
data, err := os.ReadFile(credsPath)
|
|
if err != nil {
|
|
t.Fatalf("read credentials: %v", err)
|
|
}
|
|
if string(data) != "dns_cloudflare_api_token = test-token\n" {
|
|
t.Errorf("credentials content = %q", string(data))
|
|
}
|
|
}
|
|
|
|
func TestEnsureCredentials_EmptyToken(t *testing.T) {
|
|
m := NewManager(filepath.Join(t.TempDir(), "test.ini"))
|
|
if err := m.EnsureCredentials(""); err == nil {
|
|
t.Error("expected error for empty token")
|
|
}
|
|
}
|
|
|
|
func TestParseCertOutput(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
output string
|
|
wildcard bool
|
|
issuer string
|
|
}{
|
|
{
|
|
name: "wildcard cert (no spaces around =)",
|
|
output: "subject=CN=*.cloud.payne.io\nnotAfter=Sep 4 23:22:30 2026 GMT\nissuer=C=US, O=Let's Encrypt, CN=YR1\n",
|
|
wildcard: true,
|
|
issuer: "C=US, O=Let's Encrypt, CN=YR1",
|
|
},
|
|
{
|
|
name: "wildcard cert (spaces around =)",
|
|
output: "subject=CN = *.example.com\nnotAfter=Jan 15 12:00:00 2027 GMT\nissuer=CN=R3\n",
|
|
wildcard: true,
|
|
issuer: "CN=R3",
|
|
},
|
|
{
|
|
name: "single domain cert",
|
|
output: "subject=CN=example.com\nnotAfter=Jan 15 12:00:00 2027 GMT\nissuer=CN=R3\n",
|
|
wildcard: false,
|
|
issuer: "CN=R3",
|
|
},
|
|
{
|
|
name: "single domain with subdomain",
|
|
output: "subject=CN=app.example.com\nnotAfter=Jan 15 12:00:00 2027 GMT\nissuer=CN=R3\n",
|
|
wildcard: false,
|
|
issuer: "CN=R3",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
status := &CertStatus{}
|
|
parseCertOutput(tt.output, status)
|
|
if status.Wildcard != tt.wildcard {
|
|
t.Errorf("Wildcard = %v, want %v", status.Wildcard, tt.wildcard)
|
|
}
|
|
if status.IssuerCN != tt.issuer {
|
|
t.Errorf("IssuerCN = %q, want %q", status.IssuerCN, tt.issuer)
|
|
}
|
|
if status.Expiry.IsZero() {
|
|
t.Error("Expiry should be parsed")
|
|
}
|
|
})
|
|
}
|
|
}
|