From f081589fe7b2cda9c236f7a6c255c99e3d853bc0 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 20 Jul 2024 10:58:49 +0200 Subject: [PATCH] `Get-PSModulePath`: Throw exception on missing My Documents folder (#125) --- CHANGELOG.md | 13 +++- source/Public/Get-PSModulePath.ps1 | 13 ++++ source/Public/Get-UserName.ps1 | 37 ++++++++++++ source/en-US/DscResource.Common.strings.psd1 | 3 + .../Public/Set-PSModulePath.Tests.ps1 | 2 +- tests/Unit/Public/Get-UserName.Tests.ps1 | 60 +++++++++++++++++++ 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 source/Public/Get-UserName.ps1 create mode 100644 tests/Unit/Public/Get-UserName.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 82b7868..81f8bce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Public command: + - `Get-UserName` - get current user name cross platform. + +### Fixed + +- `Get-PSModulePath` + - Throws an exception if the My Documents folder cannot be found when + calling the command with the scope `CurrentUser` ([issue #122](https://github.com/dsccommunity/DscResource.Common/issues/122)). + ## [0.17.1] - 2024-04-23 ### Added @@ -25,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Was using the wrong path separator on Linux and macOS. - `Get-LocalizedData` - Wrongly returned one or more boolean values in addition to - the localized string array. This was becuase the return value + the localized string array. This was because the return value was not handled when calling `Add()` and `Remove()` methods of `$PSBoundParameters` so it was returned to the pipeline. diff --git a/source/Public/Get-PSModulePath.ps1 b/source/Public/Get-PSModulePath.ps1 index 2f61e5b..2a5ba0a 100644 --- a/source/Public/Get-PSModulePath.ps1 +++ b/source/Public/Get-PSModulePath.ps1 @@ -131,6 +131,19 @@ function Get-PSModulePath { $documentsFolder = [Environment]::GetFolderPath('MyDocuments') + # When the $documentsFolder is null or empty string the folder does not exist. + if ([System.String]::IsNullOrEmpty($documentsFolder)) + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.PSModulePath_MissingMyDocumentsPath -f (Get-UserName)), + 'MissingMyDocumentsPath', + [System.Management.Automation.ErrorCategory]::ResourceUnavailable, + (Get-UserName) + ) + ) + } + if ($IsCoreCLR) { Join-Path -Path $documentsFolder -ChildPath 'PowerShell/Modules' diff --git a/source/Public/Get-UserName.ps1 b/source/Public/Get-UserName.ps1 new file mode 100644 index 0000000..f79e97d --- /dev/null +++ b/source/Public/Get-UserName.ps1 @@ -0,0 +1,37 @@ +<# + .SYNOPSIS + Returns the user name cross-plattform. + + .DESCRIPTION + Returns the current user name cross-plattform. The variable `$env:USERNAME` + does not exist cross-platform which hinders development and testing on + macOS and Linux. Instead this command can be used to get the user name + cross-plattform. + + .OUTPUTS + System.String + + .EXAMPLE + Get-UserName + + Returns the user name regardless of platform. +#> +function Get-UserName +{ + [CmdletBinding()] + [OutputType([System.String])] + param () + + $userName = $null + + if ($IsLinux -or $IsMacOs) + { + $userName = $env:USER + } + else + { + $userName = $env:USERNAME + } + + return $userName +} diff --git a/source/en-US/DscResource.Common.strings.psd1 b/source/en-US/DscResource.Common.strings.psd1 index 22eada9..141951b 100644 --- a/source/en-US/DscResource.Common.strings.psd1 +++ b/source/en-US/DscResource.Common.strings.psd1 @@ -47,4 +47,7 @@ ConvertFrom-StringData @' ## Find-Certificate CertificatePathError = Certificate Path '{0}' is not valid. (DRC0046) SearchingForCertificateUsingFilters = Looking for certificate in Store '{0}' using filter '{1}'. (DRC0047) + + ## Get-PSModulePath + PSModulePath_MissingMyDocumentsPath = The My Documents folder does not exist for user '{0}'. (DRC0048) '@ diff --git a/tests/Integration/Public/Set-PSModulePath.Tests.ps1 b/tests/Integration/Public/Set-PSModulePath.Tests.ps1 index f6c8d8c..6668f37 100644 --- a/tests/Integration/Public/Set-PSModulePath.Tests.ps1 +++ b/tests/Integration/Public/Set-PSModulePath.Tests.ps1 @@ -1,6 +1,6 @@ BeforeDiscovery { # Determines if we should skip tests. - if ($isWindows -or $PSEdition -eq 'Desktop') + if ($IsWindows -or $PSEdition -eq 'Desktop') { $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) diff --git a/tests/Unit/Public/Get-UserName.Tests.ps1 b/tests/Unit/Public/Get-UserName.Tests.ps1 new file mode 100644 index 0000000..d52b327 --- /dev/null +++ b/tests/Unit/Public/Get-UserName.Tests.ps1 @@ -0,0 +1,60 @@ +[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 + + # Re-import the module using force to get any code changes between runs. + Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop' + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Remove-Module -Name $script:moduleName +} + +Describe 'Get-UserName' { + Context 'When getting user name on Windows' -Skip:($IsLinux -or $IsMacOs) { + It 'Should return the correct user name' { + Get-UserName | Should -Be $env:USERNAME + } + } + + Context 'When getting user name' -Skip:($IsWindows -or $PSEdition -eq 'Desktop') { + It 'Should return the correct user name' { + Get-UserName | Should -Be $env:USER + } + } +}