Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Should -HaveParamter -DefaultValue #2398

Merged
merged 3 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 45 additions & 19 deletions src/functions/assertions/HaveParameter.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
function Get-ParameterInfo {
param (
[Parameter(Mandatory = $true)]
[Management.Automation.CommandInfo]$Command
[Management.Automation.CommandInfo]$Command,
[Parameter(Mandatory = $true)]
[string] $Name
)

# Resolve alias to the actual command so we can access scriptblock
Expand Down Expand Up @@ -71,20 +73,30 @@
}

foreach ($parameter in $parameters) {
if ($Name -ne $parameter.Name.VariablePath.UserPath) {
continue
}

$paramInfo = [PSCustomObject] @{
Name = $parameter.Name.VariablePath.UserPath
Type = "[$($parameter.StaticType.Name.ToLower())]"
HasDefaultValue = $false
DefaultValue = $null
DefaultValueType = $parameter.StaticType.Name
}

# Default value here contains a descriptor object of the default value,
# so this is null only when default value is not present at all, if default value
# is actually $null, this will have an object describing the type and the $null value.
if ($null -ne $parameter.DefaultValue) {
if ($parameter.DefaultValue.PSObject.Properties['Value']) {
$paramInfo.DefaultValue = $parameter.DefaultValue.Value
}
else {
$paramInfo.DefaultValue = $parameter.DefaultValue.Extent.Text
}
# The actual value of the default value can be falsy (e.g. $null, $false or 0)
# use this flag to communicate if default value was found in the AST or not,
# no matter if the actual default value is falsy.
# That is: param($param1 = $false) will set this to true for $param1
# but param($param1) will have this set to false, because there was no default value.
$paramInfo.HasDefaultValue = $true
# $null does not have a Value
$paramInfo.DefaultValue = if ($null -ne $parameter.DefaultValue.Value) { $parameter.DefaultValue.Value } else { $parameter.DefaulValue.Extent }
nohwnd marked this conversation as resolved.
Show resolved Hide resolved
}

$paramInfo
Expand Down Expand Up @@ -181,7 +193,8 @@
if ($ActualValue.Definition -match '^PesterMock_') {
$type = 'mock'
$suggestion = "'Get-Command $($ActualValue.Name) | Where-Object Parameters | Should -HaveParameter ...'"
} else {
}
else {
$type = 'alias'
$suggestion = "using the actual command name. For example: 'Get-Command $($ActualValue.Definition) | Should -HaveParameter ...'"
}
Expand Down Expand Up @@ -249,21 +262,34 @@
}

if ($PSBoundParameters.Keys -contains "DefaultValue") {
$parameterMetadata = Get-ParameterInfo $ActualValue | & $SafeCommands['Where-Object'] { $_.Name -eq $ParameterName }
$actualDefault = if ($parameterMetadata.DefaultValue) {
$parameterMetadata.DefaultValue
}
else {
""
$parameterMetadata = Get-ParameterInfo -Name $ParameterName -Command $ActualValue
if ($null -eq $parameterMetadata) {
# For safety, but this probably won't happen because if the parameter is not on the command we will fail much sooner.
throw "Metadata for parameter '$ParameterName' were not found."
}
$testDefault = ($actualDefault -eq $DefaultValue)

$filters += "the default value$(if ($Negate) {" not"}) to be $(Format-Nicely $DefaultValue)"

if (-not $Negate -and -not $testDefault) {
$buts += "the default value was $(Format-Nicely $actualDefault)"
$defaultIsUnspecified = -not $parameterMetadata.HasDefaultValue
if ($defaultIsUnspecified) {
if ($Negate) {
# We expected the default value to not be something, and it was unspecified, so there is not "but".
}
else {
# We expected the default value to be something, and it was unspecified, so this always fails the assertion.
$buts += "the default value was not specified"
}
}
elseif ($Negate -and $testDefault) {
$buts += "the default value was $(Format-Nicely $DefaultValue)"
else {
$actualDefault = $parameterMetadata.DefaultValue
$testDefault = ($actualDefault -eq $DefaultValue)

if (-not $Negate -and -not $testDefault) {
$buts += "the default value was $(Format-Nicely $actualDefault)"
}
elseif ($Negate -and $testDefault) {
$buts += "the default value was $(Format-Nicely $actualDefault)"
}
}
}

Expand Down
40 changes: 40 additions & 0 deletions tst/functions/assertions/HaveParameter.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,46 @@ InPesterModuleScope {
$err.Exception.Message | Verify-Equal "Expected command Invoke-DummyFunction to have a parameter ParamWithNotNullOrEmptyValidation, which is mandatory, of type [System.TimeSpan] and the default value to be 'wrong value', because of reasons, but it wasn't mandatory, it was of type [System.DateTime] and the default value was '(Get-Date)'."
}

It 'passes when object parameter has default parameter value $null' {
function Test-Parameter {
param ( [Parameter()] [object] $objParam = $null )
}

Get-Command Test-Parameter | Should -HaveParameter 'objParam' -Type 'object' -DefaultValue $null
}

It 'fails when object parameter expects default parameter value $null, but there is none' {
function Test-Parameter {
param ( [Parameter()] [object] $objParam )
}

{ Get-Command Test-Parameter | Should -HaveParameter 'objParam' -Type 'object' -DefaultValue $null } | Should -Throw "aaaa"
nohwnd marked this conversation as resolved.
Show resolved Hide resolved
}

It 'passes when integer parameter has default parameter value 0' {
function Test-Parameter {
param ( [Parameter()] [int] $intParam = 0 )
}

Get-Command Test-Parameter | Should -HaveParameter 'intParam' -Type 'int' -DefaultValue 0
}

It 'passes when bool parameter has default parameter value $true' {
function Test-Parameter {
param ( [Parameter()] [bool] $boolParam = $true )
nohwnd marked this conversation as resolved.
Show resolved Hide resolved
}

Get-Command Test-Parameter | Should -HaveParameter 'boolParam' -Type 'bool' -DefaultValue $true
}

It 'passes when bool parameter has default parameter value $false' {
function Test-Parameter {
param ( [Parameter()] [bool] $boolParam = $false )
}

Get-Command Test-Parameter | Should -HaveParameter 'boolParam' -Type 'bool' -DefaultValue $false
}

if ($PSVersionTable.PSVersion.Major -ge 5) {
It "returns the correct assertion message when parameter ParamWithNotNullOrEmptyValidation is not mandatory, of the wrong type, has a different default value than expected and has no ArgumentCompleter" {
$err = { Get-Command "Invoke-DummyFunction" | Should -HaveParameter ParamWithNotNullOrEmptyValidation -Mandatory -Type [TimeSpan] -DefaultValue "wrong value" -HasArgumentCompleter -Because 'of reasons' } | Verify-AssertionFailed
Expand Down