Ctrl K
ComunidadGuías…

internal/caddylogs/hub_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"
)

func TestHubPersistsCertificateLifecycleAndKeepsRuntimeLogsInMemory(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":1700000000,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"example.test","caddyui_server_id":2,"caddyui_server_name":"edge"}`))
	states, err := models.ListCertificateLifecycle(conn, 2)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "obtaining" || states[0].Identifier != "example.test" {
		t.Fatalf("states = %#v, want obtaining example.test", states)
	}

	// CertMagic's retry line often omits identifier and embeds it at the
	// beginning of the error. The projection must retain that failure.
	hub.AcceptLine([]byte(`{"level":"error","ts":1700000001,"logger":"tls.obtain","msg":"will retry","error":"[example.test] Obtain: DNS challenge failed","attempt":1,"retrying_in":60,"caddyui_server_id":2,"caddyui_server_name":"edge"}`))
	states, err = models.ListCertificateLifecycle(conn, 2)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "retrying" || states[0].Error == "" {
		t.Fatalf("retry state = %#v", states)
	}

	// A delayed line from another connection must not overwrite a newer
	// lifecycle projection that has already reached the UI.
	hub.AcceptLine([]byte(`{"level":"info","ts":1699999999,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"example.test","caddyui_server_id":2,"caddyui_server_name":"edge"}`))
	states, err = models.ListCertificateLifecycle(conn, 2)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "retrying" {
		t.Fatalf("stale event replaced newer lifecycle state: %#v", states)
	}
	hub.AcceptLine([]byte(`{"level":"info","ts":1700000002,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"example.test","caddyui_server_id":2,"caddyui_server_name":"edge"}`))
	states, err = models.ListCertificateLifecycle(conn, 2)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "active" || states[0].Error != "" {
		t.Fatalf("success state = %#v, want active without an error", states)
	}
	// tls.obtain emits cleanup lines after success. Logger namespace alone
	// must not regress the certificate back to Obtaining.
	hub.AcceptLine([]byte(`{"level":"info","ts":1700000003,"logger":"tls.obtain","msg":"releasing lock","identifier":"example.test","caddyui_server_id":2,"caddyui_server_name":"edge"}`))
	states, err = models.ListCertificateLifecycle(conn, 2)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Phase != "active" {
		t.Fatalf("post-success cleanup regressed lifecycle state: %#v", states)
	}

	entries := hub.Since(0, 2, 20)
	if len(entries) != 5 {
		t.Fatalf("runtime entries = %d, want 5", len(entries))
	}
	var count int
	if err := conn.QueryRow(`SELECT COUNT(*) FROM certificate_lifecycle`).Scan(&count); err != nil {
		t.Fatal(err)
	}
	if count != 1 {
		t.Fatalf("persisted lifecycle rows = %d, want compact single row", count)
	}
}

func TestHubPersistsStructuredCertObtainedEvent(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":"debug","ts":1700000004,"logger":"events","msg":"event","name":"config_loaded","caddyui_stream":"certificates","caddyui_server_id":7,"caddyui_server_name":"west"}`))
	if entries := hub.Since(0, 7, 20); len(entries) != 0 {
		t.Fatalf("unrelated persistent event leaked into runtime ring: %#v", entries)
	}
	hub.AcceptLine([]byte(`{"level":"debug","ts":1700000005,"logger":"events","msg":"event","name":"cert_obtained","data":{"identifier":"event.example","issuer":"acme.example","renewal":false},"caddyui_stream":"certificates","caddyui_server_id":7,"caddyui_server_name":"west"}`))
	states, err := models.ListCertificateLifecycle(conn, 7)
	if err != nil {
		t.Fatal(err)
	}
	if len(states) != 1 || states[0].Identifier != "event.example" || states[0].Phase != "active" {
		t.Fatalf("cert_obtained state = %#v, want active event.example", states)
	}
	entries := hub.Since(0, 7, 20)
	if len(entries) != 1 || entries[0].Logger != "events" {
		t.Fatalf("certificate event entries = %#v", entries)
	}
	if _, leaked := entries[0].Fields["caddyui_stream"]; leaked {
		t.Fatalf("internal stream marker leaked into displayed fields: %#v", entries[0].Fields)
	}
}

func TestHubDropsAccessLogsFromRuntimeRing(t *testing.T) {
	hub := New(nil)
	hub.AcceptLine([]byte(`{"level":"info","logger":"http.log.access.caddyui_access","msg":"handled request","request":{"host":"example.test"}}`))
	if entries := hub.Since(0, 0, 20); len(entries) != 0 {
		t.Fatalf("access events leaked into runtime ring: %#v", entries)
	}
}