Functions/Get-FileFromUrl.Tests.ps1

describe "BitTitan.Runbooks.Common/Get-FileFromUrl" -Tag "module", "unit" {

    # Import the function to test
    . "$($PSScriptRoot)\Get-FileFromUrl.ps1"

    context "when there are no issues" {
        # Set up the function inputs
        $fileUrl = "https://website.com/file.txt"
        $destinationPath = "Z:\SomeFolder\downloadedFile.txt"

        # Mock Split-Path to return the same path
        mock Split-Path { return "Z:\SomeFolder" }

        # Mock Test-Path to always return true (valid)
        mock Test-Path { return $true }

        # Mock New-Object to return a mock of System.Net.WebClient
        $Script:calledFileUrl = ""
        $Script:calledDestinationPath = ""
        mock New-Object {
            class WebClientMock {
                [Void]DownloadFile($fileUrl, $destinationPath) {
                    $Script:calledFileUrl = $fileUrl
                    $Script:calledDestinationPath = $destinationPath
                }
            }
            return [WebClientMock]::new()
        }

        # Mock Get-Item to return our own value
        mock Get-Item { return "Get-Item success" }

        it "downloads the file and returns the downloaded file object" {
            # Call the function
            $output = Get-FileFromUrl -FileUrl $fileUrl -DestinationPath $destinationPath

            # Verify the mocks
            Assert-MockCalled Split-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq $destinationPath -and $Parent
            }
            Assert-MockCalled Test-Path -Times 2 -Exactly -ParameterFilter {
                $Path -eq "Z:\SomeFolder" -or $Path -eq $destinationPath
            }
            Assert-MockCalled New-Object -Times 1 -Exactly -ParameterFilter {
                $TypeName -eq "System.Net.WebClient"
            }
            $Script:calledFileUrl | Should Be $fileUrl
            $Script:calledDestinationPath | Should Be $destinationPath
            Assert-MockCalled Get-Item -Times 1 -Exactly -ParameterFilter {
                $Path -eq $destinationPath
            }

            # Verify the output
            $output | Should Be "Get-Item success"
        }
    }

    context "when the destination directory does not exist" {
        # Set up the function inputs
        $fileUrl = "https://website.com/file.txt"
        $destinationPath = "Z:\SomeFolder\downloadedFile.txt"

        # Mock Split-Path to return the same path
        mock Split-Path { return "Z:\SomeFolder" }

        # Mock Test-Path to fail
        mock Test-Path { return $false }

        # Mock New-Object to return a mock of System.Net.WebClient
        $Script:calledFileUrl = ""
        $Script:calledDestinationPath = ""
        mock New-Object {
            class WebClientMock {
                [Void]DownloadFile($fileUrl, $destinationPath) {
                    $Script:calledFileUrl = $fileUrl
                    $Script:calledDestinationPath = $destinationPath
                }
            }
            return [WebClientMock]::new()
        }

        # Mock Get-Item to return our own value
        mock Get-Item { return "Get-Item success" }

        it "returns null" {
            # Call the function
            $output = Get-FileFromUrl -FileUrl $fileUrl -DestinationPath $destinationPath -ErrorAction SilentlyContinue

            # Verify the mocks
            Assert-MockCalled Split-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq $destinationPath -and $Parent
            }
            Assert-MockCalled Test-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq "Z:\SomeFolder" -or $Path -eq $destinationPath
            }
            Assert-MockCalled New-Object -Times 0 -Exactly
            $Script:calledFileUrl | Should Be ""
            $Script:calledDestinationPath | Should Be ""
            Assert-MockCalled Get-Item -Times 0

            # Verify the output
            $output | Should Be $null
        }
    }

    context "when there is an issue with the download process" {
        # Set up the function inputs
        $fileUrl = "https://website.com/file.txt"
        $destinationPath = "Z:\SomeFolder\downloadedFile.txt"

        # Mock Split-Path to return the same path
        mock Split-Path { return "Z:\SomeFolder" }

        # Mock Test-Path to always return true (valid)
        mock Test-Path { return $true }

        # Mock New-Object to return a mock of System.Net.WebClient
        $Script:calledFileUrl = ""
        $Script:calledDestinationPath = ""
        mock New-Object {
            class WebClientMock {
                [Void]DownloadFile($fileUrl, $destinationPath) {
                    throw "throwing download exception"
                }
            }
            return [WebClientMock]::new()
        }

        # Mock Get-Item to return our own value
        mock Get-Item { return "Get-Item success" }

        it "returns null" {
            # Call the function
            $output = Get-FileFromUrl -FileUrl $fileUrl -DestinationPath $destinationPath -ErrorAction SilentlyContinue

            # Verify the mocks
            Assert-MockCalled Split-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq $destinationPath -and $Parent
            }
            Assert-MockCalled Test-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq "Z:\SomeFolder" -or $Path -eq $destinationPath
            }
            Assert-MockCalled New-Object -Times 1 -Exactly -ParameterFilter {
                $TypeName -eq "System.Net.WebClient"
            }
            $Script:calledFileUrl | Should Be ""
            $Script:calledDestinationPath | Should Be ""
            Assert-MockCalled Get-Item -Times 0 -Exactly

            # Verify the output
            $output | Should Be $null
        }
    }

    context "when the file was not saved correctly after download" {
        # Set up the function inputs
        $fileUrl = "https://website.com/file.txt"
        $destinationPath = "Z:\SomeFolder\downloadedFile.txt"

        # Mock Split-Path to return the same path
        mock Split-Path { return "Z:\SomeFolder" }

        # Mock Test-Path to only return true when checking for the directory
        # This will fail when checking for the downloaded file
        mock Test-Path {
            return $Path -eq "Z:\SomeFolder"
        }

        # Mock New-Object to return a mock of System.Net.WebClient
        $Script:calledFileUrl = ""
        $Script:calledDestinationPath = ""
        mock New-Object {
            class WebClientMock {
                [Void]DownloadFile($fileUrl, $destinationPath) {
                    $Script:calledFileUrl = $fileUrl
                    $Script:calledDestinationPath = $destinationPath
                }
            }
            return [WebClientMock]::new()
        }

        # Mock Get-Item to return our own value
        mock Get-Item { return "Get-Item success" }

        it "returns null" {
            # Call the function
            $output = Get-FileFromUrl -FileUrl $fileUrl -DestinationPath $destinationPath -ErrorAction SilentlyContinue

            # Verify the mocks
            Assert-MockCalled Split-Path -Times 1 -Exactly -ParameterFilter {
                $Path -eq $destinationPath -and $Parent
            }
            Assert-MockCalled Test-Path -Times 2 -Exactly -ParameterFilter {
                $Path -eq "Z:\SomeFolder" -or $Path -eq $destinationPath
            }
            Assert-MockCalled New-Object -Times 1 -Exactly -ParameterFilter {
                $TypeName -eq "System.Net.WebClient"
            }
            $Script:calledFileUrl | Should Be $fileUrl
            $Script:calledDestinationPath | Should Be $destinationPath
            Assert-MockCalled Get-Item -Times 0 -Exactly

            # Verify the output
            $output | Should Be $null
        }
    }
}