From c5ef7be7b999a89a78feb3b4f3213fd5675cd6b1 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 31 Dec 2022 10:34:45 +0100 Subject: [PATCH] Add public commands for class-based resource (#96) - Added public command `Get-DscProperty` that returns a hashtable of available properties for a class-based resource. See comment-based help for more information. - Added public command `Test-DscProperty` that returns a true or false whether a property exist in a class-based resource. See comment-based help for more information. - Added private function `Test-DscPropertyIsAssigned` that returns a true or false whether a property in a class-based resource has a non-null value. --- CHANGELOG.md | 8 + README.md | 115 ++- source/Private/Test-DscPropertyIsAssigned.ps1 | 55 ++ source/Public/Get-DscProperty.ps1 | 170 ++++ source/Public/Test-DscProperty.ps1 | 101 +++ .../Test-DscPropertyIsAssigned.Tests.ps1 | 122 +++ tests/Unit/Public/Get-DscProperty.Tests.ps1 | 727 ++++++++++++++++++ tests/Unit/Public/Test-DscProperty.Tests.ps1 | 272 +++++++ 8 files changed, 1569 insertions(+), 1 deletion(-) create mode 100644 source/Private/Test-DscPropertyIsAssigned.ps1 create mode 100644 source/Public/Get-DscProperty.ps1 create mode 100644 source/Public/Test-DscProperty.ps1 create mode 100644 tests/Unit/Private/Test-DscPropertyIsAssigned.Tests.ps1 create mode 100644 tests/Unit/Public/Get-DscProperty.Tests.ps1 create mode 100644 tests/Unit/Public/Test-DscProperty.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index e965e35..f3662e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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). +- Added public command `Get-DscProperty` that returns a hashtable of available + properties for a class-based resource. See comment-based help for more + information. +- Added public command `Test-DscProperty` that returns a true or false + whether a property exist in a class-based resource. See comment-based help + for more information. +- Added private function `Test-DscPropertyIsAssigned` that returns a true + or false whether a property in a class-based resource has a non-null value. ### Changed diff --git a/README.md b/README.md index 06ff659..4298fcf 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,62 @@ None. $computerName = Get-ComputerName ``` +### `Get-DscProperty` + +Returns DSC resource properties that is part of a class-based DSC resource. +The properties can be filtered using name, attribute, or if it has been +assigned a non-null value. + +#### Syntax + + +```plaintext +Get-DscProperty [-InputObject] [[-Name] ] [[-ExcludeName] ] [[-Attribute] ] [-HasValue] [] +``` + + +#### Outputs + +**System.Collections.Hashtable** + +#### Notes + +This command only works with nullable data types, if using a non-nullable +type make sure to make it nullable, e.g. `[Nullable[System.Int32]]`. + +#### Example + +```powershell +Get-DscProperty -InputObject $this +``` + +Returns all DSC resource properties of the DSC resource. + +```powershell +$this | Get-DscProperty +``` + +Returns all DSC resource properties of the DSC resource. + +```powershell +Get-DscProperty -InputObject $this -Name @('MyProperty1', 'MyProperty2') +``` + +Returns the DSC resource properties with the specified names. + +```powershell +Get-DscProperty -InputObject $this -Attribute @('Mandatory', 'Optional') +``` + +Returns the DSC resource properties that has the specified attributes. + +```powershell +Get-DscProperty -InputObject $this -Attribute @('Optional') -HasValue +``` + +Returns the DSC resource properties that has the specified attributes and +has a non-null value assigned. + ### `Get-LocalizedData` Gets language-specific data into scripts and functions based on the UI culture @@ -887,7 +943,7 @@ Test-AccountRequirePassword [-Name] [] #### Outputs -None. +**System.Boolean** #### Example @@ -989,6 +1045,63 @@ This compares the values in the current state against the desires state. The function `Get-TargetResource` is called using just the required parameters to get the values in the current state. +### `Test-DscProperty` + +Tests whether the class-based resource has the specified property, and +can optionally tests if the property has a certain attribute or whether +it is assigned a non-null value. + +#### Syntax + + +```plaintext + Test-DscProperty [-InputObject] [-Name] [[-Attribute] {Key | Mandatory | NotConfigurable | Optional}] [-HasValue] [] +``` + + +#### Outputs + +**System.Boolean** + +#### Notes + +This command only works with nullable data types, if using a non-nullable +type make sure to make it nullable, e.g. `[Nullable[System.Int32]]`. + +#### Example + +```powershell +Test-DscProperty -InputObject $this -Name 'MyDscProperty' +``` + +Returns `$true` or `$false` whether the property exist or not. + +```powershell +$this | Test-DscProperty -Name 'MyDscProperty' +``` + +Returns `$true` or `$false` whether the property exist or not. + +```powershell +Test-DscProperty -InputObject $this -Name 'MyDscProperty' -HasValue +``` + +Returns `$true` if the property exist and is assigned a non-null value, if not +`$false` is returned. + +```powershell +Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' +``` + +Returns `$true` if the property exist and is an optional property. + +```powershell +Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' -HasValue +``` + +Returns `$true` if the property exist, is an optional property, and is +assigned a non-null value. + ### `Test-IsNanoServer` This function tests if the current OS is a Nano server. diff --git a/source/Private/Test-DscPropertyIsAssigned.ps1 b/source/Private/Test-DscPropertyIsAssigned.ps1 new file mode 100644 index 0000000..1b55fa9 --- /dev/null +++ b/source/Private/Test-DscPropertyIsAssigned.ps1 @@ -0,0 +1,55 @@ +<# + .SYNOPSIS + Tests whether the class-based resource property is assigned a non-null value. + + .DESCRIPTION + Tests whether the class-based resource property is assigned a non-null value. + + .PARAMETER InputObject + Specifies the object that contain the property. + + .PARAMETER Name + Specifies the name of the property. + + .EXAMPLE + Test-DscPropertyIsAssigned -InputObject $this -Name 'MyDscProperty' + + Returns $true or $false whether the property is assigned or not. + + .OUTPUTS + [System.Boolean] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [nullable[System.Int32]]. +#> +function Test-DscPropertyIsAssigned +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + begin + { + $isAssigned = $false + } + + process + { + $isAssigned = -not ($null -eq $InputObject.$Name) + } + + end + { + return $isAssigned + } +} diff --git a/source/Public/Get-DscProperty.ps1 b/source/Public/Get-DscProperty.ps1 new file mode 100644 index 0000000..34c88c1 --- /dev/null +++ b/source/Public/Get-DscProperty.ps1 @@ -0,0 +1,170 @@ + +<# + .SYNOPSIS + Returns DSC resource properties that is part of a class-based DSC resource. + + .DESCRIPTION + Returns DSC resource properties that is part of a class-based DSC resource. + The properties can be filtered using name, attribute, or if it has been + assigned a value. + + .PARAMETER InputObject + The object that contain one or more key properties. + + .PARAMETER Name + Specifies one or more property names to return. If left out all properties + are returned. + + .PARAMETER ExcludeName + Specifies one or more property names to exclude. + + .PARAMETER Attribute + Specifies one or more property attributes to return. If left out all property + types are returned. + + .PARAMETER HasValue + Specifies to return only properties that has been assigned a non-null value. + If left out all properties are returned regardless if there is a value + assigned or not. + + .EXAMPLE + Get-DscProperty -InputObject $this + + Returns all DSC resource properties of the DSC resource. + + .EXAMPLE + $this | Get-DscProperty + + Returns all DSC resource properties of the DSC resource. + + .EXAMPLE + Get-DscProperty -InputObject $this -Name @('MyProperty1', 'MyProperty2') + + Returns the DSC resource properties with the specified names. + + .EXAMPLE + Get-DscProperty -InputObject $this -Attribute @('Mandatory', 'Optional') + + Returns the DSC resource properties that has the specified attributes. + + .EXAMPLE + Get-DscProperty -InputObject $this -Attribute @('Optional') -HasValue + + Returns the DSC resource properties that has the specified attributes and + has a non-null value assigned. + + .OUTPUTS + [System.Collections.Hashtable] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [Nullable[System.Int32]]. +#> +function Get-DscProperty +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter()] + [System.String[]] + $Name, + + [Parameter()] + [System.String[]] + $ExcludeName, + + [Parameter()] + [ValidateSet('Key', 'Mandatory', 'NotConfigurable', 'Optional')] + [Alias('Type')] + [System.String[]] + $Attribute, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $HasValue + ) + + process + { + $property = $InputObject.PSObject.Properties.Name | + Where-Object -FilterScript { + <# + Return all properties if $Name is not assigned, or if assigned + just those properties. + #> + (-not $Name -or $_ -in $Name) -and + + <# + Return all properties if $ExcludeName is not assigned. Skip + property if it is included in $ExcludeName. + #> + (-not $ExcludeName -or ($_ -notin $ExcludeName)) -and + + # Only return the property if it is a DSC property. + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + $_.AttributeType.Name -eq 'DscPropertyAttribute' + } + ) + } + + if (-not [System.String]::IsNullOrEmpty($property)) + { + if ($PSBoundParameters.ContainsKey('Attribute')) + { + $propertiesOfAttribute = @() + + $propertiesOfAttribute += $property | Where-Object -FilterScript { + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + <# + To simplify the code, ignoring that this will compare + MemberNAme against type 'Optional' which does not exist. + #> + $_.NamedArguments.MemberName -in $Attribute + } + ).NamedArguments.TypedValue.Value -eq $true + } + + # Include all optional parameter if it was requested. + if ($Attribute -contains 'Optional') + { + $propertiesOfAttribute += $property | Where-Object -FilterScript { + $InputObject.GetType().GetMember($_).CustomAttributes.Where( + { + $_.NamedArguments.MemberName -notin @('Key', 'Mandatory', 'NotConfigurable') + } + ) + } + } + + $property = $propertiesOfAttribute + } + } + + # Return a hashtable containing each key property and its value. + $getPropertyResult = @{} + + foreach ($currentProperty in $property) + { + if ($HasValue.IsPresent) + { + $isAssigned = Test-DscPropertyIsAssigned -Name $currentProperty -InputObject $InputObject + + if (-not $isAssigned) + { + continue + } + } + + $getPropertyResult.$currentProperty = $InputObject.$currentProperty + } + + return $getPropertyResult + } +} diff --git a/source/Public/Test-DscProperty.ps1 b/source/Public/Test-DscProperty.ps1 new file mode 100644 index 0000000..08432ec --- /dev/null +++ b/source/Public/Test-DscProperty.ps1 @@ -0,0 +1,101 @@ +<# + .SYNOPSIS + Tests whether the class-based resource has the specified property. + + .DESCRIPTION + Tests whether the class-based resource has the specified property. + + .PARAMETER InputObject + Specifies the object that should be tested for existens of the specified + property. + + .PARAMETER Name + Specifies the name of the property. + + .PARAMETER HasValue + Specifies if the property should be evaluated to have a non-value. If + the property exist but is assigned `$null` the command returns `$false`. + + .PARAMETER Attribute + Specifies if the property should be evaluated to have a specific attribute. + If the property exist but is not the specific attribute the command returns + `$false`. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' + + Returns $true or $false whether the property exist or not. + + .EXAMPLE + $this | Test-DscProperty -Name 'MyDscProperty' + + Returns $true or $false whether the property exist or not. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -HasValue + + Returns $true if the property exist and is assigned a non-null value, if not + $false is returned. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' + + Returns `$true` if the property exist and is an optional property. + + .EXAMPLE + Test-DscProperty -InputObject $this -Name 'MyDscProperty' -Attribute 'Optional' -HasValue + + Returns `$true` if the property exist, is an optional property, and is + assigned a non-null value. + + .OUTPUTS + [System.Boolean] + + .NOTES + This command only works with nullable data types, if using a non-nullable + type make sure to make it nullable, e.g. [Nullable[System.Int32]]. +#> +function Test-DscProperty +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSObject] + $InputObject, + + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $HasValue, + + [Parameter()] + [ValidateSet('Key', 'Mandatory', 'NotConfigurable', 'Optional')] + [System.String[]] + $Attribute + ) + + begin + { + $hasProperty = $false + } + + process + { + $isDscProperty = (Get-DscProperty @PSBoundParameters).ContainsKey($Name) + + if ($isDscProperty) + { + $hasProperty = $true + } + } + + end + { + return $hasProperty + } +} diff --git a/tests/Unit/Private/Test-DscPropertyIsAssigned.Tests.ps1 b/tests/Unit/Private/Test-DscPropertyIsAssigned.Tests.ps1 new file mode 100644 index 0000000..baa7484 --- /dev/null +++ b/tests/Unit/Private/Test-DscPropertyIsAssigned.Tests.ps1 @@ -0,0 +1,122 @@ +[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-DscPropertyIsAssigned' -Tag 'Private' { + Context 'When DSC property has a non-null value' { + BeforeAll { + $inModuleScopeScriptBlock = @' +class MyMockResource +{ + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty +} + +$script:mockResourceBaseInstance = [MyMockResource] @{ + MyProperty3 = 'AnyValue' +} +'@ + + InModuleScope -ScriptBlock ([Scriptblock]::Create($inModuleScopeScriptBlock)) + } + + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = Test-DscPropertyIsAssigned -Name 'MyProperty3' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeTrue + } + } + } + + + Context 'When DSC property has a non-null value' { + BeforeAll { + $inModuleScopeScriptBlock = @' +class MyMockResource +{ +[DscProperty(Key)] +[System.String] +$MyResourceKeyProperty1 + +[DscProperty(Key)] +[System.String] +$MyResourceKeyProperty2 + +[DscProperty()] +[System.String] +$MyProperty3 + +[DscProperty(NotConfigurable)] +[System.String] +$MyResourceReadProperty +} + +$script:mockResourceBaseInstance = [MyMockResource] @{} +'@ + + InModuleScope -ScriptBlock ([Scriptblock]::Create($inModuleScopeScriptBlock)) + } + + It 'Should return the correct value' { + InModuleScope -ScriptBlock { + $result = Test-DscPropertyIsAssigned -Name 'MyProperty3' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeFalse + } + } + } +} diff --git a/tests/Unit/Public/Get-DscProperty.Tests.ps1 b/tests/Unit/Public/Get-DscProperty.Tests.ps1 new file mode 100644 index 0000000..579f348 --- /dev/null +++ b/tests/Unit/Public/Get-DscProperty.Tests.ps1 @@ -0,0 +1,727 @@ +[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:moduleName = 'DscResource.Common' + + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:moduleName -All | Remove-Module -Force + + Import-Module -Name $script:moduleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +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:moduleName -All | Remove-Module -Force +} + +Describe 'Get-DscProperty' -Tag 'Public' { + Context 'When no property is returned' { + BeforeAll { + class MyMockResource + { + [System.String] + $MyResourceKeyProperty1 + } + + $mockResourceBaseInstance = [MyMockResource]::new() + $mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -InputObject $mockResourceBaseInstance + + $result | Should -BeNullOrEmpty + } + } + + Context 'When getting all DSC properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + + [System.String] $ClassProperty + + hidden [System.String] $HiddenClassProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -HaveCount 5 + $result.Keys | Should -Contain 'MyResourceKeyProperty1' + $result.Keys | Should -Contain 'MyResourceKeyProperty2' + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' + $result.Keys | Should -Contain 'MyResourceProperty' + $result.Keys | Should -Contain 'MyResourceReadProperty' + + $result.Keys | Should -Not -Contain 'ClassProperty' -Because 'the property is not a DSC property' + $result.Keys | Should -Not -Contain 'HiddenClassProperty' -Because 'the property is not a DSC property' + + $result.MyResourceKeyProperty1 | Should -Be 'MockValue1' + $result.MyResourceKeyProperty2 | Should -Be 'MockValue2' + $result.MyResourceMandatoryProperty | Should -Be 'MockValue3' + $result.MyResourceProperty | Should -Be 'MockValue4' + $result.MyResourceReadProperty | Should -BeNullOrEmpty + } + } + + Context 'When using parameter Name' { + Context 'When getting a single property' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + + [System.String] $ClassProperty + + hidden [System.String] $HiddenClassProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Name 'MyResourceProperty' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -HaveCount 1 + $result.Keys | Should -Contain 'MyResourceProperty' + + $result.MyResourceProperty | Should -Be 'MockValue4' + } + } + + Context 'When getting multiple properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + + [System.String] $ClassProperty + + hidden [System.String] $HiddenClassProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Name @('MyResourceProperty', 'MyResourceMandatoryProperty') -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -HaveCount 2 + $result.Keys | Should -Contain 'MyResourceProperty' + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' + + $result.MyResourceProperty | Should -Be 'MockValue4' + $result.MyResourceMandatoryProperty | Should -Be 'MockValue3' + } + } + } + + Context 'When using parameter alias Type' { + Context 'When getting all key properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Type 'Key' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceProperty' -Because 'optional properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceMandatoryProperty' -Because 'mandatory properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceKeyProperty1' -Because 'the property is a key property' + $result.Keys | Should -Contain 'MyResourceKeyProperty2' -Because 'the property is a key property' + + $result.MyResourceKeyProperty1 | Should -Be 'MockValue1' + $result.MyResourceKeyProperty2 | Should -Be 'MockValue2' + } + } + } + + Context 'When using parameter Attribute' { + Context 'When getting all key properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute 'Key' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceProperty' -Because 'optional properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceMandatoryProperty' -Because 'mandatory properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceKeyProperty1' -Because 'the property is a key property' + $result.Keys | Should -Contain 'MyResourceKeyProperty2' -Because 'the property is a key property' + + $result.MyResourceKeyProperty1 | Should -Be 'MockValue1' + $result.MyResourceKeyProperty2 | Should -Be 'MockValue2' + } + } + + Context 'When getting all mandatory properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute 'Mandatory' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty2' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceProperty' -Because 'optional properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' -Because 'the property is a mandatory property' + + $result.MyResourceMandatoryProperty | Should -Be 'MockValue3' + } + } + + Context 'When getting all optional properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute 'Optional' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceMandatoryProperty' -Because 'mandatory properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty2' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceProperty' -Because 'the property is a optional property' + + $result.MyResourceProperty | Should -Be 'MockValue4' + } + } + + Context 'When getting all read properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute 'NotConfigurable' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceProperty' -Because 'optional properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceMandatoryProperty' -Because 'mandatory properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty2' -Because 'key properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceReadProperty' -Because 'the property is a read property' + + $result.MyResourceReadProperty | Should -BeNullOrEmpty + } + } + + Context 'When getting all optional and mandatory properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute @('Mandatory', 'Optional') -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty2' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' -Because 'the property is a mandatory property' + $result.Keys | Should -Contain 'MyResourceProperty' -Because 'the property is a optional property' + + $result.MyResourceMandatoryProperty | Should -Be 'MockValue3' + $result.MyResourceProperty | Should -Be 'MockValue4' + } + } + + Context 'When getting specific property names with a certain attribute' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + + [System.String] $ClassProperty + + hidden [System.String] $HiddenClassProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty = 'MockValue4' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Name @('MyResourceProperty', 'MyResourceMandatoryProperty') -Attribute 'Mandatory' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -HaveCount 1 + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' + + $result.Keys | Should -Not -Contain 'MyResourceProperty' -Because 'it is not a mandatory property' + + $result.MyResourceMandatoryProperty | Should -Be 'MockValue3' + } + } + } + + Context 'When using parameter HasValue' { + Context 'When getting all optional properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty1 + + [DscProperty()] + [System.String] + $MyResourceProperty2 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty2 = 'MockValue5' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Attribute 'Optional' -HasValue -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceMandatoryProperty' -Because 'mandatory properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty2' -Because 'key properties should not be part of the collection' + $result.Keys | Should -Not -Contain 'MyResourceReadProperty' -Because 'read properties should not be part of the collection' + + $result.Keys | Should -Not -Contain 'MyResourceProperty1' -Because 'the property has a $null value' + + $result.Keys | Should -Contain 'MyResourceProperty2' -Because 'the property has a non-null value' + + $result.MyResourceProperty2 | Should -Be 'MockValue5' + } + } + } + + Context 'When getting specific named properties' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty1 + + [DscProperty()] + [System.String] + $MyResourceProperty2 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty2 = 'MockValue5' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -Name @('MyResourceProperty1', 'MyResourceProperty2') -HasValue -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceProperty1' -Because 'the property has a $null value' + + $result.Keys | Should -Contain 'MyResourceProperty2' -Because 'the property has a non-null value' + + $result.MyResourceProperty2 | Should -Be 'MockValue5' + } + } + + Context 'When excluding specific property names' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty(Mandatory)] + [System.String] + $MyResourceMandatoryProperty + + [DscProperty()] + [System.String] + $MyResourceProperty1 + + [DscProperty()] + [System.String] + $MyResourceProperty2 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + $script:mockResourceBaseInstance.MyResourceKeyProperty1 = 'MockValue1' + $script:mockResourceBaseInstance.MyResourceKeyProperty2 = 'MockValue2' + $script:mockResourceBaseInstance.MyResourceMandatoryProperty = 'MockValue3' + $script:mockResourceBaseInstance.MyResourceProperty1 = 'MockValue5' + $script:mockResourceBaseInstance.MyResourceProperty2 = 'MockValue6' + } + + It 'Should return the correct value' { + $result = Get-DscProperty -ExcludeName @('MyResourceKeyProperty1', 'MyResourceProperty1') -HasValue -InputObject $script:mockResourceBaseInstance + + $result | Should -BeOfType [System.Collections.Hashtable] + + $result.Keys | Should -Not -Contain 'MyResourceKeyProperty1' -Because 'the property was excluded' + $result.Keys | Should -Not -Contain 'MyResourceProperty1' -Because 'the property was excluded' + + $result.Keys | Should -Contain 'MyResourceKeyProperty2' -Because 'the property has a non-null value and was not excluded' + $result.Keys | Should -Contain 'MyResourceProperty2' -Because 'the property has a non-null value and was not excluded' + $result.Keys | Should -Contain 'MyResourceMandatoryProperty' -Because 'the property has a non-null value and was not excluded' + } + } +} diff --git a/tests/Unit/Public/Test-DscProperty.Tests.ps1 b/tests/Unit/Public/Test-DscProperty.Tests.ps1 new file mode 100644 index 0000000..f7fa38b --- /dev/null +++ b/tests/Unit/Public/Test-DscProperty.Tests.ps1 @@ -0,0 +1,272 @@ +[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:moduleName = 'DscResource.Common' + + # Make sure there are not other modules imported that will conflict with mocks. + Get-Module -Name $script:moduleName -All | Remove-Module -Force + + Import-Module -Name $script:moduleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +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:moduleName -All | Remove-Module -Force +} + +Describe 'Test-DscProperty' -Tag 'Public' { + Context 'When resource does not have an Ensure property' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyResourceProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'Ensure' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeFalse + } + } + + Context 'When resource have an Ensure property' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $Ensure + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'Ensure' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeTrue + } + } + + Context 'When resource have an Ensure property, but it is not a DSC property' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [System.String] + $Ensure + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource]::new() + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'Ensure' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeFalse + } + } + + Context 'When using parameter HasValue' { + Context 'When DSC property has a non-null value' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource] @{ + MyProperty3 = 'AnyValue' + } + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'MyProperty3' -HasValue -InputObject $script:mockResourceBaseInstance + + $result | Should -BeTrue + } + } + + Context 'When DSC property has a null value' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource] @{} + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'MyProperty3' -HasValue -InputObject $script:mockResourceBaseInstance + + $result | Should -BeFalse + } + } + } + + Context 'When using parameter Attribute' { + Context 'When DSC property has the expected attribute' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource] @{ + MyProperty3 = 'AnyValue' + } + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'MyProperty3' -Attribute 'Optional' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeTrue + } + } + + Context 'When DSC property has the wrong attribute' { + BeforeAll { + class MyMockResource + { + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty1 + + [DscProperty(Key)] + [System.String] + $MyResourceKeyProperty2 + + [DscProperty()] + [System.String] + $MyProperty3 + + [DscProperty(NotConfigurable)] + [System.String] + $MyResourceReadProperty + } + + $script:mockResourceBaseInstance = [MyMockResource] @{} + } + + It 'Should return the correct value' { + $result = Test-DscProperty -Name 'MyProperty3' -Attribute 'Mandatory' -InputObject $script:mockResourceBaseInstance + + $result | Should -BeFalse + } + } + } +}