Skip to content

Commit b5c0140

Browse files
test: cover start() timer fire/reset path to raise patch coverage
TestJitteredDelay only exercised the standalone jitteredDelay() helper. The modified lines inside start() (metricsTimer creation, the metricsTimer.C case, and the Reset call replacing the old Ticker) were still unexercised by any test, which is why the PR's patch coverage gate reported 42.9% against the 80% threshold. Add TestMetricsForwarder_start_timerFiresAndResets, which runs the real start() goroutine with a short sendMetricsInterval and asserts the periodic send fires at least twice - the second fire only happens if metricsTimer.Reset is correctly rescheduling after the first, which is exactly the behavior this fix depends on. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 55322a3 commit b5c0140

1 file changed

Lines changed: 64 additions & 0 deletions

File tree

pkg/controller/utils/datadog/metrics_forwarder_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"testing"
1717
"time"
1818

19+
"github.com/go-logr/logr"
1920
"k8s.io/utils/ptr"
2021

2122
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
@@ -82,6 +83,69 @@ func TestJitteredDelay(t *testing.T) {
8283
}
8384
}
8485

86+
// TestMetricsForwarder_start_timerFiresAndResets exercises the start() goroutine
87+
// loop end-to-end with a very short sendMetricsInterval, and asserts that the
88+
// periodic metrics send fires more than once. Since start() now uses a
89+
// time.Timer that must be explicitly Reset after each fire (instead of a
90+
// self-repeating time.Ticker), a single observed tick wouldn't tell us much,
91+
// but a second tick proves metricsTimer.Reset is correctly rescheduling the
92+
// next send after the previous one completes.
93+
func TestMetricsForwarder_start_timerFiresAndResets(t *testing.T) {
94+
platformInfo := kubernetes.NewPlatformInfoFromVersionMaps(
95+
nil,
96+
map[string]string{},
97+
map[string]string{},
98+
)
99+
100+
f := &fakeMetricsForwarder{}
101+
tickCh := make(chan struct{}, 10)
102+
f.On("delegatedValidateCreds", "").Once()
103+
f.On("delegatedSendEvent", mock.Anything, mock.Anything)
104+
f.On("delegatedSendReconcileMetric", mock.Anything, mock.Anything, mock.Anything).
105+
Run(func(args mock.Arguments) { tickCh <- struct{}{} })
106+
107+
mf := &metricsForwarder{
108+
namespacedName: types.NamespacedName{Namespace: "foo", Name: "bar"},
109+
platformInfo: &platformInfo,
110+
retryInterval: 5 * time.Millisecond,
111+
sendMetricsInterval: 10 * time.Millisecond,
112+
stopChan: make(chan struct{}),
113+
errorChan: make(chan error, 100),
114+
eventChan: make(chan Event, 10),
115+
lastReconcileErr: errInitValue,
116+
logger: logr.Discard(),
117+
delegator: f,
118+
}
119+
120+
var wg sync.WaitGroup
121+
wg.Add(1)
122+
go mf.start(&wg)
123+
124+
timeout := time.After(2 * time.Second)
125+
for i := 0; i < 2; i++ {
126+
select {
127+
case <-tickCh:
128+
case <-timeout:
129+
t.Fatal("timed out waiting for periodic metrics tick(s); metricsTimer may not be firing/resetting")
130+
}
131+
}
132+
133+
mf.stop()
134+
135+
done := make(chan struct{})
136+
go func() {
137+
wg.Wait()
138+
close(done)
139+
}()
140+
select {
141+
case <-done:
142+
case <-time.After(2 * time.Second):
143+
t.Fatal("timed out waiting for start() to return after stop()")
144+
}
145+
146+
f.AssertExpectations(t)
147+
}
148+
85149
func TestMetricsForwarder_updateCredsIfNeeded(t *testing.T) {
86150
tests := []struct {
87151
name string

0 commit comments

Comments
 (0)