tests/InModule.Help.Tests.ps1

Param (
    [switch]$Batch
)

$commandName = $MyInvocation.MyCommand.Name.Replace(".Tests.ps1", "")
$here = if ($PSScriptRoot) { $PSScriptRoot } else {    (Get-Item . ).FullName }

if (!$Batch) {
    # Is not a part of the global batch => import module
    #Explicitly import the module for testing
    Import-Module "$here\..\dbops.psd1" -Force; Get-DBOModuleFileList -Type internal | ForEach-Object { . $_.FullName }
}
else {
    # Is a part of a batch, output some eye-catching happiness
    Write-Host "Running $commandName tests" -ForegroundColor Cyan
}
if ($SkipHelpTest) { return }
$includedNames = (Get-ChildItem "$here\..\functions" | Where-Object Name -like "*.ps1" ).BaseName
$commands = Get-Command -Module (Get-Module dbops) -CommandType Cmdlet, Function, Workflow | Where-Object Name -in $includedNames

## 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

    # Skip all functions that are on the exclusions list
    if ($global:FunctionHelpTestExceptions -contains $commandName) { continue }

    # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets
    $Help = Get-Help $commandName -ErrorAction SilentlyContinue
    $testhelperrors = 0
    $testhelpall = 0
    Describe "Test help for $commandName" {

        $testhelpall += 1
        if ($Help.Synopsis -like '*`[`<CommonParameters`>`]*') {
            # 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 '*`[`<CommonParameters`>`]*'
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty($Help.Description.Text)) {
            # Should be a description for every function
            It "gets description for $commandName" {
                $Help.Description | Should Not BeNullOrEmpty
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty(($Help.Examples.Example | Select-Object -First 1).Code)) {
            # Should be at least one example
            It "gets example code from $commandName" {
                ($Help.Examples.Example | Select-Object -First 1).Code | Should Not BeNullOrEmpty
            }
            $testhelperrors += 1
        }

        $testhelpall += 1
        if ([String]::IsNullOrEmpty(($Help.Examples.Example.Remarks | Select-Object -First 1).Text)) {
            # 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
            }
            $testhelperrors += 1
        }

        if ($testhelperrors -eq 0) {
            It "Ran silently $testhelpall tests" {
                $testhelperrors | Should be 0
            }
        }

        $testparamsall = 0
        $testparamserrors = 0
        Context "Test parameter help for $commandName" {

            $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable',
            'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'

            $parameterSets = $command.ParameterSets | Sort-Object -Property Name -Unique | Where-Object Parameters.Name -notin $common
            foreach ($parameterSet in $parameterSets) {
                $parameters = $parameterSet.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common
                $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique
                foreach ($parameter in $parameters) {
                    $parameterName = $parameter.Name
                    $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName

                    $testparamsall += 1
                    if ([String]::IsNullOrEmpty($parameterHelp.Description.Text)) {
                        # Should be a description for every parameter
                        It "gets help for parameter: $parameterName : in $commandName" {
                            $parameterHelp.Description.Text | Should Not BeNullOrEmpty
                        }
                        $testparamserrors += 1
                    }

                    $testparamsall += 1
                    if ($parameterSet.IsDefault) {
                        #This test only valid for the default parameter set, but might fail on others if mandatory settings are different
                        $codeMandatory = $parameter.IsMandatory.toString()
                        if ($parameterHelp.Required -ne $codeMandatory) {
                            # Required value in Help should match IsMandatory property of parameter
                            It "help for $parameterName parameter in $commandName has correct Mandatory value" {
                                $parameterHelp.Required | Should Be $codeMandatory
                            }
                            $testparamserrors += 1
                        }
                    }

                    #if ($HelpTestSkipParameterType[$commandName] -contains $parameterName) { continue }

                    $codeType = $parameter.ParameterType.Name

                    $testparamsall += 1
                    if ($parameter.ParameterType.IsEnum) {
                        # Enumerations often have issues with the typename not being reliably available
                        $names = $parameter.ParameterType::GetNames($parameter.ParameterType)
                        if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) {
                            # Parameter type in Help should match code
                            It "help for $commandName has correct parameter type for $parameterName" {
                                $parameterHelp.parameterValueGroup.parameterValue | Should be $names
                            }
                            $testparamserrors += 1
                        }
                    }
                    #removing the [] in the end to properly identify enums
                    elseif ((Invoke-Expression "[$($parameter.ParameterType.FullName.Trim('[]'))]").IsEnum) {
                        # Enumerations often have issues with the typename not being reliably available
                        $names = [Enum]::GetNames($parameter.ParameterType.DeclaredMembers[0].ReturnType)
                        if ($parameterHelp.parameterValueGroup.parameterValue -ne $names) {
                            # Parameter type in Help should match code
                            It "help for $commandName has correct parameter type for $parameterName" {
                                $parameterHelp.parameterValueGroup.parameterValue | Should be $names
                            }
                            $testparamserrors += 1
                        }
                    }
                    else {
                        # To avoid calling Trim method on a null object.
                        $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() }
                        if ($helpType -ne $codeType ) {
                            # Parameter type in Help should match code
                            It "help for $commandName has correct parameter type for $parameterName" {
                                $helpType | Should be $codeType
                            }
                            $testparamserrors += 1
                        }
                    }
                }
            }
            foreach ($helpParm in $HelpParameterNames) {
                $testparamsall += 1
                if ($helpParm -notin $parameterSets.Parameters.Name) {
                    # Shouldn't find extra parameters in help.
                    It "finds help parameter in code: $helpParm" {
                        $helpParm -in $parameterSets.Parameters.Name | Should Be $true
                    }
                    $testparamserrors += 1
                }
            }
            if ($testparamserrors -eq 0) {
                It "Ran silently $testparamsall tests" {
                    $testparamserrors | Should be 0
                }
            }
        }
    }
}