Skip to content

Instantly share code, notes, and snippets.

@gerron
Last active May 8, 2020 17:31
Show Gist options
  • Save gerron/835e0a082aa4bd630ae91b5eab383e1e to your computer and use it in GitHub Desktop.
Save gerron/835e0a082aa4bd630ae91b5eab383e1e to your computer and use it in GitHub Desktop.
#Function
#Get-Token and Get-PlainTextFromSecureString are private functions within the module
function Get-Projects
{
[CmdletBinding()]
[OutputType([Object[]])]
param (
[Parameter(Mandatory = $false)]
[PSCredential]$Credential
)
Begin
{
$ErrorActionPreference = "Stop"
if(-not ($PSBoundParameters.ContainsKey("Credential")))
{
$Credential = Get-Credential
}
$token = Get-Token $Credential
}
Process
{
$headers = @{
'Authorization' = ("Bearer{0}" -f $token)
'Content-Type' = 'application/json;v=1.0'
}
$results = Invoke-RestMethod -uri 'https://internalEndpoint.domain.corp/internalsoftware/projects' -Method GET -Headers $headers
if(-not ($results.count -gt 0))
{
throw "There was an error retrieving a Projects. Projects returned are empty."
}
$results
}
End
{
}
}
#Pester Test
#ModuleArtifact is the path to the IntermnModule file
Import-Module $ModuleArtifact -Verbose -Force
Describe "Get-Projects" {
InModuleScope InternalModule {
Mock Get-PlaintextFromSecureString {}
Context "when not passing in a credential" {
$password = ConvertTo-SecureString "Test1234!" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ('TestUser', $password)
Mock Get-Credential { $credential }
Mock Invoke-RestMethod {}
Mock Get-Token { "token" }
It "Get-Credential is called prompting for a password" {
GetProjects
Assert-MockCalled Get-Credential -Exactly 1
Assert-MockCalled Get-PlaintextFromSecureString -Exactly 1 -ParameterFilter { $SecurePassword -eq $credential.password }
}
}
Context "when passing in a credential" {
$password = ConvertTo-SecureString "Test1234!" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ('TestUser', $password)
$nonEmptyArray = @(1,2,3,4)
Mock Invoke-RestMethod { $nonEmptyArray }
Mock Get-Token { "token" }
It "Get-Credential is not called prompting for a username and password" {
GetProjects -Credential $credential
Assert-MockCalled Get-Credential 0
}
}
Context "when Invoke-RestMethod returns an empty empty array of projects" {
$password = ConvertTo-SecureString "Test1234!" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ('TestUser', $password)
Mock Get-Token { "token" }
It "GetProjects throws an exception" {
$emptyArray = @()
Mock Invoke-RestMethod { $emptyArray }
{ GetProjects -Credential $credential } | Should Throw "There was an error retrieving a Projects. Projects are empty."
Assert-MockCalled Invoke-RestMethod -Exactly 1
}
}
}
}
@cathalmchale
Copy link

You mention you tried the Mock verbatim with what's working my side. Also you're on a more recent version of pester. So only two things I can think of trying.

  1. Move Get-Credential out of Begin { .. } and into Process { .. }

I know it's not what you want, but worth a try. If it makes the difference then could consider some kind of a wrapper to avoid many calls to Get-Credential.

  1. If it's not Pester that's the problem, then how about the version of Powershell. Maybe Get-Credential implementation changed at some stage.

@cathalmchale
Copy link

cathalmchale commented May 8, 2020

The one other thing I can think of. I notice you have a Import-Module "path to module".
Probably unrelated to your issue, but I had problems with this is the past. Had to tidy up my module definitions and packaging slightly so that I no longer had to import via a path to the psm1 file. So for example, all my deployments look like this:

$env:PSModulePath = $env:PSModulePath + ";$($(Get-Location).Path)"
Import-Module Pivot.DockerAdmin

You probably know this stuff, but tripped me for a while. From my notes...

  1. Make sure your code is packaged properly^ (note: dir name same as module and manifest, then you import from level-up that includes this manifest parent directory)
  2. Include this folder in the PSModule path:
    1. Either by dropping it in one of the persistent paths.
    2. Or adding the path to it for the session**
  3. Now just use "Import-Module Hue.Script" rather than "Import-Module .\Hue.Script.psm1"
    1. If you don't do this, the manifest isn't considered at all.
  4. Make sure your manifest has the correct info - it's of critical importance now***
    1. E.g. include helper functions through NestedModules
    2. Include other files e.g. Const.psd1. Then can use within module like:
      1. $Const = Get-Content "$PSScriptRoot\Const.psd1" | Out-String | Invoke-Expression
      2. [or powershell 5+] >$Const = Import-PowerShellDataFile "$PSScriptRoot\Const.psd1"

^use this folder structure
root
--Hue.Script


----Hue.Script.psm1
----Hue.Script.psd1
----Nested.Module.psm1

@gerron
Copy link
Author

gerron commented May 8, 2020

You mention you tried the Mock verbatim with what's working my side. Also you're on a more recent version of pester. So only two things I can think of trying.

1. Move Get-Credential out of Begin { .. } and into Process { .. }

I know it's not what you want, but worth a try. If it makes the difference then could consider some kind of a wrapper to avoid many calls to Get-Credential.

1. If it's not Pester that's the problem, then how about the version of Powershell. Maybe Get-Credential implementation changed at some stage.

Tried moving into process block and same result. I'm using Powershell version 5.1 and may try experimenting with a different version. I'm even considering removing the get-credential call all together.

I'll keep experimenting and provide any updates here for future reference. I'll have to timebox this though for just today :/.

Thanks again!

@gerron
Copy link
Author

gerron commented May 8, 2020

The one other thing I can think of. I notice you have a Import-Module "path to module".
Probably unrelated to your issue, but I had problems with this is the past. Had to tidy up my module definitions and packaging slightly so that I no longer had to import via a path to the psm1 file. So for example, all my deployments look like this:

$env:PSModulePath = $env:PSModulePath + ";$($(Get-Location).Path)"
Import-Module Pivot.DockerAdmin

You probably know this stuff, but tripped me for a while. From my notes...

1. Make sure your code is packaged properly^ (note: dir name same as module and manifest, then you import from level-up that includes this manifest parent directory)

2. Include this folder in the PSModule path:
   
   1. Either by dropping it in one of the persistent paths.
   2. Or adding the path to it for the session**

3. Now just use "Import-Module Hue.Script" rather than "Import-Module .\Hue.Script.psm1"
   
   1. If you don't do this, the manifest isn't considered at all.

4. Make sure your manifest has the correct info - it's of critical importance now***
   
   1. E.g. include helper functions through NestedModules
   2. Include other files e.g. Const.psd1. Then can use within module like:
      
      1. $Const = Get-Content "$PSScriptRoot\Const.psd1" | Out-String | Invoke-Expression
      2. [or powershell 5+] >$Const = Import-PowerShellDataFile "$PSScriptRoot\Const.psd1"

^use this folder structure
root
--Hue.Script

----Hue.Script.psm1
----Hue.Script.psd1
----Nested.Module.psm1

Noted thanks!

@gerron
Copy link
Author

gerron commented May 8, 2020

@cathalmchale I ended up making the credential object a mandatory parameter which accomplishes the same goal. If I call my function without specifying a credential parameter, it prompts me using get-credential. I didn't need the logic around calling get-credential at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment