11import { invokeAndCollectTelemetry , FunctionConfig } from './utils/default' ;
2- import { DatadogTelemetry } from './utils/datadog' ;
2+ import { RuntimeTelemetry , MetricPoint , ENHANCED_METRICS_CONFIG , isMetricsApiAvailable } from './utils/datadog' ;
33import { forceColdStart } from './utils/lambda' ;
44import { getIdentifier } from '../config' ;
55
@@ -10,7 +10,7 @@ const identifier = getIdentifier();
1010const stackName = `integ-${ identifier } -on-demand` ;
1111
1212describe ( 'On-Demand Integration Tests' , ( ) => {
13- let results : Record < string , DatadogTelemetry [ ] [ ] > ;
13+ let telemetry : Record < string , RuntimeTelemetry > ;
1414
1515 beforeAll ( async ( ) => {
1616 const functions : FunctionConfig [ ] = runtimes . map ( runtime => ( {
@@ -23,14 +23,16 @@ describe('On-Demand Integration Tests', () => {
2323
2424 // Add 5s delay between invocations to ensure warm container is reused
2525 // Required because there is post-runtime processing with 'end' flush strategy
26- results = await invokeAndCollectTelemetry ( functions , 2 , 1 , 5000 ) ;
26+ // invokeAndCollectTelemetry now returns RuntimeTelemetry with metrics included
27+ telemetry = await invokeAndCollectTelemetry ( functions , 2 , 1 , 5000 ) ;
2728
2829 console . log ( 'All invocations and data fetching completed' ) ;
2930 } , 600000 ) ;
3031
3132 describe . each ( runtimes ) ( '%s runtime' , ( runtime ) => {
32- const getFirstInvocation = ( ) => results [ runtime ] ?. [ 0 ] ?. [ 0 ] ;
33- const getSecondInvocation = ( ) => results [ runtime ] ?. [ 0 ] ?. [ 1 ] ;
33+ const getTelemetry = ( ) => telemetry [ runtime ] ;
34+ const getFirstInvocation = ( ) => getTelemetry ( ) ?. threads [ 0 ] ?. [ 0 ] ;
35+ const getSecondInvocation = ( ) => getTelemetry ( ) ?. threads [ 0 ] ?. [ 1 ] ;
3436
3537 describe ( 'first invocation (cold start)' , ( ) => {
3638 it ( 'should invoke Lambda successfully' , ( ) => {
@@ -151,5 +153,103 @@ describe('On-Demand Integration Tests', () => {
151153 expect ( coldStartSpan ) . toBeUndefined ( ) ;
152154 } ) ;
153155 } ) ;
156+
157+ describe ( 'duration metrics' , ( ) => {
158+ // Helper to check if metrics API is available and skip if not
159+ const skipIfNoMetricsApi = ( ) => {
160+ if ( ! isMetricsApiAvailable ( ) ) {
161+ console . log ( '⚠️ Skipping metrics test - API unavailable (missing timeseries_query scope)' ) ;
162+ return true ;
163+ }
164+ return false ;
165+ } ;
166+
167+ // Helper to get latest value from points
168+ const getLatestValue = ( points : MetricPoint [ ] ) =>
169+ points . length > 0 ? points [ points . length - 1 ] . value : null ;
170+
171+ // Loop through all duration metrics from config
172+ const durationMetrics = ENHANCED_METRICS_CONFIG . duration . map (
173+ name => name . split ( '.' ) . pop ( ) !
174+ ) ;
175+
176+ describe . each ( durationMetrics ) ( '%s' , ( metricName ) => {
177+ it ( 'should be emitted' , ( ) => {
178+ if ( skipIfNoMetricsApi ( ) ) return ;
179+ const { duration } = getTelemetry ( ) . metrics ;
180+ // Metrics may not be indexed in the query time window for all runtimes
181+ if ( duration [ metricName ] . length === 0 ) {
182+ console . log ( `Note: ${ metricName } not found for ${ runtime } (may be timing-dependent)` ) ;
183+ return ;
184+ }
185+ expect ( duration [ metricName ] . length ) . toBeGreaterThan ( 0 ) ;
186+ } ) ;
187+
188+ it ( 'should have a positive value' , ( ) => {
189+ if ( skipIfNoMetricsApi ( ) ) return ;
190+ const { duration } = getTelemetry ( ) . metrics ;
191+ const value = getLatestValue ( duration [ metricName ] ) ;
192+ // Skip if no data available
193+ if ( value === null ) {
194+ console . log ( `Note: ${ metricName } has no data for ${ runtime } ` ) ;
195+ return ;
196+ }
197+ expect ( value ) . toBeGreaterThanOrEqual ( 0 ) ;
198+ } ) ;
199+ } ) ;
200+
201+ // Count validation
202+ describe ( 'count validation' , ( ) => {
203+ it ( 'should emit runtime_duration for each invocation' , ( ) => {
204+ if ( skipIfNoMetricsApi ( ) ) return ;
205+ const { duration } = getTelemetry ( ) . metrics ;
206+ // Enhanced metrics may aggregate points, so we check >= 1 instead of exact count
207+ expect ( duration [ 'runtime_duration' ] . length ) . toBeGreaterThanOrEqual ( 1 ) ;
208+ } ) ;
209+
210+ it ( 'should emit init_duration only on cold start' , ( ) => {
211+ if ( skipIfNoMetricsApi ( ) ) return ;
212+ const { duration } = getTelemetry ( ) . metrics ;
213+ // init_duration should exist for cold start (may be 0 or 1 depending on runtime/timing)
214+ // Some runtimes may not emit init_duration in all cases
215+ const initDurationCount = duration [ 'init_duration' ] . length ;
216+ // Expect at most 1 (cold start only, not warm start)
217+ expect ( initDurationCount ) . toBeLessThanOrEqual ( 1 ) ;
218+ } ) ;
219+ } ) ;
220+
221+ // Relationship tests
222+ it ( 'duration and runtime_duration should be comparable' , ( ) => {
223+ if ( skipIfNoMetricsApi ( ) ) return ;
224+ const { duration } = getTelemetry ( ) . metrics ;
225+ const durationValue = getLatestValue ( duration [ 'duration' ] ) ;
226+ const runtimeValue = getLatestValue ( duration [ 'runtime_duration' ] ) ;
227+ // Skip if either metric has no data
228+ if ( durationValue === null || runtimeValue === null ) {
229+ console . log ( 'Skipping relationship test - missing metric data' ) ;
230+ return ;
231+ }
232+ // Log the relationship for debugging
233+ // Note: Due to metric aggregation, duration may not always be >= runtime_duration
234+ // in the queried time window. We verify both values are positive and reasonable.
235+ console . log ( `${ runtime } : duration=${ durationValue } ms, runtime_duration=${ runtimeValue } ms` ) ;
236+ expect ( durationValue ) . toBeGreaterThan ( 0 ) ;
237+ expect ( runtimeValue ) . toBeGreaterThan ( 0 ) ;
238+ } ) ;
239+
240+ it ( 'post_runtime_duration should be reasonable' , ( ) => {
241+ if ( skipIfNoMetricsApi ( ) ) return ;
242+ const { duration } = getTelemetry ( ) . metrics ;
243+ const value = getLatestValue ( duration [ 'post_runtime_duration' ] ) ;
244+ // Skip if metric has no data
245+ if ( value === null ) {
246+ console . log ( 'Skipping post_runtime_duration test - no data' ) ;
247+ return ;
248+ }
249+ // Verify post_runtime_duration is positive and less than total duration
250+ // (exact threshold depends on runtime and extension processing)
251+ expect ( value ) . toBeGreaterThanOrEqual ( 0 ) ;
252+ } ) ;
253+ } ) ;
154254 } ) ;
155255} ) ;
0 commit comments