SMLetsExt.tests.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#Requires -Modules Pester
<#
.SYNOPSIS
    Tests the SCSMReviewActivities module
.EXAMPLE
    Invoke-Pester
.NOTES
    This script originated from work found here: https://github.com/kmarquette/PesterInAction
    scriptanalyzer section basics taken from DSCResource.Tests
#>


Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath 'ModuleHelper.psm1') -Force

$ErrorActionPreference = 'stop'
Set-StrictMode -Version latest

$RepoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
Write-Verbose -Verbose "RepoRoot $($reporoot)"
$module = 'SMLetsExt'
$psVersion = $PSVersionTable.PSVersion
Write-Verbose -Verbose "Module $($Module)"

#region PSScriptanalyzer
if ($psVersion.Major -ge 5)
{
    Write-Verbose -Verbose "Installing PSScriptAnalyzer"
    $PSScriptAnalyzerModuleName = "PSScriptAnalyzer"
    Install-PackageProvider -Name NuGet -Force 
    Install-Module -Name $PSScriptAnalyzerModuleName -Scope CurrentUser -Force 
    $PSScriptAnalyzerModule = get-module -Name $PSScriptAnalyzerModuleName -ListAvailable
    if ($PSScriptAnalyzerModule) {
        # Import the module if it is available
        $PSScriptAnalyzerModule | Import-Module -Force
    }
    else
    {
        # Module could not/would not be installed - so warn user that tests will fail.
        Write-Warning -Message ( @(
            "The 'PSScriptAnalyzer' module is not installed. "
            "The 'PowerShell modules scriptanalyzer' Pester test will fail "
            ) -Join '' )
    }
}
else
{
    Write-Verbose -Verbose "Skipping installation of PSScriptAnalyzer since it requires PSVersion 5.0 or greater. Used PSVersion: $($PSVersion)"
}

#endregion

Describe 'Text files formatting' {

    $allTextFiles = Get-TextFilesList $RepoRoot

    Context 'Files encoding' {

        It "Doesn't use Unicode encoding" {
            $unicodeFilesCount = 0
            $allTextFiles | %{
                if (Test-FileUnicode $_) {
                    $unicodeFilesCount += 1
                    Write-Warning "File $($_.FullName) contains 0x00 bytes. It's probably uses Unicode and need to be converted to UTF-8. Use Fixer 'Get-UnicodeFilesList `$pwd | ConvertTo-UTF8'."
                }
            }
            $unicodeFilesCount | Should Be 1
        }
    }

    Context 'Indentations' {

        It 'Uses spaces for indentation, not tabs' {
            $totalTabsCount = 0
            $allTextFiles | %{
                $fileName = $_.FullName
                Get-Content $_.FullName -Raw | Select-String "`t" | % {
                    Write-Warning "There are tab in $fileName. Use Fixer 'Get-TextFilesList `$pwd | ConvertTo-SpaceIndentation'."
                    $totalTabsCount++
                }
            }
            $totalTabsCount | Should Be 0
        }
    }
}

Describe "Module: $module" -Tags Unit {
#region ScriptAnalyzer
    Context 'PSScriptAnalyzer' {
        It "passes Invoke-ScriptAnalyzer" {

            # Perform PSScriptAnalyzer scan.
            # Using ErrorAction SilentlyContinue not to cause it to fail due to parse errors caused by unresolved resources.
            # Many of our examples try to import different modules which may not be present on the machine and PSScriptAnalyzer throws parse exceptions even though examples are valid.
            # Errors will still be returned as expected.
            $PSScriptAnalyzerErrors = Invoke-ScriptAnalyzer -path $RepoRoot -Severity Error -Recurse -ErrorAction SilentlyContinue
            if ($PSScriptAnalyzerErrors -ne $null) {
                Write-Error "There are PSScriptAnalyzer errors that need to be fixed:`n $PSScriptAnalyzerErrors"
                Write-Error "For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/psscriptAnalyzer/"
                $PSScriptAnalyzerErrors.Count | Should Be $null
            }
        }     
    }
#endregion
#region Generic PS module tests
    Context "Module Configuration"{ 
        
        It "Has a root module file ($module.psm1)" {        
            
            "$RepoRoot\$module.psm1" | Should Exist
        }

        It "Is valid Powershell (Has no script errors)" {

            $contents = Get-Content -Path "$RepoRoot\$module.psm1" -ErrorAction SilentlyContinue
            $errors = $null
            $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
            $errors.Count | Should Be 0
        }

        It "Has a manifest file ($module.psd1)" {
            
            "$RepoRoot\$module.psd1" | Should Exist
        }

        It "Contains a root module path in the manifest (RootModule = '.\$module.psm1')" {
            
            "$RepoRoot\$module.psd1" | Should Exist
            "$RepoRoot\$module.psd1" | Should Contain "\.\\$module.psm1"
        }
        
        It "Is valid Powershell (Has no script errors)" {
            $contents = Get-Content -Path "$RepoRoot\$module.psm1" -ErrorAction Stop
            $errors = $null
            $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
            $errors.Count | Should Be 0
        }
    }
#endregion
#region module content
    Context 'Module loads and Functions exist' {
        
        $manifest = Test-ModuleManifest -Path "$RepoRoot\$module.psd1"
        $ExportedCommands = $manifest.ExportedCommands
        $ModuleName = $manifest.Name
        
        BeforeEach {
            if (get-module $Module) {remove-module $Module}
            import-Module "$RepoRoot\$module.psd1" -ErrorAction SilentlyContinue
            $loadedModule = Get-Module $module -ErrorAction SilentlyContinue    
            $loadedFunctions = $loadedModule.ExportedCommands.Keys
            
        }
        
        It 'Module should load without error' {
            $loadedModule.Name | Should Be $ModuleName
        }

        It 'Exported commands should include all functions' {
            $loadedFunctions | Should Be $ExportedCommands.Keys
        }
        
        AfterEach {
            
            remove-module $module
            $loadedModule = $null
            $loadedFunctions = $null
        }
    }
#endregion
#region functions
    Context 'Help provided for Functions' {
        
        BeforeAll {
            if (get-module $Module) {remove-module $Module}
            import-Module "$RepoRoot\$module.psd1" -ErrorAction SilentlyContinue
            $loadedModule = Get-Module $module -ErrorAction SilentlyContinue    
            $loadedFunctions = $loadedModule.ExportedCommands.keys
        }
        
        Foreach ($Function in $loadedFunctions) {

            $Help = Get-Help $Function

            It "$Function should have a non-default Synopsis section in help" {                
                $Help.Synopsis | Should Not Match "\r\n$Function*"
                }

            It "$Function should have help examples" {
                $Help.Examples.Example.Count | Should Not Be 0
                }
        }
        
        AfterAll {
            
            remove-module $module
            $loadedFunctions = $null
            $loadedModule = $null
        }
    }
}
#endregion