@@ -1179,6 +1179,138 @@ describe("Orchestration Executor", () => {
11791179 it ( "should throw when whenAny is called with an empty task array" , ( ) => {
11801180 expect ( ( ) => whenAny ( [ ] ) ) . toThrow ( "whenAny requires at least one task" ) ;
11811181 } ) ;
1182+
1183+ it ( "should fail whenAll correctly when the failing task is the last to complete" , async ( ) => {
1184+ const printInt = ( _ : any , value : number ) => {
1185+ return value . toString ( ) ;
1186+ } ;
1187+
1188+ const orchestrator : TOrchestrator = async function * ( ctx : OrchestrationContext ) : any {
1189+ const tasks : Task < string > [ ] = [ ] ;
1190+
1191+ for ( let i = 0 ; i < 3 ; i ++ ) {
1192+ tasks . push ( ctx . callActivity ( printInt , i ) ) ;
1193+ }
1194+
1195+ const results = yield whenAll ( tasks ) ;
1196+ return results ;
1197+ } ;
1198+
1199+ const registry = new Registry ( ) ;
1200+ const orchestratorName = registry . addOrchestrator ( orchestrator ) ;
1201+ const activityName = registry . addActivity ( printInt ) ;
1202+
1203+ const oldEvents = [ newOrchestratorStartedEvent ( ) , newExecutionStartedEvent ( orchestratorName , TEST_INSTANCE_ID ) ] ;
1204+
1205+ for ( let i = 0 ; i < 3 ; i ++ ) {
1206+ oldEvents . push ( newTaskScheduledEvent ( i + 1 , activityName , i . toString ( ) ) ) ;
1207+ }
1208+
1209+ // First two tasks succeed, last task fails
1210+ const ex = new Error ( "Last task failed" ) ;
1211+ const newEvents : any [ ] = [
1212+ newTaskCompletedEvent ( 1 , printInt ( null , 0 ) ) ,
1213+ newTaskCompletedEvent ( 2 , printInt ( null , 1 ) ) ,
1214+ newTaskFailedEvent ( 3 , ex ) ,
1215+ ] ;
1216+
1217+ const executor = new OrchestrationExecutor ( registry , testLogger ) ;
1218+ const result = await executor . execute ( TEST_INSTANCE_ID , oldEvents , newEvents ) ;
1219+
1220+ const completeAction = getAndValidateSingleCompleteOrchestrationAction ( result ) ;
1221+ expect ( completeAction ?. getOrchestrationstatus ( ) ) . toEqual ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1222+ expect ( completeAction ?. getFailuredetails ( ) ?. getErrortype ( ) ) . toEqual ( "TaskFailedError" ) ;
1223+ expect ( completeAction ?. getFailuredetails ( ) ?. getErrormessage ( ) ) . toContain ( ex . message ) ;
1224+ } ) ;
1225+
1226+ it ( "should not crash when additional tasks complete after whenAll fails fast" , async ( ) => {
1227+ const printInt = ( _ : any , value : number ) => {
1228+ return value . toString ( ) ;
1229+ } ;
1230+
1231+ const orchestrator : TOrchestrator = async function * ( ctx : OrchestrationContext ) : any {
1232+ const tasks : Task < string > [ ] = [ ] ;
1233+
1234+ for ( let i = 0 ; i < 3 ; i ++ ) {
1235+ tasks . push ( ctx . callActivity ( printInt , i ) ) ;
1236+ }
1237+
1238+ const results = yield whenAll ( tasks ) ;
1239+ return results ;
1240+ } ;
1241+
1242+ const registry = new Registry ( ) ;
1243+ const orchestratorName = registry . addOrchestrator ( orchestrator ) ;
1244+ const activityName = registry . addActivity ( printInt ) ;
1245+
1246+ const oldEvents = [ newOrchestratorStartedEvent ( ) , newExecutionStartedEvent ( orchestratorName , TEST_INSTANCE_ID ) ] ;
1247+
1248+ for ( let i = 0 ; i < 3 ; i ++ ) {
1249+ oldEvents . push ( newTaskScheduledEvent ( i + 1 , activityName , i . toString ( ) ) ) ;
1250+ }
1251+
1252+ // First task fails, then remaining tasks complete in the same batch
1253+ const ex = new Error ( "First task failed" ) ;
1254+ const newEvents : any [ ] = [
1255+ newTaskFailedEvent ( 1 , ex ) ,
1256+ newTaskCompletedEvent ( 2 , printInt ( null , 1 ) ) ,
1257+ newTaskCompletedEvent ( 3 , printInt ( null , 2 ) ) ,
1258+ ] ;
1259+
1260+ const executor = new OrchestrationExecutor ( registry , testLogger ) ;
1261+ const result = await executor . execute ( TEST_INSTANCE_ID , oldEvents , newEvents ) ;
1262+
1263+ const completeAction = getAndValidateSingleCompleteOrchestrationAction ( result ) ;
1264+ expect ( completeAction ?. getOrchestrationstatus ( ) ) . toEqual ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
1265+ expect ( completeAction ?. getFailuredetails ( ) ?. getErrortype ( ) ) . toEqual ( "TaskFailedError" ) ;
1266+ expect ( completeAction ?. getFailuredetails ( ) ?. getErrormessage ( ) ) . toContain ( ex . message ) ;
1267+ } ) ;
1268+
1269+ it ( "should preserve orchestration result when whenAll failure is caught and other tasks complete" , async ( ) => {
1270+ const printInt = ( _ : any , value : number ) => {
1271+ return value . toString ( ) ;
1272+ } ;
1273+
1274+ const orchestrator : TOrchestrator = async function * ( ctx : OrchestrationContext ) : any {
1275+ const tasks : Task < string > [ ] = [ ] ;
1276+
1277+ for ( let i = 0 ; i < 3 ; i ++ ) {
1278+ tasks . push ( ctx . callActivity ( printInt , i ) ) ;
1279+ }
1280+
1281+ try {
1282+ yield whenAll ( tasks ) ;
1283+ } catch {
1284+ // Intentionally catch the failure and return a fallback result
1285+ return "handled" ;
1286+ }
1287+ } ;
1288+
1289+ const registry = new Registry ( ) ;
1290+ const orchestratorName = registry . addOrchestrator ( orchestrator ) ;
1291+ const activityName = registry . addActivity ( printInt ) ;
1292+
1293+ const oldEvents = [ newOrchestratorStartedEvent ( ) , newExecutionStartedEvent ( orchestratorName , TEST_INSTANCE_ID ) ] ;
1294+
1295+ for ( let i = 0 ; i < 3 ; i ++ ) {
1296+ oldEvents . push ( newTaskScheduledEvent ( i + 1 , activityName , i . toString ( ) ) ) ;
1297+ }
1298+
1299+ // First task fails, then remaining tasks complete in the same batch
1300+ const ex = new Error ( "One task failed" ) ;
1301+ const newEvents : any [ ] = [
1302+ newTaskFailedEvent ( 1 , ex ) ,
1303+ newTaskCompletedEvent ( 2 , printInt ( null , 1 ) ) ,
1304+ newTaskCompletedEvent ( 3 , printInt ( null , 2 ) ) ,
1305+ ] ;
1306+
1307+ const executor = new OrchestrationExecutor ( registry , testLogger ) ;
1308+ const result = await executor . execute ( TEST_INSTANCE_ID , oldEvents , newEvents ) ;
1309+
1310+ const completeAction = getAndValidateSingleCompleteOrchestrationAction ( result ) ;
1311+ expect ( completeAction ?. getOrchestrationstatus ( ) ) . toEqual ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_COMPLETED ) ;
1312+ expect ( completeAction ?. getResult ( ) ?. getValue ( ) ) . toEqual ( JSON . stringify ( "handled" ) ) ;
1313+ } ) ;
11821314} ) ;
11831315
11841316function getAndValidateSingleCompleteOrchestrationAction (
0 commit comments