@@ -3283,5 +3283,141 @@ private void Teardown()
32833283 Directory . Delete ( _workFolder , recursive : true ) ;
32843284 }
32853285 }
3286+
3287+ [ Fact ]
3288+ [ Trait ( "Level" , "L0" ) ]
3289+ [ Trait ( "Category" , "Worker" ) ]
3290+ public async Task GetDownloadInfoAsync_PropagatesDependencies_WhenPresent ( )
3291+ {
3292+ try
3293+ {
3294+ // Arrange
3295+ Setup ( ) ;
3296+
3297+ // Set RunServiceJob so we hit the Launch path
3298+ _ec . Object . Global . Variables . Set ( Constants . Variables . System . JobRequestType , "RunnerJobRequest" ) ;
3299+
3300+ // Populate lockfile dependencies
3301+ _ec . Object . Global . ActionsDependencies = new List < string >
3302+ {
3303+ "github.com/actions/checkout@v4:sha256-abc123" ,
3304+ "github.com/actions/setup-node@v4:sha256-def456"
3305+ } ;
3306+
3307+ // Capture the ActionReferenceList passed to Launch
3308+ ActionReferenceList capturedList = null ;
3309+ _launchServer
3310+ . Setup ( x => x . ResolveActionsDownloadInfoAsync ( It . IsAny < Guid > ( ) , It . IsAny < Guid > ( ) , It . IsAny < ActionReferenceList > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < bool > ( ) ) )
3311+ . Callback < Guid , Guid , ActionReferenceList , CancellationToken , bool > ( ( planId , jobId , list , ct , display ) => capturedList = list )
3312+ . Returns ( ( Guid planId , Guid jobId , ActionReferenceList actions , CancellationToken ct , bool display ) =>
3313+ {
3314+ var result = new ActionDownloadInfoCollection { Actions = new Dictionary < string , ActionDownloadInfo > ( ) } ;
3315+ foreach ( var action in actions . Actions )
3316+ {
3317+ var key = $ "{ action . NameWithOwner } @{ action . Ref } ";
3318+ result . Actions [ key ] = new ActionDownloadInfo
3319+ {
3320+ NameWithOwner = action . NameWithOwner ,
3321+ Ref = action . Ref ,
3322+ ResolvedNameWithOwner = action . NameWithOwner ,
3323+ ResolvedSha = $ "{ action . Ref } -sha",
3324+ TarballUrl = $ "https://api.github.com/repos/{ action . NameWithOwner } /tarball/{ action . Ref } ",
3325+ ZipballUrl = $ "https://api.github.com/repos/{ action . NameWithOwner } /zipball/{ action . Ref } ",
3326+ } ;
3327+ }
3328+ return Task . FromResult ( result ) ;
3329+ } ) ;
3330+
3331+ var actionStep = new Pipelines . ActionStep ( )
3332+ {
3333+ Name = "action" ,
3334+ Id = Guid . NewGuid ( ) ,
3335+ Reference = new Pipelines . RepositoryPathReference ( )
3336+ {
3337+ Name = "actions/checkout" ,
3338+ Ref = "v4" ,
3339+ RepositoryType = "GitHub"
3340+ }
3341+ } ;
3342+
3343+ // Act
3344+ var result = await _actionManager . PrepareActionsAsync ( _ec . Object , new List < Pipelines . JobStep > { actionStep } , default ) ;
3345+
3346+ // Assert
3347+ Assert . NotNull ( capturedList ) ;
3348+ Assert . NotNull ( capturedList . Dependencies ) ;
3349+ Assert . Equal ( 2 , capturedList . Dependencies . Count ) ;
3350+ Assert . Equal ( "github.com/actions/checkout@v4:sha256-abc123" , capturedList . Dependencies [ 0 ] ) ;
3351+ Assert . Equal ( "github.com/actions/setup-node@v4:sha256-def456" , capturedList . Dependencies [ 1 ] ) ;
3352+ }
3353+ finally
3354+ {
3355+ Teardown ( ) ;
3356+ }
3357+ }
3358+
3359+ [ Fact ]
3360+ [ Trait ( "Level" , "L0" ) ]
3361+ [ Trait ( "Category" , "Worker" ) ]
3362+ public async Task GetDownloadInfoAsync_OmitsDependencies_WhenEmpty ( )
3363+ {
3364+ try
3365+ {
3366+ // Arrange
3367+ Setup ( ) ;
3368+
3369+ // Set RunServiceJob so we hit the Launch path
3370+ _ec . Object . Global . Variables . Set ( Constants . Variables . System . JobRequestType , "RunnerJobRequest" ) ;
3371+
3372+ // No dependencies set (default empty list from GlobalContext)
3373+
3374+ // Capture the ActionReferenceList passed to Launch
3375+ ActionReferenceList capturedList = null ;
3376+ _launchServer
3377+ . Setup ( x => x . ResolveActionsDownloadInfoAsync ( It . IsAny < Guid > ( ) , It . IsAny < Guid > ( ) , It . IsAny < ActionReferenceList > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < bool > ( ) ) )
3378+ . Callback < Guid , Guid , ActionReferenceList , CancellationToken , bool > ( ( planId , jobId , list , ct , display ) => capturedList = list )
3379+ . Returns ( ( Guid planId , Guid jobId , ActionReferenceList actions , CancellationToken ct , bool display ) =>
3380+ {
3381+ var result = new ActionDownloadInfoCollection { Actions = new Dictionary < string , ActionDownloadInfo > ( ) } ;
3382+ foreach ( var action in actions . Actions )
3383+ {
3384+ var key = $ "{ action . NameWithOwner } @{ action . Ref } ";
3385+ result . Actions [ key ] = new ActionDownloadInfo
3386+ {
3387+ NameWithOwner = action . NameWithOwner ,
3388+ Ref = action . Ref ,
3389+ ResolvedNameWithOwner = action . NameWithOwner ,
3390+ ResolvedSha = $ "{ action . Ref } -sha",
3391+ TarballUrl = $ "https://api.github.com/repos/{ action . NameWithOwner } /tarball/{ action . Ref } ",
3392+ ZipballUrl = $ "https://api.github.com/repos/{ action . NameWithOwner } /zipball/{ action . Ref } ",
3393+ } ;
3394+ }
3395+ return Task . FromResult ( result ) ;
3396+ } ) ;
3397+
3398+ var actionStep = new Pipelines . ActionStep ( )
3399+ {
3400+ Name = "action" ,
3401+ Id = Guid . NewGuid ( ) ,
3402+ Reference = new Pipelines . RepositoryPathReference ( )
3403+ {
3404+ Name = "actions/checkout" ,
3405+ Ref = "v4" ,
3406+ RepositoryType = "GitHub"
3407+ }
3408+ } ;
3409+
3410+ // Act
3411+ var result = await _actionManager . PrepareActionsAsync ( _ec . Object , new List < Pipelines . JobStep > { actionStep } , default ) ;
3412+
3413+ // Assert
3414+ Assert . NotNull ( capturedList ) ;
3415+ Assert . Null ( capturedList . Dependencies ) ;
3416+ }
3417+ finally
3418+ {
3419+ Teardown ( ) ;
3420+ }
3421+ }
32863422 }
32873423}
0 commit comments