Skip to content

Commit

Permalink
Merge pull request #335 from fflaten/update-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
f-bader authored Jul 14, 2024
2 parents 53e2308 + bbbd2b8 commit 765db76
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 210 deletions.
1 change: 1 addition & 0 deletions powershell/public/Clear-MtDnsCache.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#>
function Clear-MtDnsCache {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification='Setting module level variable')]
[CmdletBinding()]
param()

Write-Verbose -Message "Clearing the results cached from DNS lookups in this session"
Expand Down
1 change: 1 addition & 0 deletions powershell/public/Clear-MtGraphCache.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#>
function Clear-MtGraphCache {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification='Setting module level variable')]
[CmdletBinding()]
param()

Write-Verbose -Message "Clearing the results cached from Graph API calls in this session"
Expand Down
3 changes: 2 additions & 1 deletion powershell/public/Invoke-Maester.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Shows results of tests as they are run including details on failed tests.
.EXAMPLE
```
$configuration = [PesterConfiguration]::Default
$configuration = New-PesterConfiguration
$configuration.Run.Path = './tests/Maester'
$configuration.Filter.Tag = 'CA'
$configuration.Filter.ExcludeTag = 'App'
Expand All @@ -75,6 +75,7 @@ Runs all the Pester tests in the EIDSCA folder.
Function Invoke-Maester {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Colors are beautiful')]
[Alias("Invoke-MtMaester")]
[CmdletBinding()]
param (
# Specifies one or more paths to files containing tests. The value is a path\file name or name pattern. Wildcards are permitted.
[Parameter(Position = 0)]
Expand Down
73 changes: 33 additions & 40 deletions powershell/tests/functions/Help.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,74 +1,67 @@
BeforeAll {
BeforeDiscovery {
$module = 'Maester'
$moduleRoot = (Resolve-Path "$global:testroot/..").Path
}

BeforeDiscovery {

$moduleRoot = (Resolve-Path "$global:testroot/..").Path
$moduleRoot = "$PSScriptRoot/../.."
# Get all the functions in the /public folder
$functions = Get-ChildItem -Path "$moduleRoot/public" -Filter '*.ps1' | ForEach-Object { $_.BaseName }

# Eventually this should include all functions in the /public folder
# For now, just the ones that we have tested and added
$functionsWithTests = ('Invoke-MtMaester'
)
$functionsWithTests = @('Invoke-MtMaester')
}

Describe -Tags ('Unit', 'Acceptance') "$module Module Tests" {

Context "Test Function" -ForEach $functions {

It "$_.ps1 should exist" {
"$moduleRoot/public/$_.ps1" | Should -Exist
Describe "$module Help Tests" -Tags ('Unit', 'Acceptance') -ForEach @{ moduleRoot = $moduleRoot } {
Context 'Function <_>' -ForEach $functions {
BeforeAll {
$function = $_
$functionPath = Join-Path -Path $moduleRoot -ChildPath "/public/$function.ps1"
}

It "$_.ps1 should have help block" {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch '<#'
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch '#>'
It "<function>.ps1 should exist" {
Join-Path -Path $moduleRoot -ChildPath "/public/$function.ps1"
$functionPath | Should -Exist
}

It "$_.ps1 should have a SYNOPSIS section in the help block" {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch '.SYNOPSIS'
It "<function>.ps1 should have help block" {
$functionPath | Should -FileContentMatch '<#'
$functionPath | Should -FileContentMatch '#>'
}

It "$_.ps1 should have a DESCRIPTION section in the help block" {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch '.DESCRIPTION'
It "<function>.ps1 should have a SYNOPSIS section in the help block" {
$functionPath | Should -FileContentMatch '.SYNOPSIS'
}

It "$_.ps1 should have a EXAMPLE section in the help block" {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch '.EXAMPLE'
It "<function>.ps1 should have a DESCRIPTION section in the help block" {
$functionPath | Should -FileContentMatch '.DESCRIPTION'
}

It "$_.ps1 should be an advanced function" {
foreach ($_ in $_s) {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch 'function'
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch 'cmdletbinding'
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch 'param'
}
It "<function>.ps1 should have a EXAMPLE section in the help block" {
$functionPath | Should -FileContentMatch '.EXAMPLE'
}

It "<function>.ps1 should be an advanced function" {
$functionPath | Should -FileContentMatch 'function'
$functionPath | Should -FileContentMatch 'cmdletbinding'
$functionPath | Should -FileContentMatch 'param'
}

It "$_.ps1 should contain Write-Verbose blocks" {
"$moduleRoot/public/$_.ps1" | Should -FileContentMatch 'Write-Verbose'
It "<function>.ps1 should contain Write-Verbose blocks" {
$functionPath | Should -FileContentMatch 'Write-Verbose'
}

It "$_.ps1 is valid PowerShell code" {
$psFile = Get-Content -Path "$moduleRoot/public/$_.ps1" `
-ErrorAction Stop
It "<function>.ps1 is valid PowerShell code" {
$psFile = Get-Content -Path $functionPath -ErrorAction Stop
$errors = $null
$null = [System.Management.Automation.PSParser]::Tokenize($psFile, [ref]$errors)
$errors.Count | Should -Be 0
}

It "$_ should run without exceptions" {
$scriptBlock = [scriptblock]::Create((Get-Content "$moduleRoot/public/$_.ps1" -Raw))
It '<function>.ps1 should run without exceptions' {
$scriptBlock = [scriptblock]::Create((Get-Content $functionPath -Raw))
{ & $scriptBlock } | Should -Not -Throw
}
}

Context "Test Function" -ForEach $functionsWithTests {
It "($_.Tests.ps1) should exist" {
# Intentionally using skip so the output will remind us of the missing test files :)
It 'Matching test file file should exist' -Skip:$($_ -notin $functionsWithTests) {
"$moduleRoot/tests/functions/$($_).Tests.ps1" | Should -Exist
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
Describe 'Invoke-MtMaester' {
Describe 'Invoke-Maester' {
It 'Not connected to graph should return error' {

if (Get-MgContext) { Disconnect-Graph } # Ensure we are disconnected

{ Invoke-MtMaester } | Should -Throw "Not connected to Microsoft Graph.*"
{ Invoke-Maester } | Should -Throw 'Not connected to Microsoft Graph.*'
}
}

4 changes: 2 additions & 2 deletions powershell/tests/general/Help.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ Param (
$SkipTest,

[string[]]
$CommandPath = @("$global:testroot\..\public", "$global:testroot\..\internal"),
$CommandPath = @("$PSScriptRoot\..\..\public", "$PSScriptRoot\..\..\internal"),

[string]
$ModuleName = "PSFramework",

[string]
$ExceptionsFile = "$global:testroot\general\Help.Exceptions.ps1"
$ExceptionsFile = "$PSScriptRoot\..\general\Help.Exceptions.ps1"
)
if ($SkipTest) { return }
. $ExceptionsFile
Expand Down
102 changes: 55 additions & 47 deletions powershell/tests/general/Manifest.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,48 +1,56 @@
Describe "Validating the module manifest" {
$moduleRoot = (Resolve-Path "$global:testroot\..").Path
$manifest = ((Get-Content "$moduleRoot\Maester.psd1") -join "`n") | Invoke-Expression
Context "Basic resources validation" {
$files = Get-ChildItem "$moduleRoot\public" -Recurse -File | Where-Object Name -like "*.ps1"
It "Exports all functions in the public folder" -TestCases @{ files = $files; manifest = $manifest } {

$functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '<=').InputObject
$functions | Should -BeNullOrEmpty
}
It "Exports no function that isn't also present in the public folder" -TestCases @{ files = $files; manifest = $manifest } {
$functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '=>').InputObject
$functions | Should -BeNullOrEmpty
}

It "Exports none of its internal functions" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } {
$files = Get-ChildItem "$moduleRoot/internal" -Recurse -File -Filter "*.ps1"
$files | Where-Object BaseName -In $manifest.FunctionsToExport | Should -BeNullOrEmpty
}
}

Context "Individual file validation" {
It "The root module file exists" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } {
Test-Path "$moduleRoot\$($manifest.RootModule)" | Should -Be $true
}

foreach ($format in $manifest.FormatsToProcess)
{
It "The file $format should exist" -TestCases @{ moduleRoot = $moduleRoot; format = $format } {
Test-Path "$moduleRoot\$format" | Should -Be $true
}
}

foreach ($type in $manifest.TypesToProcess)
{
It "The file $type should exist" -TestCases @{ moduleRoot = $moduleRoot; type = $type } {
Test-Path "$moduleRoot\$type" | Should -Be $true
}
}

foreach ($tag in $manifest.PrivateData.PSData.Tags)
{
It "Tags should have no spaces in name" -TestCases @{ tag = $tag } {
$tag -match " " | Should -Be $false
}
}
}
BeforeDiscovery {
$moduleRoot = "$PSScriptRoot/../.."
# Using Import-PowerShellDataFile over Test-ModuleManifest as it's easier to navigate
$manifest = Import-PowerShellDataFile -Path (Join-Path -Path $moduleRoot -ChildPath 'Maester.psd1')
}

Describe 'Validating the module manifest' -ForEach @{ moduleRoot = $moduleRoot; manifest = $manifest } {
Context 'Basic resources validation' {
BeforeAll {
$files = Get-ChildItem -Path "$moduleRoot/public" -Recurse -File -Filter '*.ps1'
}

It 'Manifest is valid' {
Test-ModuleManifest -Path (Join-Path -Path $moduleRoot -ChildPath 'Maester.psd1')
# Throws if not valid = failure. Success if not.
}

It 'Exports all functions in the public folder' {
$functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '<=').InputObject
$functions | Should -BeNullOrEmpty
}
It "Exports no function that isn't also present in the public folder" {
$functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '=>').InputObject
$functions | Should -BeNullOrEmpty
}

It 'Exports none of its internal functions' {
$files = Get-ChildItem "$moduleRoot/internal" -Recurse -File -Filter '*.ps1'
$files | Where-Object BaseName -In $manifest.FunctionsToExport | Should -BeNullOrEmpty
}
}

Context 'Testing tags' {
It "Tag '<_>' should not include whitespace" -ForEach @($manifest.PrivateData.PSData.Tags) {
$_ | Should -Not -Match '\s'
}
}

Context 'Individual file validation' {
It 'The root module file exists' {
Join-Path -Path $moduleRoot -ChildPath $manifest.RootModule | Should -Exist
}

Context 'Testing format files' -Skip:$(-not $manifest.ContainsKey('FormatsToProcess')) {
It 'The file <_> should exist' -ForEach $manifest.FormatsToProcess {
Join-Path -Path $moduleRoot -ChildPath $_ | Should -Exist
}
}

Context 'Testing types files' -Skip:$(-not $manifest.ContainsKey('TypesToProcess')) {
It 'The file <_> should exist' -ForEach $manifest.TypesToProcess {
Join-Path -Path $moduleRoot -ChildPath $_ | Should -Exist
}
}
}
}
20 changes: 9 additions & 11 deletions powershell/tests/general/Module.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
BeforeAll {
$module = 'Maester'
$moduleRoot = (Resolve-Path "$global:testroot/..").Path
$moduleRoot = "$PSScriptRoot/../.."
}

Describe -Tags ('Unit', 'Acceptance') "$module Module Tests" {

Describe "<module> Module Tests" -Tags ('Unit', 'Acceptance') {
Context 'Module Setup' {
It "has the root module $module.psm1" {
"$moduleRoot/$module.psm1" | Should -Exist
Join-Path -Path $moduleRoot -ChildPath "$module.psm1" | Should -Exist
}

It "has the a manifest file of $module.psd1" {
"$moduleRoot/$module.psd1" | Should -Exist
"$moduleRoot/$module.psd1" | Should -FileContentMatch "$module.psm1"
Join-Path -Path $moduleRoot -ChildPath "$module.psd1" | Should -Exist
Join-Path -Path $moduleRoot -ChildPath "$module.psd1" | Should -FileContentMatch "$module.psm1"
}

It "$module folder has functions" {
"$moduleRoot/public/*.ps1" | Should -Exist
It '<module> folder has functions' {
Join-Path -Path $moduleRoot -ChildPath "public/*.ps1" | Should -Exist
}

It "$module is valid PowerShell code" {
$psFile = Get-Content -Path "$moduleRoot\$module.psm1" `
-ErrorAction Stop
It '<module> is valid PowerShell code' {
$psFile = Get-Content -Path (Join-Path -Path $moduleRoot -ChildPath "$module.psm1") -ErrorAction Stop
$errors = $null
$null = [System.Management.Automation.PSParser]::Tokenize($psFile, [ref]$errors)
$errors.Count | Should -Be 0
Expand Down
73 changes: 42 additions & 31 deletions powershell/tests/general/PSScriptAnalyzer.Tests.ps1
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
[CmdletBinding()]
Param (
[switch]
$SkipTest,
Param (
[switch]
$SkipTest,

[string[]]
$CommandPath = @("$global:testroot\..\public", "$global:testroot\..\internal")
[string[]]
$CommandPath = @("$PSScriptRoot/../../public/", "$PSScriptRoot/../../internal/")
)

if ($SkipTest) { return }

$global:__pester_data.ScriptAnalyzer = New-Object System.Collections.ArrayList
BeforeDiscovery {
$commandFiles = Get-ChildItem -Path $CommandPath -Recurse -File -Filter '*.ps1'
$scriptAnalyzerRules = Get-ScriptAnalyzerRule
}

Describe 'Invoking PSScriptAnalyzer against commandbase' {
$commandFiles = foreach ($path in $CommandPath) { Get-ChildItem -Path $path -Recurse | Where-Object Name -like "*.ps1" }
$scriptAnalyzerRules = Get-ScriptAnalyzerRule
Describe 'Invoking PSScriptAnalyzer against commandbase' -ForEach @{ commandFiles = $commandFiles } {
BeforeAll {
$analysis = $commandFiles | Invoke-ScriptAnalyzer -ExcludeRule PSAvoidTrailingWhitespace, PSShouldProcess
}

foreach ($file in $commandFiles)
{
Context "Analyzing $($file.BaseName)" {
$analysis = Invoke-ScriptAnalyzer -Path $file.FullName -ExcludeRule PSAvoidTrailingWhitespace, PSShouldProcess
# The next Context blocks are kinda duplicate, but helps us document both
# which files and which rules where evaluated without running every rule for every file
Context 'Analyzing rule <_.RuleName>' -ForEach $scriptAnalyzerRules {
BeforeAll {
$rule = $_
}
It 'All files should be compliant' {
$failedFiles = foreach ($failure in $analysis) {
if ($failure.RuleName -eq $rule.RuleName) {
$failure.ScriptPath
}
}
$failedFiles | Should -BeNullOrEmpty
}
}

forEach ($rule in $scriptAnalyzerRules)
{
It "Should pass $rule" -TestCases @{ analysis = $analysis; rule = $rule } {
If ($analysis.RuleName -contains $rule)
{
$analysis | Where-Object RuleName -EQ $rule -outvariable failures | ForEach-Object { $null = $global:__pester_data.ScriptAnalyzer.Add($_) }

1 | Should -Be 0
}
else
{
0 | Should -Be 0
}
}
}
}
}
Context 'Analyzing file <_.BaseName>' -ForEach $commandFiles {
BeforeAll {
$file = $_
}
It "Should pass all rules" -Tag 'ScriptAnalyzerRule' {
$failedRules = foreach ($failure in $analysis) {
if ($failure.ScriptPath -eq $file.FullName) {
$failure
}
}
$failedRules # Intentional output so we can get it from StandardOutput-property in pester.ps1
@($failedRules).RuleName | Should -BeNullOrEmpty
}
}
}
Loading

0 comments on commit 765db76

Please sign in to comment.