@@ -578,6 +578,106 @@ describe("Safe Output Handler Manager", () => {
578578 expect ( result . temporaryIdMap [ "aw_aabbcc11" ] ) . toBeDefined ( ) ;
579579 } ) ;
580580
581+ it ( "should populate artifactUrlMap when upload_artifact succeeds" , async ( ) => {
582+ const messages = [
583+ {
584+ type : "upload_artifact" ,
585+ temporary_id : "aw_chart1" ,
586+ path : "chart.png" ,
587+ } ,
588+ ] ;
589+
590+ const mockUploadHandler = vi . fn ( ) . mockResolvedValue ( {
591+ tmpId : "aw_chart1" ,
592+ artifactUrl : "https://github.com/owner/repo/actions/runs/1/artifacts/42" ,
593+ } ) ;
594+
595+ const handlers = new Map ( [ [ "upload_artifact" , mockUploadHandler ] ] ) ;
596+ const result = await processMessages ( handlers , messages ) ;
597+
598+ expect ( result . success ) . toBe ( true ) ;
599+ expect ( result . artifactUrlMap ) . toBeDefined ( ) ;
600+ expect ( result . artifactUrlMap . get ( "aw_chart1" ) ) . toBe ( "https://github.com/owner/repo/actions/runs/1/artifacts/42" ) ;
601+ } ) ;
602+
603+ it ( "should track issue with artifact reference as needing synthetic update when artifact is uploaded after" , async ( ) => {
604+ const messages = [
605+ {
606+ type : "create_issue" ,
607+ title : "Issue with chart" ,
608+ body : "See chart: " ,
609+ } ,
610+ {
611+ type : "upload_artifact" ,
612+ temporary_id : "aw_chart1" ,
613+ path : "chart.png" ,
614+ } ,
615+ ] ;
616+
617+ const mockCreateIssueHandler = vi . fn ( ) . mockResolvedValue ( {
618+ repo : "owner/repo" ,
619+ number : 100 ,
620+ } ) ;
621+
622+ const mockUploadHandler = vi . fn ( ) . mockResolvedValue ( {
623+ tmpId : "aw_chart1" ,
624+ artifactUrl : "https://github.com/owner/repo/actions/runs/1/artifacts/42" ,
625+ } ) ;
626+
627+ const handlers = new Map ( [
628+ [ "create_issue" , mockCreateIssueHandler ] ,
629+ [ "upload_artifact" , mockUploadHandler ] ,
630+ ] ) ;
631+
632+ const result = await processMessages ( handlers , messages ) ;
633+
634+ expect ( result . success ) . toBe ( true ) ;
635+ expect ( result . artifactUrlMap . get ( "aw_chart1" ) ) . toBe ( "https://github.com/owner/repo/actions/runs/1/artifacts/42" ) ;
636+ // The issue was created before the artifact was uploaded, so it should be tracked
637+ expect ( result . outputsWithUnresolvedIds . length ) . toBe ( 1 ) ;
638+ expect ( result . outputsWithUnresolvedIds [ 0 ] . result . number ) . toBe ( 100 ) ;
639+ } ) ;
640+
641+ it ( "should replace artifact URL references in issue body when artifact is uploaded before issue" , async ( ) => {
642+ const messages = [
643+ {
644+ type : "upload_artifact" ,
645+ temporary_id : "aw_chart1" ,
646+ path : "chart.png" ,
647+ } ,
648+ {
649+ type : "create_issue" ,
650+ title : "Issue with chart" ,
651+ body : "See chart: " ,
652+ } ,
653+ ] ;
654+
655+ const mockUploadHandler = vi . fn ( ) . mockResolvedValue ( {
656+ tmpId : "aw_chart1" ,
657+ artifactUrl : "https://github.com/owner/repo/actions/runs/1/artifacts/42" ,
658+ } ) ;
659+
660+ const mockCreateIssueHandler = vi . fn ( ) . mockResolvedValue ( {
661+ repo : "owner/repo" ,
662+ number : 100 ,
663+ } ) ;
664+
665+ const handlers = new Map ( [
666+ [ "upload_artifact" , mockUploadHandler ] ,
667+ [ "create_issue" , mockCreateIssueHandler ] ,
668+ ] ) ;
669+
670+ const result = await processMessages ( handlers , messages ) ;
671+
672+ expect ( result . success ) . toBe ( true ) ;
673+ // When artifact is already uploaded, the body passed to create_issue should have URL replaced
674+ const calledMessage = mockCreateIssueHandler . mock . calls [ 0 ] [ 0 ] ;
675+ expect ( calledMessage . body ) . toContain ( "https://github.com/owner/repo/actions/runs/1/artifacts/42" ) ;
676+ expect ( calledMessage . body ) . not . toContain ( "#aw_chart1" ) ;
677+ // The issue does not need synthetic update since body was pre-resolved
678+ expect ( result . outputsWithUnresolvedIds . length ) . toBe ( 0 ) ;
679+ } ) ;
680+
581681 it ( "should silently skip message types handled by standalone steps" , async ( ) => {
582682 const messages = [
583683 { type : "create_issue" , title : "Issue" } ,
0 commit comments