@@ -157,15 +157,63 @@ describe('TelemetrySubscriber', () => {
157157 expect ( TELEMETRY_AGGREGATE_KEYS ) . toContain ( key ) ;
158158 }
159159 } ) ;
160+
161+ it ( 'uses the root session completion as the final process result' , async ( ) => {
162+ const telemetry : TelemetrySubscriber = new TelemetrySubscriber ( ) ;
163+ const manager : ReporterManager = new ReporterManager ( ) ;
164+ manager . addReporter ( createTelemetryReporter ( telemetry ) ) ;
165+ await manager . initializeAsync ( ) ;
166+
167+ manager . emit ( rawInput ( 'commandResult' , { commandName : 'build' , succeeded : true , exitCode : 0 } ) ) ;
168+ manager . emit ( rawInput ( 'sessionCompleted' , { exitCode : 1 , durationMs : 2000 } ) ) ;
169+ await manager . flushAsync ( ) ;
170+
171+ expect ( telemetry . buildAggregate ( ) ) . toMatchObject ( {
172+ commandName : 'build' ,
173+ result : 'failed' ,
174+ exitCode : 1 ,
175+ durationMs : 2000
176+ } ) ;
177+ } ) ;
178+
179+ it ( 'does not let child session lifecycle events overwrite root command state' , async ( ) => {
180+ const telemetry : TelemetrySubscriber = new TelemetrySubscriber ( ) ;
181+ const manager : ReporterManager = new ReporterManager ( ) ;
182+ manager . addReporter ( createTelemetryReporter ( telemetry ) ) ;
183+ await manager . initializeAsync ( ) ;
184+
185+ manager . emit ( rawInput ( 'commandResult' , { commandName : 'build' , succeeded : false , exitCode : 1 } ) ) ;
186+ manager . emit ( rawInput ( 'sessionCompleted' , { exitCode : 1 , durationMs : 2000 } ) ) ;
187+ manager . emit ( {
188+ ...rawInput ( 'commandResult' , { commandName : 'child-command' , succeeded : true , exitCode : 0 } ) ,
189+ sessionId : 'child' ,
190+ parentSessionId : 'sess'
191+ } ) ;
192+ manager . emit ( {
193+ ...rawInput ( 'sessionCompleted' , { exitCode : 0 , durationMs : 25 } ) ,
194+ sessionId : 'child' ,
195+ parentSessionId : 'sess'
196+ } ) ;
197+ await manager . flushAsync ( ) ;
198+
199+ expect ( telemetry . buildAggregate ( ) ) . toMatchObject ( {
200+ commandName : 'build' ,
201+ result : 'failed' ,
202+ exitCode : 1 ,
203+ durationMs : 2000
204+ } ) ;
205+ } ) ;
160206} ) ;
161207
162208describe ( 'createBeforeLogAdapter' , ( ) => {
163- it ( 'runs legacy hooks with a plain copy of the aggregate' , ( ) => {
164- const observed : Record < string , unknown > [ ] = [ ] ;
209+ it ( 'returns hook augmentations without mutating the allowlisted aggregate' , ( ) => {
165210 const hook : LegacyBeforeLogHook = ( telemetry : Record < string , unknown > ) => {
166- observed . push ( telemetry ) ;
211+ telemetry . result = 'failed' ;
212+ telemetry . customField = 'custom-value' ;
213+ ( telemetry . operationStatusCounts as Record < string , number > ) . injected = 99 ;
167214 } ;
168- const adapter : ( aggregate : ITelemetryAggregate ) => void = createBeforeLogAdapter ( [ hook ] ) ;
215+ const adapter : ( aggregate : ITelemetryAggregate ) => Record < string , unknown > =
216+ createBeforeLogAdapter ( [ hook ] ) ;
169217
170218 const aggregate : ITelemetryAggregate = {
171219 commandName : 'build' ,
@@ -176,11 +224,16 @@ describe('createBeforeLogAdapter', () => {
176224 diagnosticCategoryCounts : { } ,
177225 producerVersions : [ '@microsoft/rush-lib@5.177.2' ]
178226 } ;
179- adapter ( aggregate ) ;
227+ const record : Record < string , unknown > = adapter ( aggregate ) ;
180228
181- expect ( observed ) . toHaveLength ( 1 ) ;
182- expect ( observed [ 0 ] ) . toEqual ( { ...aggregate } ) ;
183- // The hook receives a copy, not the aggregate itself.
184- expect ( observed [ 0 ] ) . not . toBe ( aggregate ) ;
229+ expect ( record ) . toMatchObject ( {
230+ result : 'failed' ,
231+ customField : 'custom-value' ,
232+ operationStatusCounts : { success : 2 , injected : 99 }
233+ } ) ;
234+ expect ( aggregate . result ) . toBe ( 'succeeded' ) ;
235+ expect ( aggregate . operationStatusCounts ) . toEqual ( { success : 2 } ) ;
236+ expect ( record ) . not . toBe ( aggregate ) ;
237+ expect ( record . operationStatusCounts ) . not . toBe ( aggregate . operationStatusCounts ) ;
185238 } ) ;
186239} ) ;
0 commit comments