microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
hve-core-v2.3.7

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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