Ctrl K
ComunidadGuías…

internal/caddylogs/hub_ari_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: Apache-2.0

package caddylogs

import (
	"path/filepath"
	"testing"

	appdb "github.com/X4Applegate/caddyui/internal/db"
	"github.com/X4Applegate/caddyui/internal/models"
)

// Issue #151: a failed ACME renewal-information (ARI) update left the
// certificate stuck on "Issuance error" forever. Caddy recovers by logging a
// successful ARI update, which issues no certificate and therefore emits no
// cert_obtained event, so nothing cleared the stored failure.
func TestHubClearsErrorAfterSuccessfulRenewalInfoUpdate(t *testing.T) {
	conn, err := appdb.Open(filepath.Join(t.TempDir(), "caddyui.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = conn.Close() })

	hub := New(conn)
	// The reporter's failure: a transient DNS outage while updating ARI.
	hub.AcceptLine([]byte(`{"level":"error","ts":1760000000,"logger":"tls.cache.maintenance","msg":"failed updating renewal info from ACME CA","identifiers":["*.example.com"],"error":"provisioning client: performing request: Get \"https://acme-v02.api.letsencrypt.org/directory\": dial tcp: lookup acme-v02.api.letsencrypt.org on 127.0.0.11:53: server misbehaving","caddyui_server_id":3,"caddyui_server_name":"edge"}`))
	states, err := models.ListCertificateLifecycle(conn, 3)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "error" || states[0].Identifier != "*.example.com" {
		t.Fatalf("ARI failure state = %#v, want error for *.example.com", states)
	}

	// Two weeks later Caddy updates the renewal information successfully. The
	// certificate is valid and served, so the stored error must clear.
	hub.AcceptLine([]byte(`{"level":"info","ts":1761400000,"logger":"tls.cache.maintenance","msg":"updated and stored ACME renewal information","identifiers":["*.example.com"],"caddyui_server_id":3,"caddyui_server_name":"edge"}`))
	states, err = models.ListCertificateLifecycle(conn, 3)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 {
		t.Fatalf("lifecycle rows = %#v, want one compact row", states)
	}
	if states[0].Phase != "active" {
		t.Fatalf("phase after successful ARI update = %q, want active", states[0].Phase)
	}
	if states[0].Error != "" {
		t.Fatalf("stale issuance error survived recovery: %q", states[0].Error)
	}
}

// A scheduled ARI refresh is routine maintenance on a healthy certificate. It
// must not be reported to the operator as an in-flight renewal.
func TestHubDoesNotTreatRenewalInfoCheckAsRenewing(t *testing.T) {
	conn, err := appdb.Open(filepath.Join(t.TempDir(), "caddyui.db"))
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = conn.Close() })

	hub := New(conn)
	hub.AcceptLine([]byte(`{"level":"info","ts":1761400001,"logger":"tls.cache.maintenance","msg":"certificate expires soon; renewing","identifiers":["renew.example"],"caddyui_server_id":4,"caddyui_server_name":"edge"}`))
	states, err := models.ListCertificateLifecycle(conn, 4)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "renewing" {
		t.Fatalf("renewal state = %#v, want renewing", states)
	}
}