microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
docs/227-add-governance

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

327lines · modecode

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