All checks were successful
check / check (push) Successful in 3m45s
fx hands OnStop a context carrying the application's stop timeout, and the delivery engine, the retention reaper, and the archive sweeper all discarded it and called wg.Wait() bare. A worker wedged inside a delivery target that never returns, or a sweep blocked on a locked SQLite database, hung the process forever instead of letting it exit when the timeout expired. All three now wait through internal/lifecycle.WaitForShutdown, which selects the drained WaitGroup against the stop context and, on timeout, logs at error naming the component and returns an error rather than reporting a clean stop. Engine.stop also gains the cancel != nil guard its two mirrored components already had.
63 lines
1.2 KiB
Go
63 lines
1.2 KiB
Go
package lifecycle_test
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/webhooker/internal/lifecycle"
|
|
)
|
|
|
|
// waitTimeout is the stop budget the timeout case gives a
|
|
// goroutine that never returns. The test's own patience is the
|
|
// go test deadline, so the only thing this value affects is how
|
|
// long the case takes.
|
|
const waitTimeout = 100 * time.Millisecond
|
|
|
|
func discardLogger() *slog.Logger {
|
|
return slog.New(slog.DiscardHandler)
|
|
}
|
|
|
|
func TestWaitForShutdown_DrainedGroup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Go(func() {})
|
|
|
|
require.NoError(
|
|
t,
|
|
lifecycle.WaitForShutdown(
|
|
context.Background(), discardLogger(),
|
|
"test component", &wg,
|
|
),
|
|
)
|
|
}
|
|
|
|
func TestWaitForShutdown_ContextExpires(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
release := make(chan struct{})
|
|
|
|
t.Cleanup(func() { close(release) })
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Go(func() { <-release })
|
|
|
|
ctx, cancel := context.WithTimeout(
|
|
context.Background(), waitTimeout,
|
|
)
|
|
defer cancel()
|
|
|
|
err := lifecycle.WaitForShutdown(
|
|
ctx, discardLogger(), "test component", &wg,
|
|
)
|
|
|
|
require.ErrorIs(t, err, context.DeadlineExceeded)
|
|
require.ErrorContains(t, err, "test component")
|
|
}
|