@@ -98,4 +98,87 @@ describe('nodejs.eventloop.utilization', function () {
9898 'expected one data point'
9999 ) ;
100100 } ) ;
101+
102+ it ( 'should correctly calculate utilization deltas across multiple measurements' , async function ( ) {
103+ // This test ensures the bug where delta of deltas was observed instead of deltas of absolute values
104+ // does not regress. See https://github.com/open-telemetry/opentelemetry-js-contrib/pull/3118
105+ // This bug would surface on the third callback invocation.
106+
107+ const instrumentation = new RuntimeNodeInstrumentation ( { } ) ;
108+ instrumentation . setMeterProvider ( meterProvider ) ;
109+
110+ // Helper function to create blocking work that results in high utilization
111+ const createBlockingWork = ( durationMs : number ) => {
112+ const start = Date . now ( ) ;
113+ while ( Date . now ( ) - start < durationMs ) {
114+ // Busy wait to block the event loop
115+ }
116+ } ;
117+
118+ // Helper function to collect metrics and extract utilization value
119+ const collectUtilization = async ( ) : Promise < number > => {
120+ const { resourceMetrics } = await metricReader . collect ( ) ;
121+ const scopeMetrics = resourceMetrics . scopeMetrics ;
122+ const utilizationMetric = scopeMetrics [ 0 ] . metrics . find (
123+ x => x . descriptor . name === METRIC_NODEJS_EVENTLOOP_UTILIZATION
124+ ) ;
125+
126+ assert . notEqual ( utilizationMetric , undefined , 'metric not found' ) ;
127+ assert . strictEqual (
128+ utilizationMetric ! . dataPoints . length ,
129+ 1 ,
130+ 'expected one data point'
131+ ) ;
132+
133+ return utilizationMetric ! . dataPoints [ 0 ] . value as number ;
134+ } ;
135+
136+ // Wait for some time to establish baseline utilization
137+ await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
138+
139+ // First collection
140+ const firstUtilization = await collectUtilization ( ) ;
141+ assert . notStrictEqual (
142+ firstUtilization ,
143+ 1 ,
144+ 'Expected utilization in first measurement to be not 1'
145+ ) ;
146+
147+ // Second measurement: Create blocking work and measure
148+ createBlockingWork ( 50 ) ;
149+ const secondUtilization = await collectUtilization ( ) ;
150+ assert . strictEqual (
151+ secondUtilization ,
152+ 1 ,
153+ 'Expected utilization in second measurement to be 1'
154+ ) ;
155+
156+ // Third measurement: Create blocking work again and measure
157+ // This is where the bug would manifest - if we were observing delta of deltas,
158+ // this measurement would not be 1
159+ createBlockingWork ( 50 ) ;
160+ const thirdUtilization = await collectUtilization ( ) ;
161+ assert . strictEqual (
162+ thirdUtilization ,
163+ 1 ,
164+ 'Expected utilization in third measurement to be 1'
165+ ) ;
166+
167+ // Fourth measurement (should be the same as the third measurement, just a sanity check)
168+ createBlockingWork ( 50 ) ;
169+ const fourthUtilization = await collectUtilization ( ) ;
170+ assert . strictEqual (
171+ fourthUtilization ,
172+ 1 ,
173+ 'Expected utilization in fourth measurement to be 1'
174+ ) ;
175+
176+ // Fifth measurement: Do some NON-blocking work (sanity check, should be low)
177+ await new Promise ( resolve => setTimeout ( resolve , 50 ) ) ;
178+ const fifthUtilization = await collectUtilization ( ) ;
179+ assert . ok (
180+ fifthUtilization < 1 ,
181+ `Expected utilization in fifth measurement to be less than 1, but got ${ fifthUtilization } `
182+ ) ;
183+ } ) ;
101184} ) ;
0 commit comments