@@ -383,6 +383,32 @@ Describe "Get-BootstrapRegistryDomainName" {
383383 }
384384}
385385
386+ Describe " Get-FileNameFromUrl" {
387+ It " should return file name for url without query string" {
388+ $url = " https://contoso.blob.core.windows.net/packages/windowszip.zip"
389+
390+ Get-FileNameFromUrl - Url $url | Should - Be " windowszip.zip"
391+ }
392+
393+ It " should strip query string before extracting file name" {
394+ $url = " https://contoso.blob.core.windows.net/packages/windowszip.zip?sv=2025-01-01&sig=token"
395+
396+ Get-FileNameFromUrl - Url $url | Should - Be " windowszip.zip"
397+ }
398+
399+ It " should return the last segment for nested paths" {
400+ $url = " https://contoso.blob.core.windows.net/packages/release/v1.30.0/kubernetes-node-image.tar.gz"
401+
402+ Get-FileNameFromUrl - Url $url | Should - Be " kubernetes-node-image.tar.gz"
403+ }
404+
405+ It " should return empty when url ends with slash" {
406+ $url = " https://contoso.blob.core.windows.net/packages/release/v1.30.0/"
407+
408+ Get-FileNameFromUrl - Url $url | Should - Be " "
409+ }
410+ }
411+
386412Describe " DownloadFileWithOras" {
387413 BeforeEach {
388414 $global :OrasPath = " Mock-OrasCli"
@@ -472,4 +498,67 @@ Describe "DownloadFileWithOras" {
472498
473499 { DownloadFileWithOras - Reference $reference - DestinationPath $destPath - Platform " linux/amd64" } | Should -Not - Throw
474500 }
501+
502+ It " should copy from cache and skip oras pull when CachedFile is provided" {
503+ $cacheRoot = Join-Path ([System.IO.Path ]::GetTempPath()) ([System.Guid ]::NewGuid().ToString())
504+ $cacheSubDir = Join-Path $cacheRoot " nested"
505+ $cachedFileName = " windowszip.zip"
506+ $cachedFilePath = Join-Path $cacheSubDir $cachedFileName
507+ $destPath = " c:\k.zip"
508+ $reference = " myregistry.azurecr.io/aks/packages/kubernetes/windowszip:1.29.2"
509+
510+ New-Item - ItemType Directory - Path $cacheSubDir - Force | Out-Null
511+ Set-Content - Path $cachedFilePath - Value " cached-content" - NoNewline
512+
513+ $global :CacheDir = $cacheRoot
514+ $script :orasInvoked = $false
515+ function global :Mock-OrasCli {
516+ param ([Parameter (ValueFromRemainingArguments = $true )]$Args )
517+ $script :orasInvoked = $true
518+ $global :LASTEXITCODE = 0
519+ }
520+
521+ Mock Copy-Item - MockWith {}
522+
523+ try {
524+ { DownloadFileWithOras - Reference $reference - DestinationPath $destPath - CachedFile $cachedFileName } | Should -Not - Throw
525+ Assert-MockCalled - CommandName ' Copy-Item' - Exactly - Times 1 - ParameterFilter {
526+ $Path -eq $cachedFilePath -and $Destination -eq $destPath -and $Force
527+ }
528+ Assert-MockCalled - CommandName ' Move-Item' - Times 0
529+ $script :orasInvoked | Should - Be $false
530+ }
531+ finally {
532+ Remove-Item - Path $cacheRoot - Recurse - Force - ErrorAction SilentlyContinue
533+ $global :CacheDir = " c:\akse-cache"
534+ }
535+ }
536+
537+ It " should invoke oras pull and skip cache copy when CachedFile is provided but missing from cache" {
538+ $cacheRoot = Join-Path ([System.IO.Path ]::GetTempPath()) ([System.Guid ]::NewGuid().ToString())
539+ $cachedFileName = " windowszip.zip"
540+ $destPath = " c:\k.zip"
541+ $reference = " myregistry.azurecr.io/aks/packages/kubernetes/windowszip:1.29.2"
542+
543+ New-Item - ItemType Directory - Path $cacheRoot - Force | Out-Null
544+
545+ $global :CacheDir = $cacheRoot
546+ $script :orasInvoked = $false
547+ function global :Mock-OrasCli {
548+ param ([Parameter (ValueFromRemainingArguments = $true )]$Args )
549+ $script :orasInvoked = $true
550+ $global :LASTEXITCODE = 0
551+ }
552+
553+ Mock Copy-Item - MockWith {}
554+
555+ try {
556+ { DownloadFileWithOras - Reference $reference - DestinationPath $destPath - CachedFile $cachedFileName } | Should -Not - Throw
557+ Assert-MockCalled - CommandName ' Copy-Item' - Times 0
558+ $script :orasInvoked | Should - Be $true
559+ }
560+ finally {
561+ Remove-Item - Path $cacheRoot - Recurse - Force - ErrorAction SilentlyContinue
562+ }
563+ }
475564}
0 commit comments