Author SHA1 Message Date
clawbot 0d4cf45c96 Serve the delivery duration histogram from boot (closes #267)
check / check (push) Waiting to run
webhooker_delivery_duration_seconds was absent from /metrics until the first delivery, while every other delivery series is materialised at zero when the collectors are registered. initSeries now materialises the histogram too, for the same four target types, so a scrape of an instance that has delivered nothing shows it with a zero count and sum. The registration test lists it with the other series, and a new route test scrapes /metrics on a freshly built instance and finds it for every target type.

Model: opus-5-5
2026-10-02 08:38:42 +00:00
8 changed files with 33 additions and 42 deletions
-12
View File
@@ -124,11 +124,6 @@ func testEnvironmentConfigSuccess(
app := fxtest.New(
t,
// fx's own log is discarded, not sent to t.Logf: a hook still
// running after a start or stop timeout would write there after
// the test has returned. The same holds for every fxtest.New
// below.
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -277,7 +272,6 @@ func testRetentionSweepIntervalSuccess(
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -370,7 +364,6 @@ func testSessionIdleTimeoutSuccess(
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -411,7 +404,6 @@ func TestDefaultDataDir(t *testing.T) {
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -542,7 +534,6 @@ func testReceiverRateLimitSuccess(
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -659,7 +650,6 @@ func testTrustedProxiesSuccess(
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -773,7 +763,6 @@ func testAllowedEgressCIDRsSuccess(
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -1017,7 +1006,6 @@ func assertMetricsAuthAccepted(t *testing.T, expectAuth bool) {
app := fxtest.New(
t,
fx.NopLogger,
fx.Provide(globals.New, logger.New, config.New),
fx.Populate(&cfg),
)
-4
View File
@@ -137,10 +137,6 @@ func bootAtDebug(t *testing.T, dataDir string) string {
app := fxtest.New(
t,
// fx's own log is discarded, not sent to t.Logf: a hook still
// running after a start or stop timeout would write there after
// the test has returned.
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
-9
View File
@@ -78,11 +78,6 @@ func (r *recordingEvictor) Evicted() []string {
return out
}
// newTestApp builds the handlers with their real dependencies. Its
// RequireStart fails the test when starting takes longer than fx's
// default start timeout of 15s. That limit catches a start that hangs,
// not a busy host: measured with make test on 2026-10-02 at host load
// 40-52 on 48 cores, the slowest of this package's 182 starts took 0.19s.
func newTestApp(
t *testing.T,
targets ...any,
@@ -91,10 +86,6 @@ func newTestApp(
return fxtest.New(
t,
// fx's own log is discarded, not sent to t.Logf: a hook still
// running after a start or stop timeout would write there after
// the test has returned.
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
+1
View File
@@ -384,6 +384,7 @@ func (s *Set) initSeries() {
s.deliveriesFailed.WithLabelValues(label)
s.deliveryRetries.WithLabelValues(label)
s.deliveryReplays.WithLabelValues(label)
s.deliveryDuration.WithLabelValues(label)
s.deliveriesPending.WithLabelValues(label)
s.deliveriesRetrying.WithLabelValues(label)
s.circuitBreakersOpen.WithLabelValues(label)
+1
View File
@@ -167,6 +167,7 @@ func TestKnownSeriesExistBeforeAnyDelivery(t *testing.T) {
"webhooker_deliveries_succeeded_total",
"webhooker_deliveries_failed_total",
"webhooker_delivery_retries_total",
"webhooker_delivery_duration_seconds",
"webhooker_circuit_breakers_open",
} {
assert.ElementsMatch(t,
-4
View File
@@ -152,10 +152,6 @@ func newServerApp(
app := fxtest.New(
t,
// fx's own log is discarded, not sent to t.Logf: a hook still
// running after a start or stop timeout would write there after
// the test has returned.
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
+28 -4
View File
@@ -104,10 +104,6 @@ func newTestEnvWithConfig(
app := fxtest.New(
t,
// fx's own log is discarded, not sent to t.Logf: a hook still
// running after a start or stop timeout would write there after
// the test has returned.
fx.NopLogger,
fx.Provide(
globals.New,
logger.New,
@@ -1580,3 +1576,31 @@ func TestTwoMetricsRoutersInOneProcess(t *testing.T) {
}
}
}
// TestMetricsScrapeBeforeAnyDelivery pins
// https://git.eeqj.de/sneak/webhooker/issues/267: an instance that
// has delivered nothing must still serve the delivery duration
// histogram, at zero, for every target type.
func TestMetricsScrapeBeforeAnyDelivery(t *testing.T) {
t.Parallel()
env := newTestEnvWithConfig(
t, metricsConfig(t, metricsUser, metricsAuthValue),
)
scrape := env.metricsRequest(metricsUser, metricsAuthValue)
require.Equal(t, http.StatusOK, scrape.Code)
for _, targetType := range []database.TargetType{
database.TargetTypeHTTP,
database.TargetTypeDatabase,
database.TargetTypeLog,
database.TargetTypeSlack,
} {
assert.Contains(
t, scrape.Body.String(),
`webhooker_delivery_duration_seconds_count{target_type="`+
string(targetType)+`"} 0`,
)
}
}
+3 -9
View File
@@ -2,10 +2,9 @@
# script/test: run the test suite.
#
# -timeout is applied by `go test` per package, not to the run as a whole, so
# it only has to clear the slowest single package. When this budget was set
# that was internal/handlers, measured in a cache-defeated builder stage on the
# 48-core shared build host (2026-08-18); load- and host-dependent, not
# invariants:
# it only has to clear the slowest single package. That is internal/handlers,
# measured in a cache-defeated builder stage on the 48-core shared build host
# (2026-08-18); load- and host-dependent, not invariants:
#
# 16.9s host load 5-20, GOMAXPROCS 48
# 45.9s / 47.3s / 49.0s three runs at deliberate host load 31-73
@@ -24,11 +23,6 @@
# a condition CI runs under. If a CPU-limited runner ever puts a real run near
# 67s, that is the datum to revisit the org figure with.
#
# Those figures predate tests hashing the admin password at 1 MB instead of
# 64 MB (https://git.eeqj.de/sneak/webhooker/pulls/404). After that change, in
# a cache-defeated build at host load 41-55 (2026-10-02), internal/handlers
# took 10.0s and the slowest package was internal/database at 16.2s.
#
# -p 4 -parallel 8 keep the run under 2 GB of memory: at most four test
# binaries build or run at once, each with at most eight parallel tests. Under
# -race every test binary and every link costs a few hundred MB, so the