From 720a543a1f8500c95baa6feb1008c9ff0e5c82bb Mon Sep 17 00:00:00 2001 From: Svilen Date: Sat, 11 May 2019 08:50:30 +0200 Subject: [PATCH] xActiveDirectory: Combine DscResource.LocalizationHelper and DscResource.Common (#270) - Changes to xActiveDirectory - Combined the DscResource.LocalizationHelper and DscResource.Common modules into xActiveDirectory.Common (issue #268). --- CHANGELOG.md | 1 + .../MSFT_xADCommon/MSFT_xADCommon.psm1 | 4 +- .../MSFT_xADDomainController.psm1 | 4 +- .../MSFT_xADKDSKey/MSFT_xADKDSKey.psm1 | 4 +- .../MSFT_xADManagedServiceAccount.psm1 | 4 +- .../MSFT_xADReplicationSiteLink.psm1 | 4 +- .../DscResource.Common.psm1 | 190 -------- .../DscResource.LocalizationHelper.psm1 | 266 ----------- .../xActiveDirectory.Common.strings.psd1} | 2 +- .../xActiveDirectory.Common.psm1 | 430 ++++++++++++++++++ .../DscResource.LocalizationHelper.Tests.ps1 | 186 -------- ....ps1 => xActiveDirectory.Common.Tests.ps1} | 238 ++++++++-- 12 files changed, 650 insertions(+), 683 deletions(-) delete mode 100644 Modules/DscResource.Common/DscResource.Common.psm1 delete mode 100644 Modules/DscResource.LocalizationHelper/DscResource.LocalizationHelper.psm1 rename Modules/{DscResource.Common/en-US/DscResource.Common.strings.psd1 => xActiveDirectory.Common/en-US/xActiveDirectory.Common.strings.psd1} (93%) create mode 100644 Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 delete mode 100644 Tests/Unit/DscResource.LocalizationHelper.Tests.ps1 rename Tests/Unit/{DscResource.Common.Tests.ps1 => xActiveDirectory.Common.Tests.ps1} (55%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 262906d11..97990484d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and [@kungfu71186](https://github.com/kungfu71186) - Removing the Misc Folder, as it is no longer required. - Added xADKDSKey resource to create KDS Root Keys for gMSAs. [@kungfu71186](https://github.com/kungfu71186) + - Combined DscResource.LocalizationHelper and DscResource.Common Modules into xActiveDirectory.Common - Changes to xADReplicationSiteLink - Make use of the new localization helper functions. - Changes to xAdDomainController diff --git a/DSCResources/MSFT_xADCommon/MSFT_xADCommon.psm1 b/DSCResources/MSFT_xADCommon/MSFT_xADCommon.psm1 index 5030413f9..c57d31e00 100644 --- a/DSCResources/MSFT_xADCommon/MSFT_xADCommon.psm1 +++ b/DSCResources/MSFT_xADCommon/MSFT_xADCommon.psm1 @@ -1,8 +1,8 @@ $script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent $script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' -$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.LocalizationHelper' -Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'DscResource.LocalizationHelper.psm1') +$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common' +Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'xActiveDirectory.Common.psm1') data localizedString { diff --git a/DSCResources/MSFT_xADDomainController/MSFT_xADDomainController.psm1 b/DSCResources/MSFT_xADDomainController/MSFT_xADDomainController.psm1 index 94b998c06..b3bef7cd4 100644 --- a/DSCResources/MSFT_xADDomainController/MSFT_xADDomainController.psm1 +++ b/DSCResources/MSFT_xADDomainController/MSFT_xADDomainController.psm1 @@ -1,8 +1,8 @@ $script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent $script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' -$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.LocalizationHelper' -Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'DscResource.LocalizationHelper.psm1') +$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common' +Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'xActiveDirectory.Common.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADDomainController' diff --git a/DSCResources/MSFT_xADKDSKey/MSFT_xADKDSKey.psm1 b/DSCResources/MSFT_xADKDSKey/MSFT_xADKDSKey.psm1 index bb4f276d6..0fea187a8 100644 --- a/DSCResources/MSFT_xADKDSKey/MSFT_xADKDSKey.psm1 +++ b/DSCResources/MSFT_xADKDSKey/MSFT_xADKDSKey.psm1 @@ -5,12 +5,12 @@ $script:resourceModulePath = Split-Path ` $script:localizationModulePath = Join-Path ` -Path $script:resourceModulePath ` --ChildPath 'Modules\DscResource.LocalizationHelper' +-ChildPath 'Modules\xActiveDirectory.Common' Import-Module -Name ( Join-Path ` -Path $script:localizationModulePath ` - -ChildPath 'DscResource.LocalizationHelper.psm1' + -ChildPath 'xActiveDirectory.Common.psm1' ) $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADKDSKey' diff --git a/DSCResources/MSFT_xADManagedServiceAccount/MSFT_xADManagedServiceAccount.psm1 b/DSCResources/MSFT_xADManagedServiceAccount/MSFT_xADManagedServiceAccount.psm1 index f70facfc6..f44342510 100644 --- a/DSCResources/MSFT_xADManagedServiceAccount/MSFT_xADManagedServiceAccount.psm1 +++ b/DSCResources/MSFT_xADManagedServiceAccount/MSFT_xADManagedServiceAccount.psm1 @@ -4,12 +4,12 @@ $script:resourceModulePath = Split-Path ` $script:localizationModulePath = Join-Path ` -Path $script:resourceModulePath ` --ChildPath 'Modules\DscResource.LocalizationHelper' +-ChildPath 'Modules\xActiveDirectory.Common' Import-Module -Name ( Join-Path ` -Path $script:localizationModulePath ` - -ChildPath 'DscResource.LocalizationHelper.psm1' + -ChildPath 'xActiveDirectory.Common.psm1' ) $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADManagedServiceAccount' diff --git a/DSCResources/MSFT_xADReplicationSiteLink/MSFT_xADReplicationSiteLink.psm1 b/DSCResources/MSFT_xADReplicationSiteLink/MSFT_xADReplicationSiteLink.psm1 index 9b055b1a7..1427eb291 100644 --- a/DSCResources/MSFT_xADReplicationSiteLink/MSFT_xADReplicationSiteLink.psm1 +++ b/DSCResources/MSFT_xADReplicationSiteLink/MSFT_xADReplicationSiteLink.psm1 @@ -1,8 +1,8 @@ $script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent $script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' -$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.LocalizationHelper' -Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'DscResource.LocalizationHelper.psm1') +$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common' +Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'xActiveDirectory.Common.psm1') $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADReplicationSiteLink' diff --git a/Modules/DscResource.Common/DscResource.Common.psm1 b/Modules/DscResource.Common/DscResource.Common.psm1 deleted file mode 100644 index 24dd4ea99..000000000 --- a/Modules/DscResource.Common/DscResource.Common.psm1 +++ /dev/null @@ -1,190 +0,0 @@ -$script:modulesFolderPath = Split-Path -Path $PSScriptRoot -Parent - -$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.LocalizationHelper' -Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'DscResource.LocalizationHelper.psm1') - - -$script:localizedData = Get-LocalizedData -ResourceName 'DscResource.Common' -ScriptRoot $PSScriptRoot - -<# - .SYNOPSIS - This method is used to compare current and desired values for any DSC resource. - - .PARAMETER CurrentValues - This is hash table of the current values that are applied to the resource. - - .PARAMETER DesiredValues - This is a PSBoundParametersDictionary of the desired values for the resource. - - .PARAMETER ValuesToCheck - This is a list of which properties in the desired values list should be checked. - If this is empty then all values in DesiredValues are checked. -#> -function Test-DscParameterState -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $CurrentValues, - - [Parameter(Mandatory = $true)] - [System.Object] - $DesiredValues, - - [Parameter()] - [System.Array] - $ValuesToCheck - ) - - $returnValue = $true - - if (($DesiredValues.GetType().Name -ne 'HashTable') ` - -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` - -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) - { - $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) - New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage - } - - if (($DesiredValues.GetType().Name -eq 'CimInstance') -and ($null -eq $ValuesToCheck)) - { - $errorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck - New-InvalidArgumentException -ArgumentName 'ValuesToCheck' -Message $errorMessage - } - - if (($null -eq $ValuesToCheck) -or ($ValuesToCheck.Count -lt 1)) - { - $keyList = $DesiredValues.Keys - } - else - { - $keyList = $ValuesToCheck - } - - $keyList | ForEach-Object -Process { - if (($_ -ne 'Verbose')) - { - if (($CurrentValues.ContainsKey($_) -eq $false) ` - -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` - -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) - { - if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` - $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') - { - $checkDesiredValue = $DesiredValues.ContainsKey($_) - } - else - { - # If DesiredValue is a CimInstance. - $checkDesiredValue = $false - if (([System.Boolean]($DesiredValues.PSObject.Properties.Name -contains $_)) -eq $true) - { - if ($null -ne $DesiredValues.$_) - { - $checkDesiredValue = $true - } - } - } - - if ($checkDesiredValue) - { - $desiredType = $DesiredValues.$_.GetType() - $fieldName = $_ - if ($desiredType.IsArray -eq $true) - { - if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` - -or ($null -eq $CurrentValues.$fieldName)) - { - Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose - - $returnValue = $false - } - else - { - $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` - -DifferenceObject $DesiredValues.$fieldName - if ($null -ne $arrayCompare) - { - Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose - - $arrayCompare | ForEach-Object -Process { - Write-Verbose -Message ($script:localizedData.PropertyThatDoesNotMatch -f $_.InputObject, $_.SideIndicator) -Verbose - } - - $returnValue = $false - } - } - } - else - { - switch ($desiredType.Name) - { - 'String' - { - if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` - -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) - { - Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose - - $returnValue = $false - } - } - - 'Int32' - { - if (-not ($DesiredValues.$fieldName -eq 0) -or ` - -not ($null -eq $CurrentValues.$fieldName)) - { - Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose - - $returnValue = $false - } - } - - { $_ -eq 'Int16' -or $_ -eq 'UInt16' -or $_ -eq 'Single' } - { - if (-not ($DesiredValues.$fieldName -eq 0) -or ` - -not ($null -eq $CurrentValues.$fieldName)) - { - Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose - - $returnValue = $false - } - } - - 'Boolean' - { - if ($CurrentValues.$fieldName -ne $DesiredValues.$fieldName) - { - Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` - -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose - - $returnValue = $false - } - } - - default - { - Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` - -f $fieldName, $desiredType.Name) - - $returnValue = $false - } - } - } - } - } - } - } - - return $returnValue -} - -Export-ModuleMember -Function @( - 'Test-DscParameterState' -) diff --git a/Modules/DscResource.LocalizationHelper/DscResource.LocalizationHelper.psm1 b/Modules/DscResource.LocalizationHelper/DscResource.LocalizationHelper.psm1 deleted file mode 100644 index 32a3b847e..000000000 --- a/Modules/DscResource.LocalizationHelper/DscResource.LocalizationHelper.psm1 +++ /dev/null @@ -1,266 +0,0 @@ -<# - .SYNOPSIS - Retrieves the localized string data based on the machine's culture. - Falls back to en-US strings if the machine's culture is not supported. - - .PARAMETER ResourceName - The name of the resource as it appears before '.strings.psd1' of the localized string file. - For example: - For WindowsOptionalFeature: MSFT_WindowsOptionalFeature - For Service: MSFT_ServiceResource - For Registry: MSFT_RegistryResource - For Helper: SqlServerDscHelper - - .PARAMETER ScriptRoot - Optional. The root path where to expect to find the culture folder. This is only needed - for localization in helper modules. This should not normally be used for resources. - - .NOTES - To be able to use localization in the helper function, this function must - be first in the file, before Get-LocalizedData is used by itself to load - localized data for this helper module (see directly after this function). -#> -function Get-LocalizedData -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ResourceName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ScriptRoot - ) - - if (-not $ScriptRoot) - { - $dscResourcesFolder = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'DSCResources' - $resourceDirectory = Join-Path -Path $dscResourcesFolder -ChildPath $ResourceName - } - else - { - $resourceDirectory = $ScriptRoot - } - - $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture - - if (-not (Test-Path -Path $localizedStringFileLocation)) - { - # Fallback to en-US - $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' - } - - Import-LocalizedData ` - -BindingVariable 'localizedData' ` - -FileName "$ResourceName.strings.psd1" ` - -BaseDirectory $localizedStringFileLocation - - return $localizedData -} - -<# - .SYNOPSIS - Creates and throws an invalid argument exception. - - .PARAMETER Message - The message explaining why this error is being thrown. - - .PARAMETER ArgumentName - The name of the invalid argument that is causing this error to be thrown. -#> -function New-InvalidArgumentException -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ArgumentName - ) - - $argumentException = New-Object -TypeName 'ArgumentException' ` - -ArgumentList @($Message, $ArgumentName) - - $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) - } - - $errorRecord = New-Object @newObjectParameters - - throw $errorRecord -} - -<# - .SYNOPSIS - Creates and throws an invalid operation exception. - - .PARAMETER Message - The message explaining why this error is being thrown. - - .PARAMETER ErrorRecord - The error record containing the exception that is causing this terminating error. -#> -function New-InvalidOperationException -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Parameter()] - [ValidateNotNull()] - [System.Management.Automation.ErrorRecord] - $ErrorRecord - ) - - if ($null -eq $ErrorRecord) - { - $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @($Message) - } - else - { - $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` - -ArgumentList @($Message, $ErrorRecord.Exception) - } - - $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @( - $invalidOperationException.ToString(), - 'MachineStateIncorrect', - 'InvalidOperation', - $null - ) - } - - $errorRecordToThrow = New-Object @newObjectParameters - - throw $errorRecordToThrow -} - -<# - .SYNOPSIS - Creates and throws an object not found exception. - - .PARAMETER Message - The message explaining why this error is being thrown. - - .PARAMETER ErrorRecord - The error record containing the exception that is causing this terminating error. -#> -function New-ObjectNotFoundException -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Parameter()] - [ValidateNotNull()] - [System.Management.Automation.ErrorRecord] - $ErrorRecord - ) - - if ($null -eq $ErrorRecord) - { - $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) - } - else - { - $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message, $ErrorRecord.Exception) - } - - $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @( - $exception.ToString(), - 'MachineStateIncorrect', - 'ObjectNotFound', - $null - ) - } - - $errorRecordToThrow = New-Object @newObjectParameters - - throw $errorRecordToThrow -} - -<# - .SYNOPSIS - Creates and throws an invalid result exception. - - .PARAMETER Message - The message explaining why this error is being thrown. - - .PARAMETER ErrorRecord - The error record containing the exception that is causing this terminating error. -#> -function New-InvalidResultException -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $Message, - - [Parameter()] - [ValidateNotNull()] - [System.Management.Automation.ErrorRecord] - $ErrorRecord - ) - - if ($null -eq $ErrorRecord) - { - $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message) - } - else - { - $exception = New-Object -TypeName 'System.Exception' ` - -ArgumentList @($Message, $ErrorRecord.Exception) - } - - $newObjectParameters = @{ - TypeName = 'System.Management.Automation.ErrorRecord' - ArgumentList = @( - $exception.ToString(), - 'MachineStateIncorrect', - 'InvalidResult', - $null - ) - } - - $errorRecordToThrow = New-Object @newObjectParameters - - throw $errorRecordToThrow -} - -Export-ModuleMember -Function @( - 'New-InvalidArgumentException', - 'New-InvalidOperationException', - 'New-ObjectNotFoundException', - 'New-InvalidResultException' - 'Get-LocalizedData' -) diff --git a/Modules/DscResource.Common/en-US/DscResource.Common.strings.psd1 b/Modules/xActiveDirectory.Common/en-US/xActiveDirectory.Common.strings.psd1 similarity index 93% rename from Modules/DscResource.Common/en-US/DscResource.Common.strings.psd1 rename to Modules/xActiveDirectory.Common/en-US/xActiveDirectory.Common.strings.psd1 index 4739bcf2f..a11c727c3 100644 --- a/Modules/DscResource.Common/en-US/DscResource.Common.strings.psd1 +++ b/Modules/xActiveDirectory.Common/en-US/xActiveDirectory.Common.strings.psd1 @@ -1,4 +1,4 @@ -# Localized resources for helper module DscResource.Common. +# Localized resources for helper module xActiveDirectory.Common. ConvertFrom-StringData @' PropertyTypeInvalidForDesiredValues = Property 'DesiredValues' must be either a [System.Collections.Hashtable], [CimInstance] or [PSBoundParametersDictionary]. The type detected was {0}. diff --git a/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 b/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 new file mode 100644 index 000000000..3e05f4a5f --- /dev/null +++ b/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 @@ -0,0 +1,430 @@ +<# + .SYNOPSIS + Retrieves the localized string data based on the machine's culture. + Falls back to en-US strings if the machine's culture is not supported. + + .PARAMETER ResourceName + The name of the resource as it appears before '.strings.psd1' of the localized string file. + For example: + For WindowsOptionalFeature: MSFT_WindowsOptionalFeature + For Service: MSFT_ServiceResource + For Registry: MSFT_RegistryResource + For Helper: SqlServerDscHelper + + .PARAMETER ScriptRoot + Optional. The root path where to expect to find the culture folder. This is only needed + for localization in helper modules. This should not normally be used for resources. + + .NOTES + To be able to use localization in the helper function, this function must + be first in the file, before Get-LocalizedData is used by itself to load + localized data for this helper module (see directly after this function). +#> +function Get-LocalizedData +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ResourceName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ScriptRoot + ) + + if (-not $ScriptRoot) + { + $dscResourcesFolder = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'DSCResources' + $resourceDirectory = Join-Path -Path $dscResourcesFolder -ChildPath $ResourceName + } + else + { + $resourceDirectory = $ScriptRoot + } + + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture + + if (-not (Test-Path -Path $localizedStringFileLocation)) + { + # Fallback to en-US + $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' + } + + Import-LocalizedData ` + -BindingVariable 'localizedData' ` + -FileName "$ResourceName.strings.psd1" ` + -BaseDirectory $localizedStringFileLocation + + return $localizedData +} + +<# + .SYNOPSIS + Creates and throws an invalid argument exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ArgumentName + The name of the invalid argument that is causing this error to be thrown. +#> +function New-InvalidArgumentException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $ArgumentName + ) + + $argumentException = New-Object -TypeName 'ArgumentException' ` + -ArgumentList @($Message, $ArgumentName) + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @($argumentException, $ArgumentName, 'InvalidArgument', $null) + } + + $errorRecord = New-Object @newObjectParameters + + throw $errorRecord +} + +<# + .SYNOPSIS + Creates and throws an invalid operation exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidOperationException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message) + } + else + { + $invalidOperationException = New-Object -TypeName 'InvalidOperationException' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $invalidOperationException.ToString(), + 'MachineStateIncorrect', + 'InvalidOperation', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an object not found exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-ObjectNotFoundException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'ObjectNotFound', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} + +<# + .SYNOPSIS + Creates and throws an invalid result exception. + + .PARAMETER Message + The message explaining why this error is being thrown. + + .PARAMETER ErrorRecord + The error record containing the exception that is causing this terminating error. +#> +function New-InvalidResultException +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.ErrorRecord] + $ErrorRecord + ) + + if ($null -eq $ErrorRecord) + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message) + } + else + { + $exception = New-Object -TypeName 'System.Exception' ` + -ArgumentList @($Message, $ErrorRecord.Exception) + } + + $newObjectParameters = @{ + TypeName = 'System.Management.Automation.ErrorRecord' + ArgumentList = @( + $exception.ToString(), + 'MachineStateIncorrect', + 'InvalidResult', + $null + ) + } + + $errorRecordToThrow = New-Object @newObjectParameters + + throw $errorRecordToThrow +} +function Test-DscParameterState +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $CurrentValues, + + [Parameter(Mandatory = $true)] + [System.Object] + $DesiredValues, + + [Parameter()] + [System.Array] + $ValuesToCheck + ) + $returnValue = $true + + if (($DesiredValues.GetType().Name -ne 'HashTable') ` + -and ($DesiredValues.GetType().Name -ne 'CimInstance') ` + -and ($DesiredValues.GetType().Name -ne 'PSBoundParametersDictionary')) + { + $errorMessage = $script:localizedData.PropertyTypeInvalidForDesiredValues -f $($DesiredValues.GetType().Name) + New-InvalidArgumentException -ArgumentName 'DesiredValues' -Message $errorMessage + } + + if (($DesiredValues.GetType().Name -eq 'CimInstance') -and ($null -eq $ValuesToCheck)) + { + $errorMessage = $script:localizedData.PropertyTypeInvalidForValuesToCheck + New-InvalidArgumentException -ArgumentName 'ValuesToCheck' -Message $errorMessage + } + + if (($null -eq $ValuesToCheck) -or ($ValuesToCheck.Count -lt 1)) + { + $keyList = $DesiredValues.Keys + } + else + { + $keyList = $ValuesToCheck + } + + $keyList | ForEach-Object -Process { + if (($_ -ne 'Verbose')) + { + if (($CurrentValues.ContainsKey($_) -eq $false) ` + -or ($CurrentValues.$_ -ne $DesiredValues.$_) ` + -or (($DesiredValues.GetType().Name -ne 'CimInstance' -and $DesiredValues.ContainsKey($_) -eq $true) -and ($null -ne $DesiredValues.$_ -and $DesiredValues.$_.GetType().IsArray))) + { + if ($DesiredValues.GetType().Name -eq 'HashTable' -or ` + $DesiredValues.GetType().Name -eq 'PSBoundParametersDictionary') + { + $checkDesiredValue = $DesiredValues.ContainsKey($_) + } + else + { + # If DesiredValue is a CimInstance. + $checkDesiredValue = $false + if (([System.Boolean]($DesiredValues.PSObject.Properties.Name -contains $_)) -eq $true) + { + if ($null -ne $DesiredValues.$_) + { + $checkDesiredValue = $true + } + } + } + + if ($checkDesiredValue) + { + $desiredType = $DesiredValues.$_.GetType() + $fieldName = $_ + if ($desiredType.IsArray -eq $true) + { + if (($CurrentValues.ContainsKey($fieldName) -eq $false) ` + -or ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.PropertyValidationError -f $fieldName) -Verbose + + $returnValue = $false + } + else + { + $arrayCompare = Compare-Object -ReferenceObject $CurrentValues.$fieldName ` + -DifferenceObject $DesiredValues.$fieldName + if ($null -ne $arrayCompare) + { + Write-Verbose -Message ($script:localizedData.PropertiesDoesNotMatch -f $fieldName) -Verbose + + $arrayCompare | ForEach-Object -Process { + Write-Verbose -Message ($script:localizedData.PropertyThatDoesNotMatch -f $_.InputObject, $_.SideIndicator) -Verbose + } + + $returnValue = $false + } + } + } + else + { + switch ($desiredType.Name) + { + 'String' + { + if (-not [System.String]::IsNullOrEmpty($CurrentValues.$fieldName) -or ` + -not [System.String]::IsNullOrEmpty($DesiredValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + 'Int32' + { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + { $_ -eq 'Int16' -or $_ -eq 'UInt16' -or $_ -eq 'Single' } + { + if (-not ($DesiredValues.$fieldName -eq 0) -or ` + -not ($null -eq $CurrentValues.$fieldName)) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + 'Boolean' + { + if ($CurrentValues.$fieldName -ne $DesiredValues.$fieldName) + { + Write-Verbose -Message ($script:localizedData.ValueOfTypeDoesNotMatch ` + -f $desiredType.Name, $fieldName, $($CurrentValues.$fieldName), $($DesiredValues.$fieldName)) -Verbose + + $returnValue = $false + } + } + + default + { + Write-Warning -Message ($script:localizedData.UnableToCompareProperty ` + -f $fieldName, $desiredType.Name) + + $returnValue = $false + } + } + } + } + } + } + } + + return $returnValue +} +$script:localizedData = Get-LocalizedData -ResourceName 'xActiveDirectory.Common' -ScriptRoot $PSScriptRoot +Export-ModuleMember -Function @( + 'New-InvalidArgumentException' + 'New-InvalidOperationException' + 'New-ObjectNotFoundException' + 'New-InvalidResultException' + 'Get-LocalizedData' + 'Test-DscParameterState' +) diff --git a/Tests/Unit/DscResource.LocalizationHelper.Tests.ps1 b/Tests/Unit/DscResource.LocalizationHelper.Tests.ps1 deleted file mode 100644 index 6699d1d5c..000000000 --- a/Tests/Unit/DscResource.LocalizationHelper.Tests.ps1 +++ /dev/null @@ -1,186 +0,0 @@ -# Import the DscResource.LocalizationHelper module to test -$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent -$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules\DscResource.LocalizationHelper' - -Import-Module -Name (Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.LocalizationHelper.psm1') -Force - -InModuleScope 'DscResource.LocalizationHelper' { - Describe 'DscResource.LocalizationHelper\Get-LocalizedData' { - $mockTestPath = { - return $mockTestPathReturnValue - } - - $mockImportLocalizedData = { - $BaseDirectory | Should -Be $mockExpectedLanguagePath - } - - BeforeEach { - Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable - Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable - } - - Context 'When loading localized data for Swedish' { - $mockExpectedLanguagePath = 'sv-SE' - $mockTestPathReturnValue = $true - - It 'Should call Import-LocalizedData with sv-SE language' { - Mock -CommandName Join-Path -MockWith { - return 'sv-SE' - } -Verifiable - - { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw - - Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It - Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It - } - - $mockExpectedLanguagePath = 'en-US' - $mockTestPathReturnValue = $false - - It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { - Mock -CommandName Join-Path -MockWith { - return $ChildPath - } -Verifiable - - { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw - - Assert-MockCalled -CommandName Join-Path -Exactly -Times 4 -Scope It - Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It - } - - Context 'When $ScriptRoot is set to a path' { - $mockExpectedLanguagePath = 'sv-SE' - $mockTestPathReturnValue = $true - - It 'Should call Import-LocalizedData with sv-SE language' { - Mock -CommandName Join-Path -MockWith { - return 'sv-SE' - } -Verifiable - - { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw - - Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It - } - - $mockExpectedLanguagePath = 'en-US' - $mockTestPathReturnValue = $false - - It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { - Mock -CommandName Join-Path -MockWith { - return $ChildPath - } -Verifiable - - { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw - - Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It - Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It - } - } - } - - Context 'When loading localized data for English' { - Mock -CommandName Join-Path -MockWith { - return 'en-US' - } -Verifiable - - $mockExpectedLanguagePath = 'en-US' - $mockTestPathReturnValue = $true - - It 'Should call Import-LocalizedData with en-US language' { - { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw - } - } - - Assert-VerifiableMock - } - - Describe 'DscResource.LocalizationHelper\New-InvalidResultException' { - Context 'When calling with Message parameter only' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - - { New-InvalidResultException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage - } - } - - Context 'When calling with both the Message and ErrorRecord parameter' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - $mockExceptionErrorMessage = 'Mocked exception error message' - - $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage - $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null - - { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) - } - } - - Assert-VerifiableMock - } - - Describe 'DscResource.LocalizationHelper\New-ObjectNotFoundException' { - Context 'When calling with Message parameter only' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - - { New-ObjectNotFoundException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage - } - } - - Context 'When calling with both the Message and ErrorRecord parameter' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - $mockExceptionErrorMessage = 'Mocked exception error message' - - $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage - $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null - - { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) - } - } - - Assert-VerifiableMock - } - - Describe 'DscResource.LocalizationHelper\New-InvalidOperationException' { - Context 'When calling with Message parameter only' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - - { New-InvalidOperationException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage - } - } - - Context 'When calling with both the Message and ErrorRecord parameter' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - $mockExceptionErrorMessage = 'Mocked exception error message' - - $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage - $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null - - { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) - } - } - - Assert-VerifiableMock - } - - Describe 'DscResource.LocalizationHelper\New-InvalidArgumentException' { - Context 'When calling with both the Message and ArgumentName parameter' { - It 'Should throw the correct error' { - $mockErrorMessage = 'Mocked error' - $mockArgumentName = 'MockArgument' - - { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should -Throw ('Parameter name: {0}' -f $mockArgumentName) - } - } - - Assert-VerifiableMock - } -} diff --git a/Tests/Unit/DscResource.Common.Tests.ps1 b/Tests/Unit/xActiveDirectory.Common.Tests.ps1 similarity index 55% rename from Tests/Unit/DscResource.Common.Tests.ps1 rename to Tests/Unit/xActiveDirectory.Common.Tests.ps1 index 159086b5c..d183d0b81 100644 --- a/Tests/Unit/DscResource.Common.Tests.ps1 +++ b/Tests/Unit/xActiveDirectory.Common.Tests.ps1 @@ -1,11 +1,11 @@ -# Import the DscResource.Common module to test +# Import the xActiveDirectory.Common module to test $script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent -$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules\DscResource.Common' +$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules\xActiveDirectory.Common' -Import-Module -Name (Join-Path -Path $script:modulesFolderPath -ChildPath 'DscResource.Common.psm1') -Force +Import-Module -Name (Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common.psm1') -Force -InModuleScope 'DscResource.Common' { - Describe 'DscResource.Common\Test-DscParameterState' -Tag TestDscParameterState { +InModuleScope 'xActiveDirectory.Common' { + Describe 'xActiveDirectory.Common\Test-DscParameterState' -Tag TestDscParameterState { Context -Name 'When passing values' -Fixture { It 'Should return true for two identical tables' { $mockDesiredValues = @{ Example = 'test' } @@ -92,7 +92,7 @@ InModuleScope 'DscResource.Common' { It 'Should return true when only a specified value matches, but other non-listed values do not' { $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } - $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -105,7 +105,7 @@ InModuleScope 'DscResource.Common' { It 'Should return false when only specified values do not match, but other non-listed values do ' { $mockCurrentValues = @{ Example = 'test'; SecondExample = 'true' } - $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -118,7 +118,7 @@ InModuleScope 'DscResource.Common' { It 'Should return false when an empty hash table is used in the current values' { $mockCurrentValues = @{ } - $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } + $mockDesiredValues = @{ Example = 'test'; SecondExample = 'false' } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -129,17 +129,17 @@ InModuleScope 'DscResource.Common' { } It 'Should return true when evaluating a table against a CimInstance' { - $mockCurrentValues = @{ Handle = '0'; ProcessId = '1000' } + $mockCurrentValues = @{ Handle = '0'; ProcessId = '1000' } $mockWin32ProcessProperties = @{ - Handle = 0 + Handle = 0 ProcessId = 1000 } $mockNewCimInstanceParameters = @{ - ClassName = 'Win32_Process' - Property = $mockWin32ProcessProperties - Key = 'Handle' + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' ClientOnly = $true } @@ -148,24 +148,24 @@ InModuleScope 'DscResource.Common' { $testParameters = @{ CurrentValues = $mockCurrentValues DesiredValues = $mockDesiredValues - ValuesToCheck = @('Handle','ProcessId') + ValuesToCheck = @('Handle', 'ProcessId') } Test-DscParameterState @testParameters | Should -Be $true } It 'Should return false when evaluating a table against a CimInstance and a value is wrong' { - $mockCurrentValues = @{ Handle = '1'; ProcessId = '1000' } + $mockCurrentValues = @{ Handle = '1'; ProcessId = '1000' } $mockWin32ProcessProperties = @{ - Handle = 0 + Handle = 0 ProcessId = 1000 } $mockNewCimInstanceParameters = @{ - ClassName = 'Win32_Process' - Property = $mockWin32ProcessProperties - Key = 'Handle' + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' ClientOnly = $true } @@ -174,15 +174,15 @@ InModuleScope 'DscResource.Common' { $testParameters = @{ CurrentValues = $mockCurrentValues DesiredValues = $mockDesiredValues - ValuesToCheck = @('Handle','ProcessId') + ValuesToCheck = @('Handle', 'ProcessId') } Test-DscParameterState @testParameters | Should -Be $false } It 'Should return true when evaluating a hash table containing an array' { - $mockCurrentValues = @{ Example = 'test'; SecondExample = @('1','2') } - $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('1', '2') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -193,8 +193,8 @@ InModuleScope 'DscResource.Common' { } It 'Should return false when evaluating a hash table containing an array with wrong values' { - $mockCurrentValues = @{ Example = 'test'; SecondExample = @('A','B') } - $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + $mockCurrentValues = @{ Example = 'test'; SecondExample = @('A', 'B') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -206,7 +206,7 @@ InModuleScope 'DscResource.Common' { It 'Should return false when evaluating a hash table containing an array, but the CurrentValues are missing an array' { $mockCurrentValues = @{ Example = 'test' } - $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -218,7 +218,7 @@ InModuleScope 'DscResource.Common' { It 'Should return false when evaluating a hash table containing an array, but the property i CurrentValues is $null' { $mockCurrentValues = @{ Example = 'test'; SecondExample = $null } - $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1','2') } + $mockDesiredValues = @{ Example = 'test'; SecondExample = @('1', '2') } $testParameters = @{ CurrentValues = $mockCurrentValues @@ -281,14 +281,14 @@ InModuleScope 'DscResource.Common' { $mockCurrentValues = @{ Example = 'something' } $mockWin32ProcessProperties = @{ - Handle = 0 + Handle = 0 ProcessId = 1000 } $mockNewCimInstanceParameters = @{ - ClassName = 'Win32_Process' - Property = $mockWin32ProcessProperties - Key = 'Handle' + ClassName = 'Win32_Process' + Property = $mockWin32ProcessProperties + Key = 'Handle' ClientOnly = $true } @@ -305,6 +305,184 @@ InModuleScope 'DscResource.Common' { } } + Assert-VerifiableMock + } + Describe 'xActiveDirectory.Common\Get-LocalizedData' { + $mockTestPath = { + return $mockTestPathReturnValue + } + + $mockImportLocalizedData = { + $BaseDirectory | Should -Be $mockExpectedLanguagePath + } + + BeforeEach { + Mock -CommandName Test-Path -MockWith $mockTestPath -Verifiable + Mock -CommandName Import-LocalizedData -MockWith $mockImportLocalizedData -Verifiable + } + + Context 'When loading localized data for Swedish' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 3 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 4 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + Context 'When $ScriptRoot is set to a path' { + $mockExpectedLanguagePath = 'sv-SE' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with sv-SE language' { + Mock -CommandName Join-Path -MockWith { + return 'sv-SE' + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $false + + It 'Should call Import-LocalizedData and fallback to en-US if sv-SE language does not exist' { + Mock -CommandName Join-Path -MockWith { + return $ChildPath + } -Verifiable + + { Get-LocalizedData -ResourceName 'DummyResource' -ScriptRoot '.' } | Should -Not -Throw + + Assert-MockCalled -CommandName Join-Path -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Test-Path -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Import-LocalizedData -Exactly -Times 1 -Scope It + } + } + } + + Context 'When loading localized data for English' { + Mock -CommandName Join-Path -MockWith { + return 'en-US' + } -Verifiable + + $mockExpectedLanguagePath = 'en-US' + $mockTestPathReturnValue = $true + + It 'Should call Import-LocalizedData with en-US language' { + { Get-LocalizedData -ResourceName 'DummyResource' } | Should -Not -Throw + } + } + + Assert-VerifiableMock + } + + Describe 'xActiveDirectory.Common\New-InvalidResultException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidResultException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidResultException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'xActiveDirectory.Common\New-ObjectNotFoundException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-ObjectNotFoundException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-ObjectNotFoundException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.Exception: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'xActiveDirectory.Common\New-InvalidOperationException' { + Context 'When calling with Message parameter only' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + + { New-InvalidOperationException -Message $mockErrorMessage } | Should -Throw $mockErrorMessage + } + } + + Context 'When calling with both the Message and ErrorRecord parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockExceptionErrorMessage = 'Mocked exception error message' + + $mockException = New-Object -TypeName System.Exception -ArgumentList $mockExceptionErrorMessage + $mockErrorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $mockException, $null, 'InvalidResult', $null + + { New-InvalidOperationException -Message $mockErrorMessage -ErrorRecord $mockErrorRecord } | Should -Throw ('System.InvalidOperationException: {0} ---> System.Exception: {1}' -f $mockErrorMessage, $mockExceptionErrorMessage) + } + } + + Assert-VerifiableMock + } + + Describe 'xActiveDirectory.Common\New-InvalidArgumentException' { + Context 'When calling with both the Message and ArgumentName parameter' { + It 'Should throw the correct error' { + $mockErrorMessage = 'Mocked error' + $mockArgumentName = 'MockArgument' + + { New-InvalidArgumentException -Message $mockErrorMessage -ArgumentName $mockArgumentName } | Should -Throw ('Parameter name: {0}' -f $mockArgumentName) + } + } + Assert-VerifiableMock } }