diff --git a/Build/build.depend.psd1 b/Build/build.depend.psd1 index d56546d..15d06eb 100644 --- a/Build/build.depend.psd1 +++ b/Build/build.depend.psd1 @@ -15,7 +15,7 @@ # Common modules BuildHelpers = '2.0.1' - Pester = '4.6.0' + Pester = '5.5.0' PlatyPS = '0.12.0' psake = '4.7.4' PSDeploy = '1.0.1' diff --git a/PSHipChat/PShipchat.psd1 b/PSHipChat/PShipchat.psd1 index 60e2d6d..66e8f46 100644 --- a/PSHipChat/PShipchat.psd1 +++ b/PSHipChat/PShipchat.psd1 @@ -12,7 +12,7 @@ RootModule = 'PSHipchat.psm1' # Version number of this module. -ModuleVersion = '1.0.39' +ModuleVersion = '1.0.40' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index a2bb9e7..4cb275a 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,11 +1,12 @@ @{ ExcludeRules = @( - 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseDeclaredVarsMoreThanAssignments' 'PSAvoidTrailingWhitespace' + 'PSAvoidOverwritingBuiltInCmdlets' ) Severity = @( - "Warning", + "Warning" "Error" ) diff --git a/Tests/Common/Help.Tests.ps1 b/Tests/Common/Help.Tests.ps1 index ab77b34..9e2feef 100644 --- a/Tests/Common/Help.Tests.ps1 +++ b/Tests/Common/Help.Tests.ps1 @@ -1,100 +1,117 @@ # Taken with love from @juneb_get_help (https://raw.githubusercontent.com/juneb/PesterTDD/master/Module.Help.Tests.ps1) -# Import module -if (-not (Get-Module -Name $env:BHProjectName -ListAvailable)) { - Import-Module -Name $env:BHPSModuleManifest -ErrorAction 'Stop' -Force + +BeforeDiscovery { + function global:FilterOutCommonParams { + param ($Params) + $commonParams = @( + 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', + 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', + 'WarningVariable', 'Confirm', 'Whatif', 'ProgressAction' + ) + $params | Where-Object { $_.Name -notin $commonParams } | Sort-Object -Property Name -Unique + } + + $env:BHProjectName = (Get-ChildItem $PSScriptRoot/../../*.psm1 -Recurse).BaseName + + # Get module commands + # Remove all versions of the module from the session. Pester can't handle multiple versions. + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore + Import-Module -Name $PSScriptRoot/../../$env:BHProjectName -Verbose:$false -ErrorAction Stop + $params = @{ + Module = (Get-Module $env:BHProjectName) + CommandType = [System.Management.Automation.CommandTypes[]]'Cmdlet, Function' # Not alias + } + if ($PSVersionTable.PSVersion.Major -lt 6) { + $params.CommandType[0] += 'Workflow' + } + $commands = Get-Command @params + + ## When testing help, remember that help is cached at the beginning of each session. + ## To test, restart session. } -$commands = Get-Command -Module $env:BHProjectName -CommandType Cmdlet, Function, Workflow -ErrorAction 'Stop' # Not alias -## When testing help, remember that help is cached at the beginning of each session. -## To test, restart session. -foreach ($command in $commands) { - $commandName = $command.Name +AfterAll { + Remove-Item Function:/FilterOutCommonParams + Get-Module $env:BHProjectName | Remove-Module -Force -ErrorAction Ignore +} - # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets - $help = Get-Help $commandName -ErrorAction SilentlyContinue +Describe "Test help for <_.Name>" -ForEach $commands { - Describe "Test help for $commandName" { + BeforeDiscovery { + # Get command help, parameters, and links + $command = $_ + $commandHelp = Get-Help $command.Name -ErrorAction SilentlyContinue + $commandParameters = global:FilterOutCommonParams -Params $command.ParameterSets.Parameters + $commandParameterNames = $commandParameters.Name + $helpLinks = $commandHelp.relatedLinks.navigationLink.uri + } - # If help is not found, synopsis in auto-generated help is the syntax diagram - It 'Should not be auto-generated' { - $help.Synopsis | Should Not BeLike '*`[``]*' - } + BeforeAll { + # These vars are needed in both discovery and test phases so we need to duplicate them here + $command = $_ + $commandName = $_.Name + $commandHelp = Get-Help $command.Name -ErrorAction SilentlyContinue + $commandParameters = global:FilterOutCommonParams -Params $command.ParameterSets.Parameters + $commandParameterNames = $commandParameters.Name + $helpParameters = global:FilterOutCommonParams -Params $commandHelp.Parameters.Parameter + $helpParameterNames = $helpParameters.Name + } + + # If help is not found, synopsis in auto-generated help is the syntax diagram + It 'Help is not auto-generated' { + $commandHelp.Synopsis | Should -Not -BeLike '*`[``]*' + } - # Should be a description for every function - It "Gets description for $commandName" { - $help.Description | Should Not BeNullOrEmpty + # Should be a description for every function + It "Has description" { + $commandHelp.Description | Should -Not -BeNullOrEmpty + } + + # Should be at least one example + It "Has example code" { + ($commandHelp.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty + } + + # Should be at least one example description + It "Has example help" { + ($commandHelp.Examples.Example.Remarks | Select-Object -First 1).Text | Should -Not -BeNullOrEmpty + } + + It "Help link <_> is valid" -ForEach $helpLinks { + (Invoke-WebRequest -Uri $_ -UseBasicParsing).StatusCode | Should -Be '200' + } + + Context "Parameter <_.Name>" -Foreach $commandParameters { + + BeforeAll { + $parameter = $_ + $parameterName = $parameter.Name + $parameterHelp = $commandHelp.parameters.parameter | Where-Object Name -eq $parameterName + $parameterHelpType = if ($parameterHelp.ParameterValue) { $parameterHelp.ParameterValue.Trim() } } - # Should be at least one example - It "Gets example code from $commandName" { - ($help.Examples.Example | Select-Object -First 1).Code | Should Not BeNullOrEmpty + # Should be a description for every parameter + It "Has description" { + $parameterHelp.Description.Text | Should -Not -BeNullOrEmpty } - # Should be at least one example description - It "Gets example help from $commandName" { - ($help.Examples.Example.Remarks | Select-Object -First 1).Text | Should Not BeNullOrEmpty + # Required value in Help should match IsMandatory property of parameter + It "Has correct [mandatory] value" { + $codeMandatory = $_.IsMandatory.toString() + $parameterHelp.Required | Should -Be $codeMandatory } - Context "Test parameter help for $commandName" { - - $common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', - 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable', 'Confirm', 'Whatif' - - $parameters = $command.ParameterSets.Parameters | - Sort-Object -Property Name -Unique | - Where-Object { $_.Name -notin $common } - $parameterNames = $parameters.Name - - ## Without the filter, WhatIf and Confirm parameters are still flagged in "finds help parameter in code" test - $helpParameters = $help.Parameters.Parameter | - Where-Object { $_.Name -notin $common } | - Sort-Object -Property Name -Unique - $helpParameterNames = $helpParameters.Name - - foreach ($parameter in $parameters) { - $parameterName = $parameter.Name - $parameterHelp = $help.parameters.parameter | Where-Object Name -EQ $parameterName - - # Should be a description for every parameter - It "Gets help for parameter: $parameterName : in $commandName" { - $parameterHelp.Description.Text | Should Not BeNullOrEmpty - } - - # Required value in Help should match IsMandatory property of parameter - It "Help for $parameterName parameter in $commandName has correct Mandatory value" { - $codeMandatory = $parameter.IsMandatory.toString() - $parameterHelp.Required | Should Be $codeMandatory - } - - # Parameter type in Help should match code - # It "help for $commandName has correct parameter type for $parameterName" { - # $codeType = $parameter.ParameterType.Name - # # To avoid calling Trim method on a null object. - # $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } - # $helpType | Should be $codeType - # } - } - - foreach ($helpParm in $HelpParameterNames) { - # Shouldn't find extra parameters in help. - It "Finds help parameter in code: $helpParm" { - $helpParm -in $parameterNames | Should Be $true - } - } + # Parameter type in help should match code + It "Has correct parameter type" { + $parameterHelpType | Should -Be $parameter.ParameterType.Name } + } + + Context "Test <_> help parameter help for " -Foreach $helpParameterNames { - Context "Help Links should be Valid for $commandName" { - $link = $help.relatedLinks.navigationLink.uri - - foreach ($link in $links) { - if ($link) { - # Should have a valid uri if one is provided. - It "[$link] should have 200 Status Code for $commandName" { - $Results = Invoke-WebRequest -Uri $link -UseBasicParsing - $Results.StatusCode | Should Be '200' - } - } - } + # Shouldn't find extra parameters in help. + It "finds help parameter in code: <_>" { + $_ -in $parameterNames | Should -Be $true } } -} +} \ No newline at end of file diff --git a/Tests/Common/Manifest.Tests.ps1 b/Tests/Common/Manifest.Tests.ps1 index 4be0208..e3adb1b 100644 --- a/Tests/Common/Manifest.Tests.ps1 +++ b/Tests/Common/Manifest.Tests.ps1 @@ -1,83 +1,88 @@ -# Vars -$changelogPath = Join-Path -Path $env:BHProjectPath -Child 'CHANGELOG.md' +BeforeAll { + $env:BHProjectPath = Join-Path $PSScriptRoot "../../" + $env:BHProjectName = (Get-ChildItem $env:BHProjectPath -Filter '*.psm1' -Recurse).BaseName + $env:BHPSModuleManifest = (Get-ChildItem $env:BHProjectPath -Filter "${env:BHProjectName}.psd1" -Recurse).FullName + + $moduleName = $env:BHProjectName + $manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest + $outputManifestPath = Join-Path -Path (Join-Path $env:BHProjectPath $env:BHProjectName) "$($moduleName).psd1" + $manifestData = Test-ModuleManifest -Path $outputManifestPath -Verbose:$false -ErrorAction Stop -WarningAction SilentlyContinue + + $changelogPath = Join-Path -Path $env:BHProjectPath -Child 'CHANGELOG.md' + $changelogVersion = Get-Content $changelogPath | ForEach-Object { + if ($_ -match "^##\s\[(?(\d+\.){1,3}\d+)\]") { + $changelogVersion = $matches.Version + break + } + } + + $script:manifest = $null +} Describe 'Module manifest' { - Context 'Validation' { - $script:manifest = $null + Context 'Validation' { It 'Has a valid manifest' { - { - $script:manifest = Test-ModuleManifest -Path $env:BHPSModuleManifest -Verbose:$false -ErrorAction 'Stop' -WarningAction 'SilentlyContinue' - } | Should Not Throw + $manifestData | Should -Not -BeNullOrEmpty } It 'Has a valid name in the manifest' { - $script:manifest.Name | Should Be $env:BHProjectName + $manifestData.Name | Should -Be $moduleName } It 'Has a valid root module' { - $script:manifest.RootModule | Should Be "$($env:BHProjectName).psm1" + $manifestData.RootModule | Should -Be "$($moduleName).psm1" } It 'Has a valid version in the manifest' { - $script:manifest.Version -as [Version] | Should Not BeNullOrEmpty + $manifestData.Version -as [Version] | Should -Not -BeNullOrEmpty } It 'Has a valid description' { - $script:manifest.Description | Should Not BeNullOrEmpty + $manifestData.Description | Should -Not -BeNullOrEmpty } It 'Has a valid author' { - $script:manifest.Author | Should Not BeNullOrEmpty + $manifestData.Author | Should -Not -BeNullOrEmpty } It 'Has a valid guid' { - { - [guid]::Parse($script:manifest.Guid) - } | Should Not throw + { [guid]::Parse($manifestData.Guid) } | Should -Not -Throw } It 'Has a valid copyright' { - $script:manifest.CopyRight | Should Not BeNullOrEmpty + $manifestData.CopyRight | Should -Not -BeNullOrEmpty } - # Only for DSC modules - # It 'exports DSC resources' { - # $dscResources = ($Manifest.psobject.Properties | Where Name -eq 'ExportedDscResources').Value - # @($dscResources).Count | Should Not Be 0 - # } - - $script:changelogVersion = $null - It 'Has a valid version in the changelog' -Skip { - foreach ($line in (Get-Content $changelogPath)) { - if ($line -match "^##\s\[(?(\d+\.){1,3}\d+)\]") { - $script:changelogVersion = $matches.Version - break - } - } - $script:changelogVersion | Should Not BeNullOrEmpty - $script:changelogVersion -as [Version] | Should Not BeNullOrEmpty + It 'Has a valid version in the changelog' { + $changelogVersion | Should -Not -BeNullOrEmpty + $changelogVersion -as [Version] | Should -Not -BeNullOrEmpty } - It 'Has matching changelog and manifest versions' -Skip { - $script:changelogVersion -as [Version] | Should be ( $script:manifest.Version -as [Version] ) + It 'Changelog and manifest versions are the same' { + $changelogVersion -as [Version] | Should -Be ( $manifestData.Version -as [Version] ) } + } +} - if (Get-Command -Name 'git.exe' -ErrorAction 'SilentlyContinue') { - $script:tagVersion = $null +Describe 'Git tagging' -Skip { + + BeforeAll { + $gitTagVersion = $null - # Skipped as we tag as part of CI build - It 'Is tagged with a valid version' -skip { - $thisCommit = git.exe log --decorate --oneline HEAD~1..HEAD + if ($git = Get-Command git -CommandType Application -ErrorAction SilentlyContinue) { + $thisCommit = & $git log --decorate --oneline HEAD~1..HEAD + if ($thisCommit -match 'tag:\s*(\d+(?:\.\d+)*)') { $gitTagVersion = $matches[1] } + } + } - if ($thisCommit -match 'tag:\s*(\d+(?:\.\d+)*)') { - $script:tagVersion = $matches[1] - } + It 'Is tagged with a valid version' { + $gitTagVersion | Should -Not -BeNullOrEmpty + $gitTagVersion -as [Version] | Should -Not -BeNullOrEmpty + } - $script:tagVersion | Should Not BeNullOrEmpty - $script:tagVersion -as [Version] | Should Not BeNullOrEmpty - } - } + It 'Matches manifest version' { + $manifestData.Version -as [Version] | Should -Be ( $gitTagVersion -as [Version]) } -} +} \ No newline at end of file diff --git a/Tests/Common/PSSA.Tests.ps1 b/Tests/Common/PSSA.Tests.ps1 index e29133f..2c30063 100644 --- a/Tests/Common/PSSA.Tests.ps1 +++ b/Tests/Common/PSSA.Tests.ps1 @@ -1,18 +1,24 @@ # This runs all PSScriptAnalyzer rules as Pester tests to enable visibility when publishing test results -# Vars -$ScriptAnalyzerSettingsPath = Join-Path -Path $env:BHProjectPath -ChildPath 'PSScriptAnalyzerSettings.psd1' Describe 'Testing against PSSA rules' { + Context 'PSSA Standard Rules' { - $analysis = Invoke-ScriptAnalyzer -Path $env:BHModulePath -Recurse -Settings $ScriptAnalyzerSettingsPath + + BeforeAll { + $env:BHProjectPath = Join-Path $PSScriptRoot '/../../' + $env:BHModulePath = (Get-ChildItem (Join-Path $env:BHProjectPath '*.psm1') -Recurse).Directory + + $ScriptAnalyzerSettingsPath = Join-Path -Path $env:BHProjectPath -ChildPath 'PSScriptAnalyzerSettings.psd1' + $analysis = Invoke-ScriptAnalyzer -Path $env:BHModulePath -Recurse -Settings $ScriptAnalyzerSettingsPath + } + $scriptAnalyzerRules = Get-ScriptAnalyzerRule - forEach ($rule in $scriptAnalyzerRules) { - It "Should pass $rule" { - If ($analysis.RuleName -contains $rule) { - $analysis | Where-Object RuleName -EQ $rule -OutVariable 'failures' | Out-Default - $failures.Count | Should Be 0 - } + It "Should pass <_>" -TestCases $scriptAnalyzerRules { + $rule = $_ + If ($analysis.RuleName -contains $rule) { + $analysis | Where-Object RuleName -EQ $rule -OutVariable 'failures' | Out-Default + $failures.Count | Should -Be 0 } } } diff --git a/Tests/PShipchat.Tests.ps1 b/Tests/PShipchat.Tests.ps1 index 6a47b17..c63d6fd 100644 --- a/Tests/PShipchat.Tests.ps1 +++ b/Tests/PShipchat.Tests.ps1 @@ -1,60 +1,59 @@ -$moduleName = 'PSHipchat' -$projectRoot = Resolve-Path "$PSScriptRoot\.." -$moduleRoot = Split-Path (Resolve-Path "$projectRoot\$moduleName\$moduleName.psm1") +Describe "Send-Hipchat" { -If (Get-Module $moduleName) { - Remove-Module $moduleName -Force -} - -Import-Module "$moduleRoot\$moduleName.psm1" - -Describe "send-hipchat" { - Mock Invoke-WebRequest -ModuleName $moduleName { Import-Clixml "$pwd\Tests\PSHipchat.send-hipchat.invoke-webrequest.xml" } + BeforeAll { + . $PSScriptRoot/../PSHipChat/Public/Send-Hipchat.ps1 + . $PSScriptRoot/../PSHipChat/Private/ConvertTo-Json.ps1 + Mock Invoke-WebRequest -ModuleName $moduleName { Import-Clixml "$pwd\Tests\PSHipchat.send-hipchat.invoke-webrequest.xml" } + } + It "should return true" { $params = @{ - message = "Pester test message" - room = "Test" + message = "Pester test message" + room = "Test" apitoken = "c6cS2qXSv1zRyUUXpPsu3bebVF43wx8bvPQK5vg6" } - send-hipchat @params | Should Be $true + send-hipchat @params | Should -Be $true } It "should reject the colour blue" { $params = @{ - message = "Pester test message" - room = "Test" + message = "Pester test message" + room = "Test" apitoken = "fakefalsetoken" - color = "blue" + color = "blue" } - {send-hipchat @params} | Should Throw + { send-hipchat @params } | Should -Throw } } -Describe "send-hipchat timeouts" { +Describe "Send-Hipchat timeouts" { - Mock Invoke-WebRequest -ModuleName $moduleName {Throw} + BeforeAll { + . $PSScriptRoot/../PSHipChat/Public/Send-Hipchat.ps1 + . $PSScriptRoot/../PSHipChat/Private/ConvertTo-Json.ps1 + Mock Invoke-WebRequest -ModuleName $moduleName { Throw } + } + It "should retry 3 additional times" { $params = @{ - message = "Pester test message" - room = "Test" - apitoken = "fakefalsetoken" - retry = 3 - retrysecs = 1 + message = "Pester test message" + room = "Test" + apitoken = "fakefalsetoken" + retry = 3 + retrysecs = 1 ErrorAction = "SilentlyContinue" } - send-hipchat @params | Should be $false - Assert-MockCalled Invoke-WebRequest -Exactly 4 -ModuleName $moduleName -Scope It - + send-hipchat @params | Should -Be $false + Assert-MockCalled Invoke-WebRequest -Exactly 4 -ModuleName $moduleName -Scope It } - }