@marcel91200 / caddy Mirror · read-only / commits / 3adcafd4
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 14:45 UTC

admin: Fix tests locally, properly isolate storage (#7486)

* admin: Fix tests locally, properly isolate storage * Fix flaky pki_test * Drop testdata dir logic * Safer temp dir * Test handlers without a full server

Francis Lavoie committed Feb 17, 2026 at 15:14 UTC 3adcafd4c1120f071e1c1b1407566780144e7fc9
3 files changed +60 -12
admin.go
+18 -1
@@ -47,6 +47,12 @@ import (
47 "go.uber.org/zap/zapcore"
48 )
49
50 +// testCertMagicStorageOverride is a package-level test hook. Tests may set
51 +// this variable to provide a temporary certmagic.Storage so that cert
52 +// management in tests does not hit the real default storage on disk.
53 +// This must NOT be set in production code.
54 +var testCertMagicStorageOverride certmagic.Storage
55 +
56 func init() {
57 // The hard-coded default `DefaultAdminListen` can be overridden
58 // by setting the `CADDY_ADMIN` environment variable.
@@ -633,8 +639,19 @@ func (ident *IdentityConfig) certmagicConfig(logger *zap.Logger, makeCache bool)
639 // certmagic config, although it'll be mostly useless for remote management
640 ident = new(IdentityConfig)
641 }
642 + // Choose storage: prefer the package-level test override when present,
643 + // otherwise use the configured DefaultStorage. Tests may set an override
644 + // to divert storage into a temporary location. Otherwise, in production
645 + // we use the DefaultStorage since we don't want to act as part of a
646 + // cluster; this storage is for the server's local identity only.
647 + var storage certmagic.Storage
648 + if testCertMagicStorageOverride != nil {
649 + storage = testCertMagicStorageOverride
650 + } else {
651 + storage = DefaultStorage
652 + }
653 template := certmagic.Config{
637 - Storage: DefaultStorage, // do not act as part of a cluster (this is for the server's local identity)
654 + Storage: storage,
655 Logger: logger,
656 Issuers: ident.issuers,
657 }
admin_test.go
+40 -9
@@ -22,9 +22,11 @@ import (
22 "maps"
23 "net/http"
24 "net/http/httptest"
25 + "os"
26 "reflect"
27 "sync"
28 "testing"
29 + "time"
30
31 "github.com/caddyserver/certmagic"
32 "github.com/prometheus/client_golang/prometheus"
@@ -275,13 +277,12 @@ func TestAdminHandlerBuiltinRouteErrors(t *testing.T) {
277 },
278 }
279
278 - err := replaceLocalAdminServer(cfg, Context{})
280 + // Build the admin handler directly (no listener active)
281 + addr, err := ParseNetworkAddress("localhost:2019")
282 if err != nil {
280 - t.Fatalf("setting up admin server: %v", err)
283 + t.Fatalf("Failed to parse address: %v", err)
284 }
282 - defer func() {
283 - stopAdminServer(localAdminServer)
284 - }()
285 + handler := cfg.Admin.newAdminHandler(addr, false, Context{})
286
287 tests := []struct {
288 name string
@@ -314,7 +315,7 @@ func TestAdminHandlerBuiltinRouteErrors(t *testing.T) {
315 req := httptest.NewRequest(test.method, fmt.Sprintf("http://localhost:2019%s", test.path), nil)
316 rr := httptest.NewRecorder()
317
317 - localAdminServer.Handler.ServeHTTP(rr, req)
318 + handler.ServeHTTP(rr, req)
319
320 if rr.Code != test.expectedStatus {
321 t.Errorf("expected status %d but got %d", test.expectedStatus, rr.Code)
@@ -799,8 +800,24 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
800 ...
801 -----END PRIVATE KEY-----`)
802
802 - testStorage := certmagic.FileStorage{Path: t.TempDir()}
803 - err := testStorage.Store(context.Background(), "localhost/localhost.crt", certPEM)
803 + tmpDir, err := os.MkdirTemp("", "TestManageIdentity-")
804 + if err != nil {
805 + t.Fatal(err)
806 + }
807 + testStorage := certmagic.FileStorage{Path: tmpDir}
808 + // Clean up the temp dir after the test finishes. Ensure any background
809 + // certificate maintenance is stopped first to avoid RemoveAll races.
810 + t.Cleanup(func() {
811 + if identityCertCache != nil {
812 + identityCertCache.Stop()
813 + identityCertCache = nil
814 + }
815 + // Give goroutines a moment to exit and release file handles.
816 + time.Sleep(50 * time.Millisecond)
817 + _ = os.RemoveAll(tmpDir)
818 + })
819 +
820 + err = testStorage.Store(context.Background(), "localhost/localhost.crt", certPEM)
821 if err != nil {
822 t.Fatal(err)
823 }
@@ -862,7 +879,7 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
879 },
880 },
881 },
865 - storage: &certmagic.FileStorage{Path: "testdata"},
882 + storage: &testStorage,
883 },
884 checkState: func(t *testing.T, cfg *Config) {
885 if len(cfg.Admin.Identity.issuers) != 1 {
@@ -900,6 +917,13 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
917 identityCertCache.Stop()
918 identityCertCache = nil
919 }
920 + // Ensure any cache started by manageIdentity is stopped at the end
921 + defer func() {
922 + if identityCertCache != nil {
923 + identityCertCache.Stop()
924 + identityCertCache = nil
925 + }
926 + }()
927
928 ctx := Context{
929 Context: context.Background(),
@@ -907,6 +931,13 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
931 moduleInstances: make(map[string][]Module),
932 }
933
934 + // If this test provided a FileStorage, set the package-level
935 + // testCertMagicStorageOverride so certmagicConfig will use it.
936 + if test.cfg != nil && test.cfg.storage != nil {
937 + testCertMagicStorageOverride = test.cfg.storage
938 + defer func() { testCertMagicStorageOverride = nil }()
939 + }
940 +
941 err := manageIdentity(ctx, test.cfg)
942
943 if test.wantErr {
caddytest/integration/pki_test.go
+2 -2
@@ -53,7 +53,7 @@ func TestLeafCertLifetimeLessThanIntermediate(t *testing.T) {
53 }
54 }
55 }
56 - `, "json", "certificate lifetime (168h0m0s) should be less than intermediate certificate lifetime (168h0m0s)")
56 + `, "json", "should be less than intermediate certificate lifetime")
57 }
58
59 func TestIntermediateLifetimeLessThanRoot(t *testing.T) {
@@ -103,5 +103,5 @@ func TestIntermediateLifetimeLessThanRoot(t *testing.T) {
103 }
104 }
105 }
106 - `, "json", "intermediate certificate lifetime must be less than root certificate lifetime (86400h0m0s)")
106 + `, "json", "intermediate certificate lifetime must be less than actual root certificate lifetime")
107 }