@@ -953,6 +953,256 @@ describe("TaskExecutor", () => {
953953 } ,
954954 } ) ;
955955 } ) ;
956+
957+ test ( "should propagate errors from init hooks" , async ( ) => {
958+ const executionOrder : string [ ] = [ ] ;
959+ const expectedError = new Error ( "Init hook error" ) ;
960+
961+ // Register global init hook that throws an error
962+ lifecycleHooks . registerGlobalInitHook ( {
963+ id : "failing-init" ,
964+ fn : async ( ) => {
965+ executionOrder . push ( "global-init" ) ;
966+ throw expectedError ;
967+ } ,
968+ } ) ;
969+
970+ // Register task init hook that should never be called
971+ lifecycleHooks . registerTaskInitHook ( "test-task" , {
972+ id : "task-init" ,
973+ fn : async ( ) => {
974+ executionOrder . push ( "task-init" ) ;
975+ return {
976+ foo : "bar" ,
977+ } ;
978+ } ,
979+ } ) ;
980+
981+ // Register failure hook to verify it's called
982+ lifecycleHooks . registerGlobalFailureHook ( {
983+ id : "global-failure" ,
984+ fn : async ( { error } ) => {
985+ executionOrder . push ( "failure" ) ;
986+ expect ( error ) . toBe ( expectedError ) ;
987+ } ,
988+ } ) ;
989+
990+ // Register complete hook to verify it's called with error
991+ lifecycleHooks . registerGlobalCompleteHook ( {
992+ id : "global-complete" ,
993+ fn : async ( { result } ) => {
994+ executionOrder . push ( "complete" ) ;
995+ expect ( result ) . toEqual ( {
996+ ok : false ,
997+ error : expectedError ,
998+ } ) ;
999+ } ,
1000+ } ) ;
1001+
1002+ const task = {
1003+ id : "test-task" ,
1004+ fns : {
1005+ run : async ( payload : any , params : RunFnParams < any > ) => {
1006+ executionOrder . push ( "run" ) ;
1007+ return {
1008+ output : "test-output" ,
1009+ } ;
1010+ } ,
1011+ } ,
1012+ } ;
1013+
1014+ const result = await executeTask ( task , { test : "data" } ) ;
1015+
1016+ // Verify only the global init hook ran, and failure/complete hooks were called
1017+ expect ( executionOrder ) . toEqual ( [ "global-init" , "failure" , "complete" ] ) ;
1018+
1019+ // Verify the error result
1020+ expect ( result ) . toEqual ( {
1021+ result : {
1022+ ok : false ,
1023+ id : "test-run-id" ,
1024+ error : {
1025+ type : "BUILT_IN_ERROR" ,
1026+ message : "Init hook error" ,
1027+ name : "Error" ,
1028+ stackTrace : expect . any ( String ) ,
1029+ } ,
1030+ skippedRetrying : false ,
1031+ } ,
1032+ } ) ;
1033+ } ) ;
1034+
1035+ test ( "should propagate errors from task init hooks" , async ( ) => {
1036+ const executionOrder : string [ ] = [ ] ;
1037+ const expectedError = new Error ( "Task init hook error" ) ;
1038+
1039+ // Register global init hook that succeeds
1040+ lifecycleHooks . registerGlobalInitHook ( {
1041+ id : "global-init" ,
1042+ fn : async ( ) => {
1043+ executionOrder . push ( "global-init" ) ;
1044+ return {
1045+ foo : "bar" ,
1046+ } ;
1047+ } ,
1048+ } ) ;
1049+
1050+ // Register task init hook that throws an error
1051+ lifecycleHooks . registerTaskInitHook ( "test-task" , {
1052+ id : "task-init" ,
1053+ fn : async ( ) => {
1054+ executionOrder . push ( "task-init" ) ;
1055+ throw expectedError ;
1056+ } ,
1057+ } ) ;
1058+
1059+ // Register failure hook to verify it's called
1060+ lifecycleHooks . registerGlobalFailureHook ( {
1061+ id : "global-failure" ,
1062+ fn : async ( { error, init } ) => {
1063+ executionOrder . push ( "failure" ) ;
1064+ expect ( error ) . toBe ( expectedError ) ;
1065+ // Verify we got the global init data
1066+ expect ( init ) . toEqual ( { foo : "bar" } ) ;
1067+ } ,
1068+ } ) ;
1069+
1070+ // Register complete hook to verify it's called with error
1071+ lifecycleHooks . registerGlobalCompleteHook ( {
1072+ id : "global-complete" ,
1073+ fn : async ( { result, init } ) => {
1074+ executionOrder . push ( "complete" ) ;
1075+ expect ( result ) . toEqual ( {
1076+ ok : false ,
1077+ error : expectedError ,
1078+ } ) ;
1079+ // Verify we got the global init data
1080+ expect ( init ) . toEqual ( { foo : "bar" } ) ;
1081+ } ,
1082+ } ) ;
1083+
1084+ const task = {
1085+ id : "test-task" ,
1086+ fns : {
1087+ run : async ( payload : any , params : RunFnParams < any > ) => {
1088+ executionOrder . push ( "run" ) ;
1089+ return {
1090+ output : "test-output" ,
1091+ } ;
1092+ } ,
1093+ } ,
1094+ } ;
1095+
1096+ const result = await executeTask ( task , { test : "data" } ) ;
1097+
1098+ // Verify both init hooks ran, but run wasn't called, and failure/complete hooks were called
1099+ expect ( executionOrder ) . toEqual ( [ "global-init" , "task-init" , "failure" , "complete" ] ) ;
1100+
1101+ // Verify the error result
1102+ expect ( result ) . toEqual ( {
1103+ result : {
1104+ ok : false ,
1105+ id : "test-run-id" ,
1106+ error : {
1107+ type : "BUILT_IN_ERROR" ,
1108+ message : "Task init hook error" ,
1109+ name : "Error" ,
1110+ stackTrace : expect . any ( String ) ,
1111+ } ,
1112+ skippedRetrying : false ,
1113+ } ,
1114+ } ) ;
1115+ } ) ;
1116+
1117+ test ( "should propagate errors from start hooks" , async ( ) => {
1118+ const executionOrder : string [ ] = [ ] ;
1119+ const expectedError = new Error ( "Start hook error" ) ;
1120+
1121+ // Register global init hook that succeeds
1122+ lifecycleHooks . registerGlobalInitHook ( {
1123+ id : "global-init" ,
1124+ fn : async ( ) => {
1125+ executionOrder . push ( "global-init" ) ;
1126+ return {
1127+ foo : "bar" ,
1128+ } ;
1129+ } ,
1130+ } ) ;
1131+
1132+ // Register global start hook that throws an error
1133+ lifecycleHooks . registerGlobalStartHook ( {
1134+ id : "global-start" ,
1135+ fn : async ( ) => {
1136+ executionOrder . push ( "global-start" ) ;
1137+ throw expectedError ;
1138+ } ,
1139+ } ) ;
1140+
1141+ // Register task start hook that should never be called
1142+ lifecycleHooks . registerTaskStartHook ( "test-task" , {
1143+ id : "task-start" ,
1144+ fn : async ( ) => {
1145+ executionOrder . push ( "task-start" ) ;
1146+ } ,
1147+ } ) ;
1148+
1149+ // Register failure hook to verify it's called
1150+ lifecycleHooks . registerGlobalFailureHook ( {
1151+ id : "global-failure" ,
1152+ fn : async ( { error, init } ) => {
1153+ executionOrder . push ( "failure" ) ;
1154+ expect ( error ) . toBe ( expectedError ) ;
1155+ // Verify we got the init data
1156+ expect ( init ) . toEqual ( { foo : "bar" } ) ;
1157+ } ,
1158+ } ) ;
1159+
1160+ // Register complete hook to verify it's called with error
1161+ lifecycleHooks . registerGlobalCompleteHook ( {
1162+ id : "global-complete" ,
1163+ fn : async ( { result, init } ) => {
1164+ executionOrder . push ( "complete" ) ;
1165+ expect ( result ) . toEqual ( {
1166+ ok : false ,
1167+ error : expectedError ,
1168+ } ) ;
1169+ // Verify we got the init data
1170+ expect ( init ) . toEqual ( { foo : "bar" } ) ;
1171+ } ,
1172+ } ) ;
1173+
1174+ const task = {
1175+ id : "test-task" ,
1176+ fns : {
1177+ run : async ( payload : any , params : RunFnParams < any > ) => {
1178+ executionOrder . push ( "run" ) ;
1179+ return {
1180+ output : "test-output" ,
1181+ } ;
1182+ } ,
1183+ } ,
1184+ } ;
1185+
1186+ const result = await executeTask ( task , { test : "data" } ) ;
1187+
1188+ // Verify init succeeded, start hook failed, and run wasn't called
1189+ expect ( executionOrder ) . toEqual ( [ "global-init" , "global-start" , "failure" , "complete" ] ) ;
1190+
1191+ // Verify the error result
1192+ expect ( result ) . toEqual ( {
1193+ result : {
1194+ ok : false ,
1195+ id : "test-run-id" ,
1196+ error : {
1197+ type : "BUILT_IN_ERROR" ,
1198+ message : "Start hook error" ,
1199+ name : "Error" ,
1200+ stackTrace : expect . any ( String ) ,
1201+ } ,
1202+ skippedRetrying : false ,
1203+ } ,
1204+ } ) ;
1205+ } ) ;
9561206} ) ;
9571207
9581208function executeTask ( task : TaskMetadataWithFunctions , payload : any ) {
0 commit comments