functions/_Install-Vendir.Tests.ps1

# <copyright file="_Install-Vendir.Tests.ps1" company="Endjin Limited">
# Copyright (c) Endjin Limited. All rights reserved.
# </copyright>

Describe "_Install-Vendir" {

    BeforeAll {
        $sut = "$PSScriptRoot\_Install-Vendir.ps1"
        . $sut

        # Set up the script-scoped variable used as default
        $script:VendirVersion = '0.41.0'

        # Default mocks for checksum verification (most tests don't care about checksums)
        Mock Write-Warning { }
        Mock Get-FileHash { return @{ Hash = 'fakehash' } }
    }

    Context "OS Detection" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should use correct OS string in download URL for current platform" {
            $capturedUri = $null
            Mock Invoke-WebRequest { $script:capturedUri = $Uri } -ParameterFilter { $OutFile }

            _Install-Vendir

            if ($IsWindows) {
                $script:capturedUri | Should -Match 'vendir-windows-'
            }
            elseif ($IsMacOS) {
                $script:capturedUri | Should -Match 'vendir-darwin-'
            }
            elseif ($IsLinux) {
                $script:capturedUri | Should -Match 'vendir-linux-'
            }
        }
    }

    Context "Architecture Mapping" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should use correct architecture string in download URL" {
            $capturedUri = $null
            Mock Invoke-WebRequest { $script:capturedUri = $Uri } -ParameterFilter { $OutFile }

            _Install-Vendir

            $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
            switch ($arch) {
                'X64'   { $script:capturedUri | Should -Match '-amd64' }
                'Arm64' { $script:capturedUri | Should -Match '-arm64' }
            }
        }
    }

    Context "Download URL Construction" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should construct URL with correct format including version, OS, and architecture" {
            $capturedUri = $null
            Mock Invoke-WebRequest { $script:capturedUri = $Uri } -ParameterFilter { $OutFile }

            _Install-Vendir -Version '0.45.1'

            $script:capturedUri | Should -Match '^https://github\.com/carvel-dev/vendir/releases/download/v0\.45\.1/vendir-'
        }

        It "Should use default version when not specified" {
            $capturedUri = $null
            Mock Invoke-WebRequest { $script:capturedUri = $Uri } -ParameterFilter { $OutFile }

            _Install-Vendir

            $script:capturedUri | Should -Match "/v$script:VendirVersion/"
        }
    }

    Context "Directory Creation" {
        BeforeAll {
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should create bin/ directory if missing" {
            Mock Test-Path { return $false }
            Mock New-Item { }

            _Install-Vendir

            Should -Invoke New-Item -ParameterFilter { $ItemType -eq 'Directory' -and $Path -match 'bin$' }
        }

        It "Should not create bin/ directory if it exists" {
            Mock Test-Path { return $true }
            Mock New-Item { }

            _Install-Vendir

            Should -Invoke New-Item -Times 0
        }
    }

    Context "Binary Download - Golden Path" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should call Invoke-WebRequest with correct parameters" {
            Mock Invoke-WebRequest { } -Verifiable

            _Install-Vendir

            Should -Invoke Invoke-WebRequest -ParameterFilter {
                $Uri -match 'github\.com/carvel-dev/vendir' -and
                $OutFile -match 'vendir' -and
                $ErrorAction -eq 'Stop'
            }
        }

        It "Should return the destination path" {
            $result = _Install-Vendir

            $result | Should -Match 'bin[/\\]vendir'
        }

        It "Should write information about download progress" {
            _Install-Vendir

            Should -Invoke Write-Information -ParameterFilter { $MessageData -like '*Downloading vendir*' }
        }

        It "Should write information about installation completion" {
            _Install-Vendir

            Should -Invoke Write-Information -ParameterFilter { $MessageData -like '*installed to*' }
        }
    }

    Context "Binary Download - Error Handling" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Write-Information { }
        }

        It "Should throw descriptive error when download fails" {
            Mock Invoke-WebRequest { throw "404 Not Found" }

            { _Install-Vendir } | Should -Throw "*Failed to download vendir*"
        }

        It "Should include URL in error message" {
            Mock Invoke-WebRequest { throw "Connection refused" }

            { _Install-Vendir } | Should -Throw "*github.com/carvel-dev/vendir*"
        }
    }

    Context "Executable Permissions" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
        }

        # Note: $IsWindows, $IsMacOS, $IsLinux are automatic variables that cannot be mocked
        # We test the actual behavior on the current platform

        It "Should handle executable permissions appropriately for current platform" {
            if ($IsWindows) {
                # On Windows, chmod should not be called
                function chmod {}   # You cannot mock a non-existent command
                Mock chmod { throw "chmod should not be called on Windows" }

                { _Install-Vendir } | Should -Not -Throw
            }
            else {
                # On non-Windows, chmod +x should be called
                $chmodCalled = $false
                Mock chmod { $script:chmodCalled = $true }

                _Install-Vendir

                $script:chmodCalled | Should -Be $true
            }
        }
    }

    Context "Unsupported Architecture" {
        It "Should throw for unsupported architecture" {
            # We can't mock [RuntimeInformation]::OSArchitecture, so inline the switch logic
            # to verify the default branch behavior
            $arch = 'RISC-V'
            {
                switch ($arch) {
                    'X64'   { $archStr = 'amd64' }
                    'Arm64' { $archStr = 'arm64' }
                    default { throw "Unsupported architecture: $arch" }
                }
            } | Should -Throw "Unsupported architecture: RISC-V"
        }
    }

    Context "Checksum Verification" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should verify SHA256 checksum after download" {
            Mock Invoke-WebRequest { } -ParameterFilter { $Uri -match 'vendir-' }
            Mock Invoke-WebRequest {
                return @{ Content = "abc123def456 vendir-linux-amd64`nfed789 vendir-windows-amd64.exe`n000aaa vendir-darwin-amd64" }
            } -ParameterFilter { $Uri -match 'checksums\.txt' }
            Mock Get-FileHash {
                $hash = $IsWindows ? 'fed789' : 'abc123def456'
                return @{ Hash = $hash }
            }

            { _Install-Vendir } | Should -Not -Throw

            Should -Invoke Get-FileHash -Times 1
        }

        It "Should throw when checksum does not match" {
            Mock Invoke-WebRequest { } -ParameterFilter { $Uri -match 'vendir-' -and $Uri -notmatch 'checksums' }
            Mock Invoke-WebRequest {
                return @{ Content = "expectedhash vendir-linux-amd64`nfed789 vendir-windows-amd64.exe`n000aaa vendir-darwin-amd64" }
            } -ParameterFilter { $Uri -match 'checksums\.txt' }
            Mock Get-FileHash { return @{ Hash = 'wronghash' } }

            { _Install-Vendir } | Should -Throw "*Checksum mismatch*"
        }

        It "Should warn and continue when checksums.txt download fails" {
            Mock Invoke-WebRequest { } -ParameterFilter { $Uri -match 'vendir-' -and $Uri -notmatch 'checksums' }
            Mock Invoke-WebRequest { throw "404 Not Found" } -ParameterFilter { $Uri -match 'checksums\.txt' }
            Mock Write-Warning { }

            { _Install-Vendir } | Should -Not -Throw

            Should -Invoke Write-Warning -Times 1
        }
    }

    Context "Custom Version" {
        BeforeAll {
            Mock Test-Path { return $true }
            Mock Invoke-WebRequest { }
            Mock Write-Information { }
            if (-not $IsWindows) { Mock chmod { } }
        }

        It "Should use provided version parameter in URL" {
            $capturedUri = $null
            Mock Invoke-WebRequest { $script:capturedUri = $Uri } -ParameterFilter { $OutFile }

            _Install-Vendir -Version '0.99.0'

            $script:capturedUri | Should -Match '/v0\.99\.0/'
        }

        It "Should include custom version in information message" {
            Mock Write-Information { } -Verifiable

            _Install-Vendir -Version '0.99.0'

            Should -Invoke Write-Information -ParameterFilter { $MessageData -like '*0.99.0*' }
        }
    }
}