Functions/Import-BT_Module.Tests.ps1

describe "BitTitan.Runbooks.Modules/Import-BT_Module" -Tags "module", "unit" {

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

    # Declare Get-Module to override the existing function
    # If we don't do this the mock won't work
    function Get-Module {
        param ($Name, [Switch]$ListAvailable)
    }

    # Retrieve the current BitTitan Runbook Environment settings
    $previousPSDefaultParameterValues = $Global:PSDefaultParameterValues

    context "when running on the Prod local machine environment" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {
            return "module imported"
        }

        # Set up the settings
        $Global:PSDefaultParameterValues["*-BT_*:Environment"] = "BT"
        $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $true

        it "imports the module" {
            # Call the function
            Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

            # Verify the mocks
            Assert-MockCalled Install-Module -Times 0 -Exactly -Scope it
            Assert-MockCalled Import-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Get-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it

            # Verify the output
            $errorVariable | Should BeNullOrEmpty
        }
    }

    context "when running on the Beta local machine environment" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {
            return "module imported"
        }

        # Set up the settings
        $Global:PSDefaultParameterValues["*-BT_*:Environment"] = "Beta"
        $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $true

        it "imports the module" {
            # Call the function
            Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

            # Verify the mocks
            Assert-MockCalled Install-Module -Times 0 -Exactly -Scope it
            Assert-MockCalled Import-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Get-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it

            # Verify the output
            $errorVariable | Should BeNullOrEmpty
        }
    }

    context "when running on the Prod MSPComplete environment" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {
            if (!$ListAvailable) {
                return "module imported"
            }
        }

        # Set up the settings
        $Global:PSDefaultParameterValues["*-BT_*:Environment"] = "BT"
        $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $false

        it "installs and imports the module" {
            # Call the function
            Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

            # Verify the mocks
            Assert-MockCalled Install-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Import-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Get-Module -Times 2 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it

            # Verify the output
            $errorVariable | Should BeNullOrEmpty
        }
    }

    context "when running on the Beta MSPComplete environment" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {
            if (!$ListAvailable) {
                return "module imported"
            }
        }

        # Mock Get-Item
        mock Get-Item {
            return [PSCustomObject]@{
                FullName = "FullName"
            }
        }

        # Mock Move-Item
        mock Move-Item {}

        # Set up the settings
        $Global:PSDefaultParameterValues["*-BT_*:Environment"] = "Beta"
        $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $false

        it "installs and imports the module" {
            # Call the function
            Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

            # Verify the mocks
            Assert-MockCalled Install-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule.Beta"
            } -Scope it
            Assert-MockCalled Import-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Get-Module -Times 2 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it

            # Verify the output
            $errorVariable | Should BeNullOrEmpty
        }
    }

    context "when running on the default environment (Beta MSPComplete)" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {
            if (!$ListAvailable) {
                return "module imported"
            }
        }

        # Mock Get-Item
        mock Get-Item {
            return [PSCustomObject]@{
                FullName = "FullName"
            }
        }

        # Mock Move-Item
        mock Move-Item {}

        # Set up the settings
        $Global:PSDefaultParameterValues = @{}

        it "installs and imports the module" {
            # Call the function
            Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

            # Verify the mocks
            Assert-MockCalled Install-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule.Beta"
            } -Scope it
            Assert-MockCalled Import-Module -Times 1 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it
            Assert-MockCalled Get-Module -Times 2 -Exactly -ParameterFilter {
                $Name -eq "BitTitan.Runbooks.TestModule"
            } -Scope it

            # Verify the output
            $errorVariable | Should BeNullOrEmpty
        }
    }

    # Declare the functions to throw an exception
    $functionsToThrowExceptions = @(
        "Install-Module",
        "Import-Module",
        "Get-Module"
    )
    foreach ($function in $functionsToThrowExceptions) {
        context "when $($function) throws an exception" {
            # Mock Install-Module
            mock Install-Module {}

            # Mock Import-Module
            mock Import-Module {}

            # Mock Get-Module
            mock Get-Module {
                if (!$ListAvailable) {
                    return "module imported"
                }
            }

            # Mock Get-Item
            mock Get-Item {
                return [PSCustomObject]@{
                    FullName = "FullName"
                }
            }

            # Mock Move-Item
            mock Move-Item {}

            # Mock the function to throw an exception
            mock $function {
                throw "throws exception"
            }

            it -TestCases @(
                @{
                    Environment = "Beta"
                    IsRunningOnLocalMachine = $false
                },
                @{
                    Environment = "Beta"
                    IsRunningOnLocalMachine = $true
                },
                @{
                    Environment = "BT"
                    IsRunningOnLocalMachine = $false
                },
                @{
                    Environment = "BT"
                    IsRunningOnLocalMachine = $true
                }
            ) "fails to install and/or import the module in the <Environment> environment and when IsRunningOnLocalMachine is <IsRunningOnLocalMachine>, and outputs an error." {
                param ($Environment, $IsRunningOnLocalMachine)

                # Set up the settings
                $Global:PSDefaultParameterValues["*-BT_*:Environment"] = $Environment
                $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $IsRunningOnLocalMachine

                # Call the function
                Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

                # Verify the output
                # If running on local machine and the function to throw is Install-Module,
                # there will be no error output as Install-Module will not be called.
                if ($function -ne "Install-Module" -and !$IsRunningOnLocalMachine) {
                    $errorVariable | Should Not BeNullOrEmpty
                }
            }
        }
    }

    context "when the module fails to be imported" {
        # Mock Install-Module
        mock Install-Module {}

        # Mock Import-Module
        mock Import-Module {}

        # Mock Get-Module
        mock Get-Module {}

        # Mock Get-Item
        mock Get-Item {
            return [PSCustomObject]@{
                FullName = "FullName"
            }
        }

        # Mock Move-Item
        mock Move-Item {}

        it -TestCases @(
                @{
                    Environment = "Beta"
                    IsRunningOnLocalMachine = $false
                },
                @{
                    Environment = "Beta"
                    IsRunningOnLocalMachine = $true
                },
                @{
                    Environment = "BT"
                    IsRunningOnLocalMachine = $false
                },
                @{
                    Environment = "BT"
                    IsRunningOnLocalMachine = $true
                }
            ) "it outputs an error." {
                param ($Environment, $IsRunningOnLocalMachine)

                # Set up the settings
                $Global:PSDefaultParameterValues["*-BT_*:Environment"] = $Environment
                $Global:PSDefaultParameterValues["*-BT_*:IsRunningOnLocalMachine"] = $IsRunningOnLocalMachine

                # Call the function
                Import-BT_Module BitTitan.Runbooks.TestModule -Quiet -ErrorAction SilentlyContinue -ErrorVariable errorVariable

                # Verify the output
                $errorVariable | Should Not BeNullOrEmpty
            }
    }

    # Reset the BitTitan Runbook Environment settings
    $Global:PSDefaultParameterValues = $previousPSDefaultParameterValues
}