@@ -20,6 +20,7 @@ describe("Safe Output Handler Manager", () => {
2020 // Clean up environment variables
2121 delete process . env . GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG ;
2222 delete process . env . GH_AW_TRACKER_LABEL ;
23+ delete process . env . GH_AW_SAFE_OUTPUT_JOBS ;
2324 } ) ;
2425
2526 describe ( "loadConfig" , ( ) => {
@@ -218,6 +219,91 @@ describe("Safe Output Handler Manager", () => {
218219 expect ( core . warning ) . toHaveBeenCalledWith ( expect . stringContaining ( "No handler loaded for message type 'unknown_type'" ) ) ;
219220 } ) ;
220221
222+ it ( "should skip custom safe output job types gracefully without error" , async ( ) => {
223+ // Set up custom safe output jobs (e.g., send_slack_message handled by a dedicated job step)
224+ process . env . GH_AW_SAFE_OUTPUT_JOBS = JSON . stringify ( {
225+ send_slack_message : "message_url" ,
226+ } ) ;
227+
228+ const messages = [
229+ { type : "create_issue" , title : "Issue" } ,
230+ { type : "send_slack_message" , channel : "#alerts" , text : "Hello" } ,
231+ ] ;
232+
233+ const mockHandler = vi . fn ( ) . mockResolvedValue ( { success : true } ) ;
234+
235+ // Only create_issue handler is available; send_slack_message is a custom job
236+ const handlers = new Map ( [ [ "create_issue" , mockHandler ] ] ) ;
237+
238+ const result = await processMessages ( handlers , messages ) ;
239+
240+ expect ( result . success ) . toBe ( true ) ;
241+ expect ( result . results ) . toHaveLength ( 2 ) ;
242+
243+ // First message should succeed
244+ expect ( result . results [ 0 ] . success ) . toBe ( true ) ;
245+ expect ( result . results [ 0 ] . type ) . toBe ( "create_issue" ) ;
246+
247+ // Custom job message should be skipped gracefully (not an error)
248+ expect ( result . results [ 1 ] . success ) . toBe ( false ) ;
249+ expect ( result . results [ 1 ] . type ) . toBe ( "send_slack_message" ) ;
250+ expect ( result . results [ 1 ] . skipped ) . toBe ( true ) ;
251+ expect ( result . results [ 1 ] . reason ) . toBe ( "Handled by custom safe output job" ) ;
252+ expect ( result . results [ 1 ] . error ) . toBeUndefined ( ) ;
253+
254+ // Should NOT have logged a "No handler loaded" warning
255+ expect ( core . warning ) . not . toHaveBeenCalledWith ( expect . stringContaining ( "No handler loaded for message type 'send_slack_message'" ) ) ;
256+ } ) ;
257+
258+ it ( "should skip multiple custom safe output job types gracefully" , async ( ) => {
259+ process . env . GH_AW_SAFE_OUTPUT_JOBS = JSON . stringify ( {
260+ send_slack_message : "message_url" ,
261+ notion_add_comment : "comment_url" ,
262+ } ) ;
263+
264+ const messages = [
265+ { type : "send_slack_message" , channel : "#alerts" , text : "Hello" } ,
266+ { type : "notion_add_comment" , page_id : "abc123" , text : "Note" } ,
267+ { type : "create_issue" , title : "Issue" } ,
268+ ] ;
269+
270+ const mockHandler = vi . fn ( ) . mockResolvedValue ( { success : true } ) ;
271+ const handlers = new Map ( [ [ "create_issue" , mockHandler ] ] ) ;
272+
273+ const result = await processMessages ( handlers , messages ) ;
274+
275+ expect ( result . success ) . toBe ( true ) ;
276+ expect ( result . results ) . toHaveLength ( 3 ) ;
277+
278+ // Custom job types should be skipped gracefully
279+ expect ( result . results [ 0 ] . skipped ) . toBe ( true ) ;
280+ expect ( result . results [ 0 ] . reason ) . toBe ( "Handled by custom safe output job" ) ;
281+ expect ( result . results [ 1 ] . skipped ) . toBe ( true ) ;
282+ expect ( result . results [ 1 ] . reason ) . toBe ( "Handled by custom safe output job" ) ;
283+
284+ // create_issue should succeed
285+ expect ( result . results [ 2 ] . success ) . toBe ( true ) ;
286+ } ) ;
287+
288+ it ( "should still warn for unknown types not in custom job types" , async ( ) => {
289+ process . env . GH_AW_SAFE_OUTPUT_JOBS = JSON . stringify ( {
290+ send_slack_message : "message_url" ,
291+ } ) ;
292+
293+ const messages = [ { type : "completely_unknown_type" , data : "test" } ] ;
294+
295+ const handlers = new Map ( ) ;
296+
297+ const result = await processMessages ( handlers , messages ) ;
298+
299+ expect ( result . success ) . toBe ( true ) ;
300+ expect ( result . results [ 0 ] . error ) . toContain ( "No handler loaded" ) ;
301+ expect ( result . results [ 0 ] . skipped ) . toBeUndefined ( ) ;
302+
303+ // Should have logged a warning for truly unknown types
304+ expect ( core . warning ) . toHaveBeenCalledWith ( expect . stringContaining ( "No handler loaded for message type 'completely_unknown_type'" ) ) ;
305+ } ) ;
306+
221307 it ( "should handle handler errors gracefully" , async ( ) => {
222308 const messages = [ { type : "create_issue" , title : "Issue" } ] ;
223309
0 commit comments