mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:01:32 +00:00
7cb453a336
Mechanical reformat. The new 'gofmt drift' CI step (added in
ci-pipeline-cleanup Phase 4, commit 0f205a8) surfaced 111 files
with accumulated gofmt drift across cmd/, internal/, and deploy/test/.
Each file's diff is gofmt-standard: whitespace adjustments, intra-
group import sorting (alphabetical by import path within blank-line-
separated groups), and struct-tag column alignment. No semantic
changes — verified via 'git diff --ignore-all-space' which shows only
the line-position deltas from import reordering.
The gate stays in place after this commit. Going forward it catches
gofmt drift at PR time.
252 lines
7.2 KiB
Go
252 lines
7.2 KiB
Go
package acme
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// TestComputeARICertID_InvalidPEM_Input tests the ARI certificate ID computation with invalid PEM.
|
|
func TestComputeARICertID_InvalidPEM_Input(t *testing.T) {
|
|
// Test with invalid PEM data
|
|
_, err := computeARICertID("not a valid pem")
|
|
if err == nil {
|
|
t.Error("expected error for invalid PEM")
|
|
}
|
|
}
|
|
|
|
func TestConstructARIURLFallback_LetsEncrypt(t *testing.T) {
|
|
directoryURL := "https://acme-v02.api.letsencrypt.org/directory"
|
|
certID := "abc123"
|
|
|
|
url := constructARIURLFallback(directoryURL, certID)
|
|
|
|
expected := "https://acme-v02.api.letsencrypt.org/renewalInfo/abc123"
|
|
if url != expected {
|
|
t.Errorf("constructARIURLFallback: expected %s, got %s", expected, url)
|
|
}
|
|
}
|
|
|
|
func TestConstructARIURLFallback_NoDirectory(t *testing.T) {
|
|
directoryURL := "https://example.com/acme"
|
|
certID := "xyz789"
|
|
|
|
url := constructARIURLFallback(directoryURL, certID)
|
|
|
|
expected := "https://example.com/acme/renewalInfo/xyz789"
|
|
if url != expected {
|
|
t.Errorf("constructARIURLFallback: expected %s, got %s", expected, url)
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_Disabled tests that ARI returns nil when disabled.
|
|
func TestGetRenewalInfo_Disabled(t *testing.T) {
|
|
config := &Config{
|
|
DirectoryURL: "https://acme.invalid/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: false,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
result, err := connector.GetRenewalInfo(ctx, "any-cert-pem")
|
|
if err != nil {
|
|
t.Fatalf("GetRenewalInfo failed: %v", err)
|
|
}
|
|
|
|
if result != nil {
|
|
t.Error("GetRenewalInfo should return nil when ARI is disabled")
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_NotFound tests handling of 404 response (CA doesn't support ARI).
|
|
func TestGetRenewalInfo_NotFound(t *testing.T) {
|
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Mock directory endpoint
|
|
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"newOrder": "/acme/new-order",
|
|
"newAccount": "/acme/new-account",
|
|
})
|
|
return
|
|
}
|
|
|
|
// All other endpoints return 404
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
}))
|
|
defer mockServer.Close()
|
|
|
|
config := &Config{
|
|
DirectoryURL: mockServer.URL + "/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: true,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
// GetRenewalInfo will fail when parsing the cert PEM, which is expected
|
|
result, err := connector.GetRenewalInfo(ctx, "invalid-cert-pem")
|
|
if err == nil {
|
|
// If it doesn't fail on cert parsing, that's also okay
|
|
// The 404 handling happens after cert ID computation
|
|
if result != nil {
|
|
t.Error("GetRenewalInfo should return nil for 404 response")
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_ServerError tests handling of server errors.
|
|
func TestGetRenewalInfo_ServerError(t *testing.T) {
|
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Mock directory endpoint
|
|
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"newOrder": "/acme/new-order",
|
|
"newAccount": "/acme/new-account",
|
|
})
|
|
return
|
|
}
|
|
|
|
// All other endpoints return 500
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
}))
|
|
defer mockServer.Close()
|
|
|
|
config := &Config{
|
|
DirectoryURL: mockServer.URL + "/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: true,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := connector.GetRenewalInfo(ctx, "invalid-cert-pem")
|
|
// Error is expected because cert parsing fails first
|
|
if err == nil {
|
|
// If we get here, the server error handling should catch it
|
|
t.Error("expected error for invalid cert or 500 response")
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_InvalidPEM tests handling of invalid PEM input.
|
|
func TestGetRenewalInfo_InvalidPEM(t *testing.T) {
|
|
config := &Config{
|
|
DirectoryURL: "https://acme.invalid/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: true,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := connector.GetRenewalInfo(ctx, "invalid pem data")
|
|
if err == nil {
|
|
t.Error("GetRenewalInfo should return error for invalid PEM")
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_MalformedResponse tests handling of malformed JSON response.
|
|
func TestGetRenewalInfo_MalformedResponse(t *testing.T) {
|
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Mock directory endpoint
|
|
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"renewalInfo": "/acme/renewalInfo",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Mock renewalInfo with malformed JSON
|
|
if r.URL.Path != "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte(`{"suggestedWindow": invalid json}`))
|
|
return
|
|
}
|
|
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
}))
|
|
defer mockServer.Close()
|
|
|
|
config := &Config{
|
|
DirectoryURL: mockServer.URL + "/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: true,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := connector.GetRenewalInfo(ctx, "invalid-cert-pem")
|
|
// Error is expected
|
|
if err == nil {
|
|
t.Error("GetRenewalInfo should return error for malformed response or invalid cert")
|
|
}
|
|
}
|
|
|
|
// TestGetRenewalInfo_MissingWindow tests handling of missing suggestedWindow.
|
|
func TestGetRenewalInfo_MissingWindow(t *testing.T) {
|
|
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Mock directory endpoint
|
|
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"renewalInfo": "/acme/renewalInfo",
|
|
})
|
|
return
|
|
}
|
|
|
|
// Mock renewalInfo without suggestedWindow
|
|
if r.URL.Path != "/directory" && r.Method == http.MethodGet {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]interface{}{})
|
|
return
|
|
}
|
|
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
}))
|
|
defer mockServer.Close()
|
|
|
|
config := &Config{
|
|
DirectoryURL: mockServer.URL + "/directory",
|
|
Email: "test@example.com",
|
|
ChallengeType: "http-01",
|
|
ARIEnabled: true,
|
|
}
|
|
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
connector := New(config, logger)
|
|
|
|
ctx := context.Background()
|
|
|
|
_, err := connector.GetRenewalInfo(ctx, "invalid-cert-pem")
|
|
// Error is expected due to invalid cert PEM
|
|
if err == nil {
|
|
t.Error("expected error for invalid cert or missing window")
|
|
}
|
|
}
|