From 7bed1c1be3e3466b4bddc02fe67019c7de76c57f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 29 Dec 2022 17:59:31 +0100 Subject: [PATCH 1/7] Add `Assert-RequiredCommandParameter` and `Test-AccountRequirePassword` --- CHANGELOG.md | 16 +++ .../Assert-RequiredCommandParameter.ps1 | 93 ++++++++++++++++ source/Public/Test-AccountRequirePassword.ps1 | 73 +++++++++++++ .../Unit/Public/Assert-ElevatedUser.Tests.ps1 | 18 ++- .../Assert-RequiredCommandParameter.Tests.ps1 | 103 ++++++++++++++++++ .../Test-AccountRequirePassword.Tests.ps1 | 75 +++++++++++++ .../Unit/Public/Test-IsNumericType.Tests.ps1 | 36 ++---- 7 files changed, 379 insertions(+), 35 deletions(-) create mode 100644 source/Public/Assert-RequiredCommandParameter.ps1 create mode 100644 source/Public/Test-AccountRequirePassword.ps1 create mode 100644 tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 create mode 100644 tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9770f0b..444212c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added public function `Assert-RequiredCommandParameter` that throws an + exception if a specified parameter is not assigned a value and optionally + only if a specified criteria is not met - [Issue #92](https://github.com/dsccommunity/DscResource.Common/issues/92) + - Related to SqlServerDsc [Issue #1796](https://github.com/dsccommunity/SqlServerDsc/issues/1796). +- Added public function `Test-AccountRequirePassword` that throws an + exception if a specified parameter is not assigned a value and optionally + only if a specified criteria is not met - [Issue #93](https://github.com/dsccommunity/DscResource.Common/issues/93) + - Related to SqlServerDsc [Issue #1794](https://github.com/dsccommunity/SqlServerDsc/issues/1794). + +### Fixed + +- Fixed unit tests for `Assert-ElevatedUser` and `Test-IsNumericType` so + the public function is tested correctly using the exported function. + ## [0.13.1] - 2022-12-18 ### Changed diff --git a/source/Public/Assert-RequiredCommandParameter.ps1 b/source/Public/Assert-RequiredCommandParameter.ps1 new file mode 100644 index 0000000..95c11d9 --- /dev/null +++ b/source/Public/Assert-RequiredCommandParameter.ps1 @@ -0,0 +1,93 @@ +<# + .SYNOPSIS + Assert that required parameters has been specified. + + .DESCRIPTION + Assert that required parameters has been specified, and throws an exception if not. + + .PARAMETER BoundParameter + A hashtable containing the parameters to evaluate. Normally this is set to + $PSBoundParameters. + + .PARAMETER RequiredParameter + One or more parameter names that is required to have been specified. + + .PARAMETER IfParameterPresent + One or more parameter names that if specified will trigger the evaluation. + If neither of the parameter names has been specified the evaluation of required + parameters are not made. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') + + Throws an exception if either of the two parameters are not specified. + + .EXAMPLE + Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') + + Throws an exception if the parameter 'Property1' is specified and one of the required parameters are not. + + .OUTPUTS + None. + + .NOTES + This command should probably be a parmeter set of the command Assert-BoundParameter + in the module DscResource.Common, instead of being a separate command. +#> +function Assert-RequiredCommandParameter +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $BoundParameter, + + [Parameter(Mandatory = $true)] + [System.String[]] + $RequiredParameter, + + [Parameter()] + [System.String[]] + $IfParameterPresent + ) + + $evaluateRequiredParameter = $true + + if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $hasIfParameterPresent = $BoundParameter.Keys.Where( { $_ -in $IfParameterPresent } ) + + if (-not $hasIfParameterPresent) + { + $evaluateRequiredParameter = $false + } + } + + if ($evaluateRequiredParameter) + { + foreach ($parameter in $RequiredParameter) + { + if ($parameter -notin $BoundParameter.Keys) + { + $errorMessage = if ($PSBoundParameters.ContainsKey('IfParameterPresent')) + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f ($RequiredParameter -join ''', '''), ($IfParameterPresent -join ''', ''') + } + else + { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f ($RequiredParameter -join ''', ''') + } + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $errorMessage, + 'ARCP0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Command parameters' + ) + ) + } + } + } +} diff --git a/source/Public/Test-AccountRequirePassword.ps1 b/source/Public/Test-AccountRequirePassword.ps1 new file mode 100644 index 0000000..ee68538 --- /dev/null +++ b/source/Public/Test-AccountRequirePassword.ps1 @@ -0,0 +1,73 @@ +<# + .SYNOPSIS + Returns wether the specified service account require password to be provided. + + .DESCRIPTION + Returns wether the specified service account require password to be provided. + If the account is a (global) managed service account, virtual account, or a + built-in account then there is no need to provide a password. + + .PARAMETER Name + Credential name for the service account. + + .EXAMPLE + Test-AccountRequirePassword -Name 'DOMAIN\MyMSA$' + + Returns $false as a manged service account does not need a password. + + .EXAMPLE + Test-AccountRequirePassword -Name 'DOMAIN\MySqlUser' + + Returns $true as a user account need a password. + + .EXAMPLE + Test-AccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' + + Returns $false as a virtual account does not need a password. + + .OUTPUTS + [System.Boolean] +#> +function Test-AccountRequirePassword +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + # Assume local or domain service account. + $requirePassword = $true + + switch -Regex ($Name.ToUpper()) + { + # Built-in account. + '^(?:NT ?AUTHORITY\\)?(SYSTEM|LOCALSERVICE|LOCAL SERVICE|NETWORKSERVICE|NETWORK SERVICE)$' # CSpell: disable-line + { + $requirePassword = $false + + break + } + + # Virtual account. + '^(?:NT SERVICE\\)(.*)$' + { + $requirePassword = $false + + break + } + + # (Global) Managed Service Account. + '\$$' + { + $requirePassword = $false + + break + } + } + + return $requirePassword +} diff --git a/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 b/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 index cde42c5..901add8 100644 --- a/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 +++ b/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 @@ -24,7 +24,7 @@ BeforeDiscovery { } BeforeAll { - $script:dscModuleName = 'DSCResource.Common' + $script:dscModuleName = 'DscResource.Common' Import-Module -Name $script:dscModuleName @@ -61,17 +61,15 @@ Describe 'Assert-ElevatedUser' -Tag 'Public' { } It 'Should throw the correct error' -Skip:$mockIsElevated { - InModuleScope -ScriptBlock { - $mockErrorMessage = $script:localizedData.ElevatedUser_UserNotElevated - - { Assert-ElevatedUser } | Should -Throw -ExpectedMessage $mockErrorMessage + $mockErrorMessage = InModuleScope -ScriptBlock { + $script:localizedData.ElevatedUser_UserNotElevated } + + { Assert-ElevatedUser } | Should -Throw -ExpectedMessage $mockErrorMessage } It 'Should not throw an exception' -Skip:(-not $mockIsElevated) { - InModuleScope -ScriptBlock { - { Assert-ElevatedUser } | Should -Not -Throw - } + { Assert-ElevatedUser } | Should -Not -Throw } Context 'When on Linux or macOS' { @@ -106,9 +104,7 @@ Describe 'Assert-ElevatedUser' -Tag 'Public' { } It 'Should not throw an exception' { - InModuleScope -ScriptBlock { - { Assert-ElevatedUser } | Should -Not -Throw - } + { Assert-ElevatedUser } | Should -Not -Throw } } } diff --git a/tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 b/tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 new file mode 100644 index 0000000..c1b54d0 --- /dev/null +++ b/tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 @@ -0,0 +1,103 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'DscResource.Common' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Assert-RequiredCommandParameter' -Tag 'Public' { + Context 'When required parameter is missing' { + It 'Should throw the correct error' { + $mockErrorMessage = InModuleScope -ScriptBlock { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f 'Parameter1' + } + + { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' } | + Should -Throw -ExpectedMessage $mockErrorMessage + } + } + + Context 'When the parameter in IfParameterPresent is not present' { + It 'Should not throw an error' { + { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | + Should -Not -Throw + } + } + + Context 'When the parameter in IfParameterPresent is not present' { + It 'Should throw the correct error' { + $mockErrorMessage = InModuleScope -ScriptBlock { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' + } + + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter2 = 'Value2' + } -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' + } | Should -Throw -ExpectedMessage $mockErrorMessage + } + } + + Context 'When the parameters in IfParameterPresent is present and the required parameters are not present' { + It 'Should throw the correct error' { + $mockErrorMessage = InModuleScope -ScriptBlock { + $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f "Parameter3', 'Parameter4", "Parameter1', 'Parameter2" + } + + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter3', 'Parameter4') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Throw -ExpectedMessage $mockErrorMessage + } + } + + Context 'When the parameters in IfParameterPresent is present and required parameters are present' { + It 'Should throw the correct error' { + { + Assert-RequiredCommandParameter -BoundParameter @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter1', 'Parameter2') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Not -Throw + } + } +} diff --git a/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 b/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 new file mode 100644 index 0000000..3c3c151 --- /dev/null +++ b/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 @@ -0,0 +1,75 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'DscResource.Common' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'Test-AccountRequirePassword' -Tag 'Public' { + Context 'When service account is a built-in account' { + It 'Should return $false' { + Test-AccountRequirePassword -Name 'NT Authority\NETWORK SERVICE' | Should -BeFalse + } + } + + Context 'When service account is a virtual account' { + It 'Should return $false' { + Test-AccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' | Should -BeFalse + } + } + + Context 'When service account is a (global) managed service account' { + It 'Should return $false' { + Test-AccountRequirePassword -Name 'DOMAIN\MyMSA$' | Should -BeFalse + } + } + + Context 'When service account is a local user account' { + It 'Should return $true' { + Test-AccountRequirePassword -Name 'MySqlUser' | Should -BeTrue + } + } + + Context 'When service account is a domain user account' { + It 'Should return $true' { + Test-AccountRequirePassword -Name 'DOMAIN\MySqlUser' | Should -BeTrue + } + } +} diff --git a/tests/Unit/Public/Test-IsNumericType.Tests.ps1 b/tests/Unit/Public/Test-IsNumericType.Tests.ps1 index 06dbf38..eefab2b 100644 --- a/tests/Unit/Public/Test-IsNumericType.Tests.ps1 +++ b/tests/Unit/Public/Test-IsNumericType.Tests.ps1 @@ -46,21 +46,17 @@ Describe 'Test-IsNumericType' -Tag 'Public' { Context 'When passing value with named parameter' { Context 'When type is numeric' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = Test-IsNumericType -Object ([System.UInt32] 3) + $result = Test-IsNumericType -Object ([System.UInt32] 3) - $result | Should -BeTrue - } + $result | Should -BeTrue } } Context 'When type is not numeric' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = Test-IsNumericType -Object ([System.String] 'a') + $result = Test-IsNumericType -Object ([System.String] 'a') - $result | Should -BeFalse - } + $result | Should -BeFalse } } } @@ -68,41 +64,33 @@ Describe 'Test-IsNumericType' -Tag 'Public' { Context 'When passing value in pipeline' { Context 'When type is numeric' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = ([System.UInt32] 3) | Test-IsNumericType + $result = ([System.UInt32] 3) | Test-IsNumericType - $result | Should -BeTrue - } + $result | Should -BeTrue } } Context 'When type is not numeric' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = ([System.String] 'a') | Test-IsNumericType + $result = ([System.String] 'a') | Test-IsNumericType - $result | Should -BeFalse - } + $result | Should -BeFalse } } Context 'When type is an array with no numeric values' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = ('a', 'b') | Test-IsNumericType + $result = ('a', 'b') | Test-IsNumericType - $result | Should -BeFalse - } + $result | Should -BeFalse } } Context 'When type is an array with a numeric value' { It 'Should return the correct value' { - InModuleScope -ScriptBlock { - $result = ('a', 1, 'b') | Test-IsNumericType + $result = ('a', 1, 'b') | Test-IsNumericType - $result | Should -BeTrue - } + $result | Should -BeTrue } } } From 862189a89cf233f7b73798f705340aa9c8116a94 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 29 Dec 2022 18:11:19 +0100 Subject: [PATCH 2/7] Fix localization strings --- source/en-US/DscResource.Common.strings.psd1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/en-US/DscResource.Common.strings.psd1 b/source/en-US/DscResource.Common.strings.psd1 index 7928fd5..8acc2d1 100644 --- a/source/en-US/DscResource.Common.strings.psd1 +++ b/source/en-US/DscResource.Common.strings.psd1 @@ -39,4 +39,8 @@ ConvertFrom-StringData @' ## Assert-ElevatedUser ElevatedUser_UserNotElevated = This command must run in an elevated PowerShell session. (DRC0043) + + ## Assert-RequiredCommandParameter + RequiredCommandParameter_SpecificParametersMustAllBeSet = The parameters '{0}' must all be specified. (DRC0044) + RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist = The parameters '{0}' must all be specified if either parameter '{1}' is specified. (DRC0045) '@ From d4f86d7dfb8c16056702427bf692d3b3dacc28cc Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 29 Dec 2022 18:24:25 +0100 Subject: [PATCH 3/7] Fix README --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/README.md b/README.md index a4a291a..6902d3f 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,42 @@ This will assert that the module DhcpServer is available and it will be forcibly imported into the session (even if it was already in the session). If the module is not available an exception will be thrown. +### `Assert-RequiredCommandParameter` + +Assert that required parameters has been specified, and throws an exception if not. + +#### Syntax + + +```plaintext +Assert-RequiredCommandParameter [-BoundParameter] [-RequiredParameter] [[-IfParameterPresent] + ] [] +``` + + +#### Outputs + +None. + +#### Example + + +```powershell +Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') +``` + + +Throws an exception if either of the two parameters are not specified. + + +```powershell +Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') +``` + + +Throws an exception if the parameter 'Property1' is specified and one of the +required parameters are not. + ### `Compare-DscParameterState` Compare current against desired property state for any DSC resource and return @@ -843,6 +879,44 @@ Set-PSModulePath -Path ';' -Machine Sets the machine environment variable `PSModulePath` to the specified path or paths (separated with semi-colons). +### `Test-AccountRequirePassword` + +Returns wether the specified service account require password to be provided. +If the account is a (global) managed service account, virtual account, or a +built-in account then there is no need to provide a password. + +#### Syntax + + +```plaintext +Test-AccountRequirePassword [-Name] [] +``` + + +#### Outputs + +None. + +#### Example + +```powershell +Test-AccountRequirePassword -Name 'DOMAIN\MySqlUser' +``` + +Returns $true as a user account need a password. + +```powershell +Test-AccountRequirePassword -Name 'DOMAIN\MyMSA$' +``` + +Returns $false as a manged service account does not need a password. + +```powershell +Test-AccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' +``` + +Returns $false as a virtual account does not need a password. + ### `Test-DscParameterState` This function is used to compare the values in the current state against From cdf5ef75e9a2a79dd5b0f18a1ff9cba91b2b7a94 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 29 Dec 2022 18:27:36 +0100 Subject: [PATCH 4/7] Remove note in comment-based help --- source/Public/Assert-RequiredCommandParameter.ps1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/Public/Assert-RequiredCommandParameter.ps1 b/source/Public/Assert-RequiredCommandParameter.ps1 index 95c11d9..767f38d 100644 --- a/source/Public/Assert-RequiredCommandParameter.ps1 +++ b/source/Public/Assert-RequiredCommandParameter.ps1 @@ -29,10 +29,6 @@ .OUTPUTS None. - - .NOTES - This command should probably be a parmeter set of the command Assert-BoundParameter - in the module DscResource.Common, instead of being a separate command. #> function Assert-RequiredCommandParameter { From 1e5e80768ab5e061a46e44a66a8e4249411eb13c Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 30 Dec 2022 11:10:42 +0100 Subject: [PATCH 5/7] Update with better code and settings --- .vscode/settings.json | 11 ++- CHANGELOG.md | 18 +++-- README.md | 76 +++++++++---------- .../Assert-RequiredCommandParameter.ps1 | 10 +-- source/Public/Assert-BoundParameter.ps1 | 63 ++++++++++++--- source/Public/Test-AccountRequirePassword.ps1 | 6 +- .../Assert-RequiredCommandParameter.Tests.ps1 | 69 +++++++++-------- .../Public/Assert-BoundParameter.Tests.ps1 | 33 ++++++++ .../Unit/Public/Assert-ElevatedUser.Tests.ps1 | 3 + .../Test-AccountRequirePassword.Tests.ps1 | 3 + .../Unit/Public/Test-IsNumericType.Tests.ps1 | 3 + 11 files changed, 197 insertions(+), 98 deletions(-) rename source/{Public => Private}/Assert-RequiredCommandParameter.ps1 (90%) rename tests/Unit/{Public => Private}/Assert-RequiredCommandParameter.Tests.ps1 (51%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2f0a10e..c524097 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,5 +37,14 @@ "[markdown]": { "files.trimTrailingWhitespace": true, "files.encoding": "utf8" - } + }, + "powershell.pester.useLegacyCodeLens": false, + "pester.testFilePath": [ + "[tT]ests/[uU]nit/*.[tT]ests.[pP][sS]1", + "[tT]ests/[uU]nit/**/*.[tT]ests.[pP][sS]1" + ], + "pester.runTestsInNewProcess": false, + "pester.pesterModulePath": "./output/RequiredModules/Pester", + "powershell.pester.codeLens": true, + "pester.suppressCodeLensNotice": true } diff --git a/CHANGELOG.md b/CHANGELOG.md index 444212c..d2f4d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,15 +9,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added public function `Assert-RequiredCommandParameter` that throws an +- Added private function `Assert-RequiredCommandParameter` that throws an exception if a specified parameter is not assigned a value and optionally - only if a specified criteria is not met - [Issue #92](https://github.com/dsccommunity/DscResource.Common/issues/92) + throws only if a specific parameter is passed. - [Issue #92](https://github.com/dsccommunity/DscResource.Common/issues/92) - Related to SqlServerDsc [Issue #1796](https://github.com/dsccommunity/SqlServerDsc/issues/1796). -- Added public function `Test-AccountRequirePassword` that throws an - exception if a specified parameter is not assigned a value and optionally - only if a specified criteria is not met - [Issue #93](https://github.com/dsccommunity/DscResource.Common/issues/93) +- Added public function `Test-AccountRequirePassword` that returns true or + false whether an account need a password to be passed - [Issue #93](https://github.com/dsccommunity/DscResource.Common/issues/93) - Related to SqlServerDsc [Issue #1794](https://github.com/dsccommunity/SqlServerDsc/issues/1794). +### Changed + +- DscResource.Common + - Updated Visual Studio Code project settings to configure testing for Pester 5. +- `Assert-BoundParameter` + - Now has a new parameter set that calls `Assert-RequiredCommandParameter` + which will throw an exception if a specified parameter is not assigned + a value and optionally throws only if a specific parameter is passed. + ### Fixed - Fixed unit tests for `Assert-ElevatedUser` and `Test-IsNumericType` so diff --git a/README.md b/README.md index 6902d3f..06ff659 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,29 @@ functions. ### `Assert-BoundParameter` +This command asserts passed parameters. It takes a hashtable, normally +`$PSBoundParameters`. There are two parameter sets for this command. + +#### Mutually exclusive parameters are not set + Asserts that a specified set of parameters are not passed together with another set of parameters. -There is no built in logic to validate against parameters sets for DSC -so this can be used instead to validate the parameters that were set in -the configuration. + +>There is no built in logic to validate against parameters sets for DSC +>so this can be used instead to validate the parameters that were set in +>the configuration. + +#### Required parameter is set + +Assert that required parameters has been specified, and throws an exception if not. #### Syntax ```plaintext -Assert-BoundParameter [-BoundParameterList] [-MutuallyExclusiveList1] - [-MutuallyExclusiveList2] [] +Assert-BoundParameter -BoundParameterList -MutuallyExclusiveList1 -MutuallyExclusiveList2 [] + +Assert-BoundParameter -BoundParameterList -RequiredParameter [-IfParameterPresent ] [] ``` @@ -75,6 +86,23 @@ Assert-BoundParameter @assertBoundParameterParameters This example throws an exception if `$PSBoundParameters` contains both the parameters `Parameter1` and `Parameter2`. + +```powershell +Assert-RequiredCommandParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') +``` + + +Throws an exception if either of the two parameters are not specified. + + +```powershell +Assert-RequiredCommandParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') +``` + + +Throws an exception if the parameter 'Property1' is specified and either +of the required parameters are not. + ### `Assert-ElevatedUser` Assert that the user has elevated the PowerShell session. @@ -182,42 +210,6 @@ This will assert that the module DhcpServer is available and it will be forcibly imported into the session (even if it was already in the session). If the module is not available an exception will be thrown. -### `Assert-RequiredCommandParameter` - -Assert that required parameters has been specified, and throws an exception if not. - -#### Syntax - - -```plaintext -Assert-RequiredCommandParameter [-BoundParameter] [-RequiredParameter] [[-IfParameterPresent] - ] [] -``` - - -#### Outputs - -None. - -#### Example - - -```powershell -Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') -``` - - -Throws an exception if either of the two parameters are not specified. - - -```powershell -Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') -``` - - -Throws an exception if the parameter 'Property1' is specified and one of the -required parameters are not. - ### `Compare-DscParameterState` Compare current against desired property state for any DSC resource and return @@ -881,7 +873,7 @@ or paths (separated with semi-colons). ### `Test-AccountRequirePassword` -Returns wether the specified service account require password to be provided. +Returns whether the specified account require a password to be provided. If the account is a (global) managed service account, virtual account, or a built-in account then there is no need to provide a password. diff --git a/source/Public/Assert-RequiredCommandParameter.ps1 b/source/Private/Assert-RequiredCommandParameter.ps1 similarity index 90% rename from source/Public/Assert-RequiredCommandParameter.ps1 rename to source/Private/Assert-RequiredCommandParameter.ps1 index 767f38d..37c157f 100644 --- a/source/Public/Assert-RequiredCommandParameter.ps1 +++ b/source/Private/Assert-RequiredCommandParameter.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION Assert that required parameters has been specified, and throws an exception if not. - .PARAMETER BoundParameter + .PARAMETER BoundParameterList A hashtable containing the parameters to evaluate. Normally this is set to $PSBoundParameters. @@ -25,7 +25,7 @@ .EXAMPLE Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') - Throws an exception if the parameter 'Property1' is specified and one of the required parameters are not. + Throws an exception if the parameter 'Property1' is specified and either of the required parameters are not. .OUTPUTS None. @@ -37,7 +37,7 @@ function Assert-RequiredCommandParameter ( [Parameter(Mandatory = $true)] [System.Collections.Hashtable] - $BoundParameter, + $BoundParameterList, [Parameter(Mandatory = $true)] [System.String[]] @@ -52,7 +52,7 @@ function Assert-RequiredCommandParameter if ($PSBoundParameters.ContainsKey('IfParameterPresent')) { - $hasIfParameterPresent = $BoundParameter.Keys.Where( { $_ -in $IfParameterPresent } ) + $hasIfParameterPresent = $BoundParameterList.Keys.Where( { $_ -in $IfParameterPresent } ) if (-not $hasIfParameterPresent) { @@ -64,7 +64,7 @@ function Assert-RequiredCommandParameter { foreach ($parameter in $RequiredParameter) { - if ($parameter -notin $BoundParameter.Keys) + if ($parameter -notin $BoundParameterList.Keys) { $errorMessage = if ($PSBoundParameters.ContainsKey('IfParameterPresent')) { diff --git a/source/Public/Assert-BoundParameter.ps1 b/source/Public/Assert-BoundParameter.ps1 index cdf7db9..5b7702b 100644 --- a/source/Public/Assert-BoundParameter.ps1 +++ b/source/Public/Assert-BoundParameter.ps1 @@ -20,6 +20,14 @@ An array of parameter names that are not allowed to be bound at the same time as those in MutuallyExclusiveList1. + .PARAMETER RequiredParameter + One or more parameter names that is required to have been specified. + + .PARAMETER IfParameterPresent + One or more parameter names that if specified will trigger the evaluation. + If neither of the parameter names has been specified the evaluation of required + parameters are not made. + .EXAMPLE $assertBoundParameterParameters = @{ BoundParameterList = $PSBoundParameters @@ -35,6 +43,16 @@ This example throws an exception if `$PSBoundParameters` contains both the parameters `Parameter1` and `Parameter2`. + + .EXAMPLE + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') + + Throws an exception if either of the two parameters are not specified. + + .EXAMPLE + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') + + Throws an exception if the parameter 'Property1' is specified and either of the required parameters are not. #> function Assert-BoundParameter { @@ -46,24 +64,47 @@ function Assert-BoundParameter [System.Collections.Hashtable] $BoundParameterList, - [Parameter(Mandatory = $true)] + [Parameter(ParameterSetName = 'MutuallyExclusiveParameters', Mandatory = $true)] [System.String[]] $MutuallyExclusiveList1, - [Parameter(Mandatory = $true)] + [Parameter(ParameterSetName = 'MutuallyExclusiveParameters', Mandatory = $true)] [System.String[]] - $MutuallyExclusiveList2 - ) + $MutuallyExclusiveList2, - $itemFoundFromList1 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList1 }) - $itemFoundFromList2 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList2 }) + [Parameter(ParameterSetName = 'RequiredParameter', Mandatory = $true)] + [System.String[]] + $RequiredParameter, - if ($itemFoundFromList1.Count -gt 0 -and $itemFoundFromList2.Count -gt 0) + [Parameter(ParameterSetName = 'RequiredParameter')] + [System.String[]] + $IfParameterPresent + ) + + switch ($PSCmdlet.ParameterSetName) { - $errorMessage = ` - $script:localizedData.ParameterUsageWrong ` - -f ($MutuallyExclusiveList1 -join "','"), ($MutuallyExclusiveList2 -join "','") + 'MutuallyExclusiveParameters' + { + $itemFoundFromList1 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList1 }) + $itemFoundFromList2 = $BoundParameterList.Keys.Where({ $_ -in $MutuallyExclusiveList2 }) + + if ($itemFoundFromList1.Count -gt 0 -and $itemFoundFromList2.Count -gt 0) + { + $errorMessage = ` + $script:localizedData.ParameterUsageWrong ` + -f ($MutuallyExclusiveList1 -join "','"), ($MutuallyExclusiveList2 -join "','") - New-InvalidArgumentException -ArgumentName 'Parameters' -Message $errorMessage + New-InvalidArgumentException -ArgumentName 'Parameters' -Message $errorMessage + } + + break + } + + 'RequiredParameter' + { + Assert-RequiredCommandParameter @PSBoundParameters + + break + } } } diff --git a/source/Public/Test-AccountRequirePassword.ps1 b/source/Public/Test-AccountRequirePassword.ps1 index ee68538..986c86a 100644 --- a/source/Public/Test-AccountRequirePassword.ps1 +++ b/source/Public/Test-AccountRequirePassword.ps1 @@ -1,14 +1,14 @@ <# .SYNOPSIS - Returns wether the specified service account require password to be provided. + Returns whether the specified account require a password to be provided. .DESCRIPTION - Returns wether the specified service account require password to be provided. + Returns whether the specified account require a password to be provided. If the account is a (global) managed service account, virtual account, or a built-in account then there is no need to provide a password. .PARAMETER Name - Credential name for the service account. + Credential name for the account. .EXAMPLE Test-AccountRequirePassword -Name 'DOMAIN\MyMSA$' diff --git a/tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 similarity index 51% rename from tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 rename to tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 index c1b54d0..f3ae9db 100644 --- a/tests/Unit/Public/Assert-RequiredCommandParameter.Tests.ps1 +++ b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 @@ -26,6 +26,9 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'DscResource.Common' + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + Import-Module -Name $script:dscModuleName $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName @@ -42,62 +45,66 @@ AfterAll { Get-Module -Name $script:dscModuleName -All | Remove-Module -Force } -Describe 'Assert-RequiredCommandParameter' -Tag 'Public' { +Describe 'Assert-RequiredCommandParameter' -Tag 'Private' { Context 'When required parameter is missing' { It 'Should throw the correct error' { - $mockErrorMessage = InModuleScope -ScriptBlock { - $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f 'Parameter1' - } + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f 'Parameter1' - { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' } | - Should -Throw -ExpectedMessage $mockErrorMessage + { Assert-RequiredCommandParameter -BoundParameterList @{} -RequiredParameter 'Parameter1' } | + Should -Throw -ExpectedMessage $mockErrorMessage + } } } Context 'When the parameter in IfParameterPresent is not present' { It 'Should not throw an error' { - { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | - Should -Not -Throw + InModuleScope -ScriptBlock { + { Assert-RequiredCommandParameter -BoundParameterList @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | + Should -Not -Throw + } } } Context 'When the parameter in IfParameterPresent is not present' { It 'Should throw the correct error' { - $mockErrorMessage = InModuleScope -ScriptBlock { - $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' - } + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter2 = 'Value2' - } -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' - } | Should -Throw -ExpectedMessage $mockErrorMessage + { + Assert-RequiredCommandParameter -BoundParameterList @{ + Parameter2 = 'Value2' + } -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' + } | Should -Throw -ExpectedMessage $mockErrorMessage + } } } Context 'When the parameters in IfParameterPresent is present and the required parameters are not present' { It 'Should throw the correct error' { - $mockErrorMessage = InModuleScope -ScriptBlock { - $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f "Parameter3', 'Parameter4", "Parameter1', 'Parameter2" - } + InModuleScope -ScriptBlock { + $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f "Parameter3', 'Parameter4", "Parameter1', 'Parameter2" - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter1 = 'Value1' - Parameter2 = 'Value2' - } -RequiredParameter @('Parameter3', 'Parameter4') -IfParameterPresent @('Parameter1', 'Parameter2') - } | Should -Throw -ExpectedMessage $mockErrorMessage + { + Assert-RequiredCommandParameter -BoundParameterList @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter3', 'Parameter4') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Throw -ExpectedMessage $mockErrorMessage + } } } Context 'When the parameters in IfParameterPresent is present and required parameters are present' { It 'Should throw the correct error' { - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter1 = 'Value1' - Parameter2 = 'Value2' - } -RequiredParameter @('Parameter1', 'Parameter2') -IfParameterPresent @('Parameter1', 'Parameter2') - } | Should -Not -Throw + InModuleScope -ScriptBlock { + { + Assert-RequiredCommandParameter -BoundParameterList @{ + Parameter1 = 'Value1' + Parameter2 = 'Value2' + } -RequiredParameter @('Parameter1', 'Parameter2') -IfParameterPresent @('Parameter1', 'Parameter2') + } | Should -Not -Throw + } } } } diff --git a/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 b/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 index 0bd50d2..43423d9 100644 --- a/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 +++ b/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 @@ -25,6 +25,39 @@ AfterAll { } Describe 'Assert-BoundParameter' -Tag 'AssertBoundParameter' { + It 'Should have the correct parameters in parameter set ' -ForEach @( + @{ + MockParameterSetName = 'MutuallyExclusiveParameters' + # cSpell: disable-next + MockExpectedParameters = '-BoundParameterList -MutuallyExclusiveList1 -MutuallyExclusiveList2 []' + } + @{ + MockParameterSetName = 'RequiredParameter' + # cSpell: disable-next + MockExpectedParameters = '-BoundParameterList -RequiredParameter [-IfParameterPresent ] []' + } + ) { + InModuleScope -Parameters $_ -ScriptBlock { + $result = (Get-Command -Name 'Assert-BoundParameter').ParameterSets | + Where-Object -FilterScript { + $_.Name -eq $mockParameterSetName + } | + Select-Object -Property @( + @{ + Name = 'ParameterSetName' + Expression = { $_.Name } + }, + @{ + Name = 'ParameterListAsString' + Expression = { $_.ToString() } + } + ) + + $result.ParameterSetName | Should -Be $MockParameterSetName + $result.ParameterListAsString | Should -Be $MockExpectedParameters + } + } + Context 'When the assert is successful' { Context 'When there are no bound parameters' { It 'Should not throw an error' { diff --git a/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 b/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 index 901add8..c45484c 100644 --- a/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 +++ b/tests/Unit/Public/Assert-ElevatedUser.Tests.ps1 @@ -26,6 +26,9 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'DscResource.Common' + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + Import-Module -Name $script:dscModuleName $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName diff --git a/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 b/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 index 3c3c151..3e12643 100644 --- a/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 +++ b/tests/Unit/Public/Test-AccountRequirePassword.Tests.ps1 @@ -26,6 +26,9 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'DscResource.Common' + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + Import-Module -Name $script:dscModuleName $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName diff --git a/tests/Unit/Public/Test-IsNumericType.Tests.ps1 b/tests/Unit/Public/Test-IsNumericType.Tests.ps1 index eefab2b..6db38c3 100644 --- a/tests/Unit/Public/Test-IsNumericType.Tests.ps1 +++ b/tests/Unit/Public/Test-IsNumericType.Tests.ps1 @@ -26,6 +26,9 @@ BeforeDiscovery { BeforeAll { $script:dscModuleName = 'DscResource.Common' + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force + Import-Module -Name $script:dscModuleName $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName From 6a3c8d4e5ceb18f802dfc354922708549503aec7 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 30 Dec 2022 11:20:37 +0100 Subject: [PATCH 6/7] Fix unit tests --- .../Assert-RequiredCommandParameter.Tests.ps1 | 15 ++++++++-- .../Public/Assert-BoundParameter.Tests.ps1 | 30 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 index f3ae9db..b410eef 100644 --- a/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 +++ b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 @@ -57,7 +57,18 @@ Describe 'Assert-RequiredCommandParameter' -Tag 'Private' { } } - Context 'When the parameter in IfParameterPresent is not present' { + Context 'When the required parameter is present' { + It 'Should not throw an error' { + InModuleScope -ScriptBlock { + { Assert-RequiredCommandParameter -BoundParameterList @{ + Parameter1 = 'Value1' + } -RequiredParameter 'Parameter1' } | + Should -Not -Throw + } + } + } + + Context 'When both required parameter and parameter in IfParameterPresent is not present' { It 'Should not throw an error' { InModuleScope -ScriptBlock { { Assert-RequiredCommandParameter -BoundParameterList @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | @@ -66,7 +77,7 @@ Describe 'Assert-RequiredCommandParameter' -Tag 'Private' { } } - Context 'When the parameter in IfParameterPresent is not present' { + Context 'When the required parameter is not present and parameter in IfParameterPresent is present' { It 'Should throw the correct error' { InModuleScope -ScriptBlock { $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' diff --git a/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 b/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 index 43423d9..835c66b 100644 --- a/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 +++ b/tests/Unit/Public/Assert-BoundParameter.Tests.ps1 @@ -121,6 +121,21 @@ Describe 'Assert-BoundParameter' -Tag 'AssertBoundParameter' { } | Should -Not -Throw } } + + Context 'When the required parameter is present' { + BeforeAll { + Mock -CommandName Assert-RequiredCommandParameter + } + + It 'Should not throw an error' { + InModuleScope -ScriptBlock { + { Assert-BoundParameter -BoundParameterList @{ + Parameter1 = 'Value1' + } -RequiredParameter 'Parameter1' } | + Should -Not -Throw + } + } + } } Context 'When the assert fails' { @@ -171,5 +186,20 @@ Describe 'Assert-BoundParameter' -Tag 'AssertBoundParameter' { } | Should -Throw -ExpectedMessage "$errorMessage*" } } + + Context 'When the required parameter is not present' { + BeforeAll { + Mock -CommandName Assert-RequiredCommandParameter -MockWith { + throw 'Mocked error' + } + } + + It 'Should not throw an error' { + InModuleScope -ScriptBlock { + { Assert-BoundParameter -BoundParameterList @{} -RequiredParameter 'Parameter1' } | + Should -Throw -ExpectedMessage '*Mocked error*' + } + } + } } } From 36f5388c6e764042b16afd10b7ebc3a8b9000c38 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 30 Dec 2022 11:23:13 +0100 Subject: [PATCH 7/7] Fix Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f4d18..4e42c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added private function `Assert-RequiredCommandParameter` that throws an - exception if a specified parameter is not assigned a value and optionally + exception if a specified parameter is not assigned a value, and optionally throws only if a specific parameter is passed. - [Issue #92](https://github.com/dsccommunity/DscResource.Common/issues/92) - Related to SqlServerDsc [Issue #1796](https://github.com/dsccommunity/SqlServerDsc/issues/1796). - Added public function `Test-AccountRequirePassword` that returns true or @@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Assert-BoundParameter` - Now has a new parameter set that calls `Assert-RequiredCommandParameter` which will throw an exception if a specified parameter is not assigned - a value and optionally throws only if a specific parameter is passed. + a value, and optionally throws only if a specific parameter is passed. ### Fixed