test: de-flake internal/metrics timing-sensitive tests - #788
Conversation
The usage-metrics and relay-metrics-collector tests measured real wall-clock sleeps and asserted durations within tight tolerances (e.g. +/-5ms), which fails intermittently under CI scheduling jitter. Inject a controllable time source into environmentMetricUsage and RelayMetricsCollector. Usage activity messages are now stamped with a timestamp when they are handed off, before crossing into the processing goroutine, so tests that advance a fake clock between messages observe exact durations. The timing-sensitive tests now advance a fake clock and assert exact values instead of sleeping. SDK-2839
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e7b550b. Configure here.
| select { | ||
| case <-ticker.C: | ||
| e.flushInternal() | ||
| e.flushInternal(e.now()) |
There was a problem hiding this comment.
Handoff timestamps race with flushes
Low Severity
Activity timestamps are sampled in usageActivityMessage before the send, while ticker flushes sample e.now() after winning select. If a connect/disconnect is stamped and then preempted—or loses select to a ready ticker—flushInternal can reset firstActive to a later time before that message is applied, so lastActive ends up before firstActive and streamingDurationAdjustment goes negative, producing a bad follow-up relayUsage event.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit e7b550b. Configure here.


Ticket: SDK-2839
The
internal/metricstests measured real wall-clock sleeps and asserted durations within tight tolerances, which fails intermittently under CI runner scheduling jitter. From a survey of the last 300 CI runs,TestNonoverlappingStreamsalone failed 5 times across unrelated branches (including v9 push runs), e.g.:TestCapturesSimpleStreamDuration,TestStreamWithoutDisconnect, and theTestRelayMetricsCollectorsubtestthe event start time still shifts when events are not sent(which raced the 1ms flush ticker against a 1ms sleep) have also flaked.Changes
environmentMetricUsagetakes an injectable time source (NewEnvironmentMetricUsagebehavior is unchanged; it usestime.Now). Usage activity, flush, and shutdown messages are stamped with a timestamp when they are handed off — before crossing into the processing goroutine — so a test that advances a fake clock between messages observes exact durations with no race against the processing goroutine.RelayMetricsCollectorlikewise takes an injectable time source vianewRelayMetricsCollectorWithTimeSource.fakeClocktest helper instead of sleeping, and assert exact durations (assert.Equal(int64(40), ...)) instead ofInDeltaftolerances.For the production code path the only behavioral difference is that a usage message's timestamp is captured at hand-off rather than when the processing goroutine dequeues it — if anything slightly more accurate, since it no longer includes queueing delay.
Verification
go test -race -count=3 ./internal/metrics/green.-racewhile the machine is under full CPU load (previously the InDelta assertions failed readily under contention).Note
Low Risk
Changes are mostly test infrastructure; production usage timestamps move slightly earlier (at enqueue), which is a minor reporting nuance, not security- or data-critical logic.
Overview
Stabilizes
internal/metricstests that previously slept on the wall clock and used loose duration tolerances, which flaked under CI scheduling.Relay metrics:
RelayMetricsCollectornow uses an injectablenow(production still usestime.NowvianewRelayMetricsCollectorWithTimeSource). The interval-shift subtest drives an empty flush with afakeClockand asserts exactStartDate/EndDateinstead of racing the background ticker.Usage metrics:
environmentMetricUsageaccepts the same injectable clock in tests. Activity, flush, and shutdown messages carry a timestamp stamped when the message is handed to the channel (before the processing goroutine runs);flushInternaltakes that time instead of callingtime.Nowinside the goroutine. Stream-duration tests advancefakeClockand useassert.EqualonTotalStreamMsinstead ofInDeltaf.A shared
fakeClocktest helper replaces real sleeps in the converted tests.Reviewed by Cursor Bugbot for commit e7b550b. Bugbot is set up for automated code reviews on this repo. Configure here.