microsoft/hve-core

Public

mirrored from https://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2b4d1232f1fef5f2c858ccec23582bfed93db47f

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/tests/linting/Invoke-PSScriptAnalyzer.Tests.ps1

332lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6 Pester tests for Invoke-PSScriptAnalyzer.ps1 script
7.DESCRIPTION
8 Tests for PSScriptAnalyzer wrapper script:
9 - Parameter validation
10 - Module availability checks
11 - ChangedFilesOnly filtering
12 - CI integration
13#>
14
15BeforeAll {
16 $script:ScriptPath = Join-Path $PSScriptRoot '../../linting/Invoke-PSScriptAnalyzer.ps1'
17 $script:ModulePath = Join-Path $PSScriptRoot '../../linting/Modules/LintingHelpers.psm1'
18 $script:CIHelpersPath = Join-Path $PSScriptRoot '../../lib/Modules/CIHelpers.psm1'
19
20 # Import modules for mocking
21 Import-Module $script:ModulePath -Force
22 Import-Module $script:CIHelpersPath -Force
23}
24
25AfterAll {
26 Remove-Module LintingHelpers -Force -ErrorAction SilentlyContinue
27 Remove-Module CIHelpers -Force -ErrorAction SilentlyContinue
28}
29
30#region Parameter Validation Tests
31
32Describe 'Invoke-PSScriptAnalyzer Parameter Validation' -Tag 'Unit' {
33 Context 'ChangedFilesOnly parameter' {
34 BeforeEach {
35 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
36 Mock Invoke-ScriptAnalyzer { @() }
37 Mock Get-ChangedFilesFromGit { @('script.ps1') }
38 Mock Get-FilesRecursive { @() }
39 Mock Set-CIOutput {}
40 Mock Set-CIEnv {}
41 Mock Write-CIStepSummary {}
42 Mock Write-CIAnnotation {}
43 }
44
45 It 'Accepts ChangedFilesOnly switch' {
46 { & $script:ScriptPath -ChangedFilesOnly } | Should -Not -Throw
47 }
48
49 It 'Accepts BaseBranch with ChangedFilesOnly' {
50 { & $script:ScriptPath -ChangedFilesOnly -BaseBranch 'develop' } | Should -Not -Throw
51 }
52 }
53
54 Context 'ConfigPath parameter' {
55 BeforeEach {
56 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
57 Mock Invoke-ScriptAnalyzer { @() }
58 Mock Get-FilesRecursive { @() }
59 Mock Set-CIOutput {}
60 Mock Set-CIEnv {}
61 Mock Write-CIStepSummary {}
62 Mock Write-CIAnnotation {}
63 }
64
65 It 'Uses default config path when not specified' {
66 # Script defaults to scripts/linting/PSScriptAnalyzer.psd1
67 { & $script:ScriptPath } | Should -Not -Throw
68 }
69
70 It 'Accepts custom config path' {
71 $configPath = Join-Path $PSScriptRoot '../../linting/PSScriptAnalyzer.psd1'
72 { & $script:ScriptPath -ConfigPath $configPath } | Should -Not -Throw
73 }
74 }
75
76 Context 'OutputPath parameter' {
77 BeforeEach {
78 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
79 Mock Invoke-ScriptAnalyzer { @() }
80 Mock Get-FilesRecursive { @() }
81 Mock Set-CIOutput {}
82 Mock Set-CIEnv {}
83 Mock Write-CIStepSummary {}
84 Mock Write-CIAnnotation {}
85 }
86
87 It 'Accepts custom output path' {
88 $outputPath = Join-Path ([System.IO.Path]::GetTempPath()) 'test-output.json'
89 { & $script:ScriptPath -OutputPath $outputPath } | Should -Not -Throw
90 }
91 }
92}
93
94#endregion
95
96#region Module Availability Tests
97
98Describe 'PSScriptAnalyzer Module Availability' -Tag 'Unit' {
99 Context 'Module not installed' {
100 BeforeEach {
101 Mock Get-Module { $null } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
102 Mock Install-Module {} -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
103 Mock Import-Module { throw 'Module not found' } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
104 Mock Write-Error {}
105 }
106
107 It 'Reports error when module unavailable' {
108 { & $script:ScriptPath } | Should -Throw
109 }
110 }
111
112 Context 'Module installed' {
113 BeforeEach {
114 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
115 Mock Invoke-ScriptAnalyzer { @() }
116 Mock Get-FilesRecursive { @() }
117 Mock Set-CIOutput {}
118 Mock Set-CIEnv {}
119 Mock Write-CIStepSummary {}
120 Mock Write-CIAnnotation {}
121 }
122
123 It 'Proceeds when module available' {
124 { & $script:ScriptPath } | Should -Not -Throw
125 }
126 }
127}
128
129#endregion
130
131#region File Discovery Tests
132
133Describe 'File Discovery' -Tag 'Unit' {
134 Context 'All files mode' {
135 BeforeEach {
136 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
137 Mock Invoke-ScriptAnalyzer { @() }
138 Mock Set-CIOutput {}
139 Mock Set-CIEnv {}
140 Mock Write-CIStepSummary {}
141 Mock Write-CIAnnotation {}
142 }
143
144 It 'Uses Get-FilesRecursive for all files' {
145 Mock Get-FilesRecursive {
146 return @('script1.ps1', 'script2.ps1')
147 }
148
149 & $script:ScriptPath
150 Should -Invoke Get-FilesRecursive -Times 1
151 }
152 }
153
154 Context 'Changed files only mode' {
155 BeforeEach {
156 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
157 Mock Invoke-ScriptAnalyzer { @() }
158 Mock Get-FilesRecursive { @() }
159 Mock Set-CIOutput {}
160 Mock Set-CIEnv {}
161 Mock Write-CIStepSummary {}
162 Mock Write-CIAnnotation {}
163 }
164
165 It 'Uses Get-ChangedFilesFromGit when ChangedFilesOnly specified' {
166 Mock Get-ChangedFilesFromGit {
167 return @('changed.ps1')
168 }
169
170 & $script:ScriptPath -ChangedFilesOnly
171 Should -Invoke Get-ChangedFilesFromGit -Times 1
172 }
173
174 It 'Passes BaseBranch to Get-ChangedFilesFromGit' {
175 Mock Get-ChangedFilesFromGit {
176 return @('changed.ps1')
177 }
178
179 & $script:ScriptPath -ChangedFilesOnly -BaseBranch 'develop'
180 Should -Invoke Get-ChangedFilesFromGit -Times 1 -ParameterFilter {
181 $BaseBranch -eq 'develop'
182 }
183 }
184 }
185}
186
187#endregion
188
189#region CI Integration Tests
190
191Describe 'CI Integration' -Tag 'Unit' {
192 Context 'Write-CIAnnotation calls' {
193 BeforeEach {
194 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
195 Mock Get-FilesRecursive { @('test.ps1') }
196 Mock Set-CIOutput {}
197 Mock Set-CIEnv {}
198 Mock Write-CIStepSummary {}
199 Mock Write-CIAnnotation {}
200 }
201
202 It 'Calls Write-CIAnnotation for each issue' {
203 Mock Invoke-ScriptAnalyzer {
204 return @(
205 [PSCustomObject]@{
206 ScriptPath = 'test.ps1'
207 Line = 10
208 Column = 5
209 RuleName = 'PSAvoidUsingInvokeExpression'
210 Severity = 'Warning'
211 Message = 'Avoid using Invoke-Expression'
212 }
213 )
214 }
215
216 & $script:ScriptPath
217 Should -Invoke Write-CIAnnotation -Times 1
218 }
219
220 It 'Sets CI output for file count' {
221 Mock Invoke-ScriptAnalyzer { @() }
222
223 & $script:ScriptPath
224 Should -Invoke Set-CIOutput -Times 1 -ParameterFilter {
225 $Name -eq 'count'
226 }
227 }
228 }
229}
230
231#endregion
232
233#region Output Tests
234
235Describe 'Output Generation' -Tag 'Unit' {
236 BeforeAll {
237 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
238 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
239 }
240
241 AfterAll {
242 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
243 }
244
245 Context 'JSON output file' {
246 BeforeEach {
247 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
248 Mock Get-FilesRecursive { @('test.ps1') }
249 Mock Set-CIOutput {}
250 Mock Set-CIEnv {}
251 Mock Write-CIStepSummary {}
252 Mock Write-CIAnnotation {}
253
254 Mock Invoke-ScriptAnalyzer {
255 return @(
256 [PSCustomObject]@{
257 ScriptPath = 'test.ps1'
258 Line = 10
259 Column = 5
260 RuleName = 'TestRule'
261 Severity = 'Warning'
262 Message = 'Test message'
263 }
264 )
265 }
266
267 $script:OutputFile = Join-Path $script:TempDir 'output.json'
268 }
269
270 It 'Creates JSON output file' {
271 & $script:ScriptPath -OutputPath $script:OutputFile
272 Test-Path $script:OutputFile | Should -BeTrue
273 }
274
275 It 'Output file contains valid JSON' {
276 & $script:ScriptPath -OutputPath $script:OutputFile
277 { Get-Content $script:OutputFile | ConvertFrom-Json } | Should -Not -Throw
278 }
279 }
280}
281
282#endregion
283
284#region Exit Code Tests
285
286Describe 'Exit Code Handling' -Tag 'Unit' {
287 Context 'No issues found' {
288 BeforeEach {
289 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
290 Mock Get-FilesRecursive { @() }
291 Mock Set-CIOutput {}
292 Mock Set-CIEnv {}
293 Mock Write-CIStepSummary {}
294 Mock Write-CIAnnotation {}
295 Mock Invoke-ScriptAnalyzer { @() }
296 }
297
298 It 'Returns success when no issues' {
299 { & $script:ScriptPath } | Should -Not -Throw
300 }
301 }
302
303 Context 'Issues found' {
304 BeforeEach {
305 Mock Get-Module { $true } -ParameterFilter { $Name -eq 'PSScriptAnalyzer' }
306 Mock Get-FilesRecursive { @('test.ps1') }
307 Mock Set-CIOutput {}
308 Mock Set-CIEnv {}
309 Mock Write-CIStepSummary {}
310 Mock Write-CIAnnotation {}
311
312 Mock Invoke-ScriptAnalyzer {
313 return @(
314 [PSCustomObject]@{
315 ScriptPath = 'test.ps1'
316 Severity = 'Error'
317 RuleName = 'TestRule'
318 Message = 'Error found'
319 Line = 1
320 Column = 1
321 }
322 )
323 }
324 }
325
326 It 'Script completes with issues in output' {
327 { & $script:ScriptPath } | Should -Not -Throw
328 }
329 }
330}
331
332#endregion