Connect-MetasysAccount.Tests.ps1
<# Tests for ConnectMetasys [ ] If Vault is not specified, don't use vault [ ] If Host is not specified, prompt [ ] If UserName is not specified, and unique username for host not found in vault, prompt [ ] If Password is not specified, and password for host/user not found in value, prompt [ ] If Version is not specified, default to latest [ ] Expect Invoke-RestMethod https://$host/api/v$version/api/log -Method Post -Body '{ "usernaem": "$username", "password": "$passwordPlainText"} #> <# Combinations #> BeforeAll { $mod = Import-Module -Name ./ -Force -PassThru . ./MockConsole.ps1 Set-Variable -Name LatestVersion -Value (Get-MetasysLatestVersion) -Option Constant function CreateLoginResponse { param( [string]$accessToken = "test token", [DateTimeOffset]$expires = [DateTimeOffset]::UtcNow ) @{ accessToken = $accessToken; expires = $expires } } } Describe "Connect-Metasys" -Tag "Unit" { Describe "When Secret Vault does not have the credentials" { Context "No parameters supplied and preferences for version and skip check not set" { BeforeAll { Clear-MetasysEnvVariables $mockConsole = [MockConsole]::new() Mock Read-Host -ModuleName MetasysRestClient { $mockConsole.ReadHost($Prompt) } $loginResponse = CreateLoginResponse Mock Invoke-RestMethod -ModuleName MetasysRestClient { $loginResponse } Mock Get-SavedMetasysPassword -ModuleName MetasysRestClient Mock Get-SavedMetasysUsers -ModuleName MetasysRestClient Mock Set-SavedMetasysPassword -ModuleName MetasysRestClient Mock Get-MetasysDefaultApiVersion -ModuleName MetasysRestClient # qualify with $script to avoid unused-vars warnings from PSScriptAnalyzer $script:response = Connect-MetasysAccount } AfterAll { Clear-MetasysEnvVariables } It "Should prompt for MetasysHost" { Should -Invoke Read-Host -ModuleName MetasysRestClient -ParameterFilter { $Prompt -eq [MockConsole]::MetasysHostPrompt } -Times 1 -Exactly -Scope Context } It "Should prompt for UserName" { Should -Invoke Read-Host -ModuleName MetasysRestClient -ParameterFilter { $Prompt -eq [MockConsole]::UserNamePrompt } -Times 1 -Exactly -Scope Context } It "Should prompt for Password" { Should -Invoke Read-Host -ModuleName MetasysRestClient -ParameterFilter { $Prompt -eq [MockConsole]::PasswordPrompt } -Times 1 -Exactly -Scope Context } It "Should invoke Invoke-RestMethod with correct url, body, method and content type" { $expectedBody = @{ username = $mockConsole.GetUserName(); password = $mockConsole.GetPasswordAsPlainText(); } | ConvertTo-Json -Compress Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -ParameterFilter { $Method -eq 'Post' -and $ContentType -eq 'application/json' -and $Uri.ToString() -eq "https://$($mockConsole.GetMetasysHost())/api/v$LatestVersion/login" -and ($Body | ConvertFrom-Json | ConvertTo-Json -Compress) -eq $expectedBody } -Times 1 -Exactly -Scope Context } It 'Should set $env:METASYS_ACCESS_TOKEN as an encrypted string whose decrypted value matches what we expect' { $env:METASYS_ACCESS_TOKEN | ConvertTo-SecureString | ConvertFrom-SecureString -AsPlainText | Should -Be $loginResponse.accessToken } It 'Should set $env:METASYS_EXPIRES' { $env:METASYS_EXPIRES | Should -Be ($loginResponse.expires.ToString("o")) } It 'Should set $env:METASYS_HOST' { $env:METASYS_HOST | Should -Be $mockConsole.GetMetasysHost() } It 'Should return nothing' { $script:response | Should -BeNullOrEmpty } It 'Should save credentials if vault is available' { Should -Invoke Set-SavedMetasysPassword -ModuleName MetasysRestClient -ParameterFilter { $SiteHost -eq $mockConsole.GetMetasysHost() -and $UserName -eq $mockConsole.GetUserName() -and (ConvertFrom-SecureString -AsPlainText $Password) -eq $mockConsole.GetPasswordAsPlainText() } -Scope Context -Times 1 -Exactly } } Context "No parameters passed, but preferences for version are set" { BeforeAll { Clear-MetasysEnvVariables $mockConsole = [MockConsole]::new() Mock Read-Host -ModuleName MetasysRestClient { $mockConsole.ReadHost($Prompt) } $loginResponse = CreateLoginResponse Mock Invoke-RestMethod -ModuleName MetasysRestClient { $loginResponse } Mock Get-SavedMetasysPassword -ModuleName MetasysRestClient Mock Get-SavedMetasysUsers -ModuleName MetasysRestClient Mock Set-SavedMetasysPassword -ModuleName MetasysRestClient $oldVersion = "3" # So in this test the following two preferences have been set by the user Mock Get-MetasysDefaultApiVersion -ModuleName MetasysRestClient { $oldVersion } # qualify with $script to avoid unused-vars warnings from PSScriptAnalyzer $script:response = Connect-MetasysAccount } AfterAll { Clear-MetasysEnvVariables } It "Should invoke Invoke-RestMethod with correct version" { $expectedBody = @{ username = $mockConsole.GetUserName(); password = $mockConsole.GetPasswordAsPlainText(); } | ConvertTo-Json -Compress Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -ParameterFilter { $Method -eq 'Post' -and $ContentType -eq 'application/json' -and $Uri.ToString() -eq "https://$($mockConsole.GetMetasysHost())/api/v$oldVersion/login" -and ($Body | ConvertFrom-Json | ConvertTo-Json -Compress) -eq $expectedBody } -Times 1 -Exactly -Scope Context } } Context "When host, username, and password are passed as parameters" { BeforeAll { Clear-MetasysEnvVariables Mock Read-Host -ModuleName MetasysRestClient $loginResponse = CreateLoginResponse Mock Invoke-RestMethod -ModuleName MetasysRestClient { $loginResponse } Mock Get-MetasysDefaultApiVersion -ModuleName MetasysRestClient $script:metasysHost = "aHost" $script:userName = "aUser" $script:securePassword = "aPassword" | ConvertTo-SecureString -AsPlainText # qualify with $script to avoid unused-vars warnings from PSScriptAnalyzer $script:response = Connect-MetasysAccount -MetasysHost $script:metasysHost ` -UserName $script:userName -Password $script:securePassword } AfterAll { Clear-MetasysEnvVariables } It "Should not prompt for anything" { Should -Invoke Read-Host -ModuleName MetasysRestClient -Times 0 -Exactly } It "Should invoke Invoke-RestMethod with correct url, body, method and content type" { $expectedBody = @{ username = $script:userName password = $script:securePassword | ConvertFrom-SecureString -AsPlainText } | ConvertTo-Json -Compress Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -ParameterFilter { $Method -eq 'Post' -and $ContentType -eq 'application/json' -and $Uri.ToString() -eq "https://$($script:metasysHost)/api/v$LatestVersion/login" -and ($Body | ConvertFrom-Json | ConvertTo-Json -Compress) -eq $expectedBody } -Times 1 -Exactly -Scope Context } It 'Should set $env:METASYS_ACCESS_TOKEN' { $env:METASYS_ACCESS_TOKEN | ConvertTo-SecureString | ConvertFrom-SecureString -AsPlainText | Should -Be $loginResponse.accessToken } It 'Should set $env:METASYS_EXPIRES' { $env:METASYS_EXPIRES | Should -Be ($loginResponse.expires.ToString("o")) } It 'Should set $env:METASYS_HOST' { $env:METASYS_HOST | Should -Be $script:metasysHost } It 'Should return nothing' { $script:response | Should -BeNullOrEmpty } } Context "Only alias is passed and the config file includes username, hostname, skip-check and version" { BeforeAll { Clear-MetasysEnvVariables $script:metasysHost = "host.com" $script:version = "4" $script:username = "api" Mock Get-Content -ModuleName MetasysRestClient { @" { "hosts": { "alias": "host", "hostname": "$($script:metasysHost)", "username": "$($script:username)", "version": "$($script:version)", "skip-certificate-check": true } } "@ } $loginResponse = CreateLoginResponse Mock Invoke-RestMethod -ModuleName MetasysRestClient { $loginResponse } Mock Get-MetasysDefaultApiVersion -ModuleName MetasysRestClient $mockConsole = [MockConsole]::new() Mock Read-Host -ModuleName MetasysRestClient { $mockConsole.ReadHost($Prompt) } $script:response = Connect-MetasysAccount host } AfterAll { Clear-MetasysEnvVariables } It 'Should invoke cma with correct hostname, username, version and the skip-check' { Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -ParameterFilter { $Uri.ToString() -eq "https://$($script:metasysHost)/api/v$($script:version)/login" -and ($Body | ConvertFrom-Json).username -eq $script:username -and $SkipCertificateCheck -eq $true } -Times 1 -Exactly -Scope Context } It 'Should save the value of skip certificate check for calls to imm' { $env:METASYS_SKIP_CERTIFICATE_CHECK | Should -Be 'True' } } } Describe "When secret vault is used but not configured" { } Describe "When secret vault is used and has only one set of credentials for host" { Context "When no parameters are passed" { BeforeAll { $mockConsole = [MockConsole]::new() Mock Read-Host -ModuleName MetasysRestClient { $mockConsole.ReadHost($Prompt) } Mock Invoke-RestMethod -ModuleName MetasysRestClient { CreateLoginResponse } Mock Get-SavedMetasysUsers -ModuleName MetasysRestClient { , [PSCustomObject]@{ UserName = $mockConsole.GetUserName() } } -ParameterFilter { $SiteHost -eq $mockConsole.GetMetasysHost() } Mock Get-SavedMetasysPassword -ModuleName MetasysRestClient { $mockConsole.GetPassword() } -ParameterFilter { $SiteHost -eq $mockConsole.GetMetasysHost() -and $UserName -eq $mockConsole.GetUserName() } Connect-MetasysAccount } It "Prompts for input exactly 1 time and that is for Metasys Host" { Should -Invoke Read-Host -ModuleName MetasysRestClient -Times 1 -Exactly -Scope Context Should -Invoke Read-Host -ModuleName MetasysRestClient -Scope Context -ParameterFilter { $Prompt -eq [MockConsole]::MetasysHostPrompt } } It "Invokes Invoke-MetasysMethod with correct username and password in the body" { $expectedBody = @{ username = $mockConsole.GetUserName(); password = $mockConsole.GetPasswordAsPlainText(); } | ConvertTo-Json -Compress Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -ParameterFilter { ($Body | ConvertFrom-Json | ConvertTo-Json -Compress) -eq $expectedBody } -Scope Context } } } Describe "Version Handling" { BeforeAll { Clear-MetasysEnvVariables Mock Read-Host -ModuleName MetasysRestClient { if ($Prompt -eq "Metasys Host") { "hostname" } else { "password" | ConvertTo-SecureString -AsPlainText } } } AfterAll { Clear-MetasysEnvVariables } Context "When a valid version is specified" { BeforeAll { $version = 3 } It "Should be used in the Uri" { Mock Invoke-RestMethod -ModuleName MetasysRestClient { CreateLoginResponse } Connect-MetasysAccount -Version $version Should -Invoke Invoke-RestMethod -ModuleName MetasysRestClient -Times 1 -Exactly -ParameterFilter { $Uri.ToString() -eq "https://hostname/api/v$version/login" } -Scope Context } It 'Should be saved to $env:Version' { $env:METASYS_VERSION | Should -Be $version } } } Describe "Error Processing" { Context "When the request throws an exception" { It "Should stop processing immediately to avoid additional errors" { Mock Invoke-RestMethod -ModuleName MetasysRestClient { throw [System.Net.Http.HttpRequestException]::new() } Mock Write-Information -ModuleName MetasysRestClient { Connect-MetasysAccount -h oas -u user -p (password | ConvertTo-SecureString -AsPlainText) } | Should -Throw Should -Invoke Write-Information -ModuleName MetasysRestClient -Exactly -Times 0 -ParameterFilter { $MessageData -match "Login was successful.*" } } } } } |