Functions/Send-Email.Tests.ps1

describe "BitTitan.Runbooks.Common/Send-Email" -Tag "module", "unit" {

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

    # Set up the function inputs
    $emailCredentials = [PSCredential]::new("username", ("password" | ConvertTo-SecureString -AsPlainText -Force))
    $smtpMailServer = "smtp.server.com"
    $portNumber = 123
    $destinationEmailAddress = "email@domain.com"
    $emailSubject = "Email subject"
    $emailBody = "Email body."
    $attachmentFilePaths = @("Z:\file1.txt", "Z:\file2.txt", "Z:\file3.txt")
    $attachmentFilePath = "Z:\singleFile.txt"

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

    # Declare our own Send-MailMessage
    function Send-MailMessage {
        param ($From, $Credential, $SmtpServer, $Port, $To, $Subject, $Body, $Attachments)
    }

    context "when there are no issues" {
        # Mock Compress-Archive
        mock Compress-Archive {}

        # Mock Get-Item
        mock Get-Item { return @{ FullName = "Z:\TempArchive.zip" } }

        # Mock Send-MailMessage
        mock Send-MailMessage {}

        it "sends the email with multiple attachments" {
            # Call the function
            $output = Send-Email -EmailCredentials $emailCredentials -SmtpServer $smtpMailServer -PortNumber $portNumber `
                -DestinationEmailAddress $destinationEmailAddress -EmailSubject $emailSubject -EmailBody $emailBody `
                -AttachmentFilePaths $attachmentFilePaths

            # Verify the mocks
            Assert-MockCalled Send-MailMessage -Times 1 -Exactly -ParameterFilter {
                $From -eq "username" -and $Credential -eq $emailCredentials -and
                $SmtpServer -eq $smtpMailServer -and $Port -eq $portNumber -and
                $To -eq $destinationEmailAddress -and $Subject -eq $emailSubject -and
                $Body -eq $emailBody -and $Attachments -eq "Z:\TempArchive.zip"
            }

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

        it "sends the email with a single attachment" {
            # Call the function
            $output = Send-Email -EmailCredentials $emailCredentials -SmtpServer $smtpMailServer -PortNumber $portNumber `
                -DestinationEmailAddress $destinationEmailAddress -EmailSubject $emailSubject -EmailBody $emailBody `
                -AttachmentFilePath $attachmentFilePath

            # Verify the mocks
            Assert-MockCalled Send-MailMessage -Times 1 -Exactly -ParameterFilter {
                $From -eq "username" -and $Credential -eq $emailCredentials -and
                $SmtpServer -eq $smtpMailServer -and $Port -eq $portNumber -and
                $To -eq $destinationEmailAddress -and $Subject -eq $emailSubject -and
                $Body -eq $emailBody -and $Attachments -eq "Z:\singleFile.txt"
            } -Scope it

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

    # Declare the functions to throw exceptions
    $functionsToThrowExceptions = @(
        "Compress-Archive",
        "Get-Item",
        "Send-MailMessage"
    )
    foreach ($function in $functionsToThrowExceptions) {
        context "when $($function) throws an exception" {
            # Mock Compress-Archive
            mock Compress-Archive {}

            # Mock Get-Item
            mock Get-Item { return @{ FullName = "Z:\TempArchive.zip" } }

            # Mock Send-MailMessage
            mock Send-MailMessage {}

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

            it "fails to send the email and outputs an error message" {
                # Call the function
                $output = Send-Email -EmailCredentials $emailCredentials -SmtpServer $smtpMailServer -PortNumber $portNumber `
                    -DestinationEmailAddress $destinationEmailAddress -EmailSubject $emailSubject -EmailBody $emailBody `
                    -AttachmentFilePaths $attachmentFilePaths -ErrorAction SilentlyContinue -ErrorVariable errorVariable

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

    context "when Get-Item fails" {
        # Mock Compress-Archive
        mock Compress-Archive {}

        # Mock Get-Item
        mock Get-Item {}

        # Mock Send-MailMessage
        mock Send-MailMessage {}

        it "fails to send the email and outputs an error message" {
            # Call the function
            $output = Send-Email -EmailCredentials $emailCredentials -SmtpServer $smtpMailServer -PortNumber $portNumber `
                -DestinationEmailAddress $destinationEmailAddress -EmailSubject $emailSubject -EmailBody $emailBody `
                -AttachmentFilePaths $attachmentFilePaths -ErrorAction SilentlyContinue -ErrorVariable errorVariable

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