microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat-ds-agent

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/tests/linting/LintingHelpers.Tests.ps1

368lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6 Pester tests for LintingHelpers.psm1 module
7.DESCRIPTION
8 Comprehensive tests for all 3 exported functions in the LintingHelpers module:
9 - Get-ChangedFilesFromGit
10 - Get-FilesRecursive
11 - Get-GitIgnorePatterns
12#>
13
14BeforeAll {
15 $modulePath = Join-Path $PSScriptRoot '../../linting/Modules/LintingHelpers.psm1'
16 Import-Module $modulePath -Force
17}
18
19#region Get-ChangedFilesFromGit Tests
20
21Describe 'Get-ChangedFilesFromGit' {
22 Context 'Merge-base succeeds' {
23 BeforeEach {
24 # Mock git commands at module scope with proper LASTEXITCODE handling
25 $changedFiles = @('scripts/test.ps1', 'docs/readme.md', 'config/settings.json')
26
27 Mock git {
28 $global:LASTEXITCODE = 0
29 return 'abc123def456789'
30 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
31
32 Mock git {
33 $global:LASTEXITCODE = 0
34 return $changedFiles
35 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
36
37 Mock Test-Path { return $true } -ModuleName 'LintingHelpers' -ParameterFilter { $PathType -eq 'Leaf' }
38 }
39
40 It 'Returns changed files filtered by extension' {
41 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1')
42 $result | Should -Contain 'scripts/test.ps1'
43 $result | Should -Not -Contain 'docs/readme.md'
44 $result | Should -Not -Contain 'config/settings.json'
45 }
46
47 It 'Returns all files with wildcard extension' {
48 $result = Get-ChangedFilesFromGit -FileExtensions @('*')
49 $result.Count | Should -Be 3
50 }
51
52 It 'Returns files matching multiple extension patterns' {
53 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1', '*.md')
54 $result | Should -Contain 'scripts/test.ps1'
55 $result | Should -Contain 'docs/readme.md'
56 $result | Should -Not -Contain 'config/settings.json'
57 }
58
59 It 'Uses default extension pattern when not specified' {
60 $result = Get-ChangedFilesFromGit
61 $result.Count | Should -Be 3
62 }
63 }
64
65 Context 'Merge-base fails, HEAD~1 fallback' {
66 BeforeEach {
67 # Merge-base fails
68 Mock git {
69 $global:LASTEXITCODE = 128
70 return $null
71 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
72
73 # rev-parse succeeds for HEAD~1 check
74 Mock git {
75 $global:LASTEXITCODE = 0
76 return 'HEAD~1-sha'
77 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'rev-parse' }
78
79 # diff returns fallback file
80 Mock git {
81 $global:LASTEXITCODE = 0
82 return @('fallback-file.ps1')
83 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
84
85 Mock Test-Path { return $true } -ModuleName 'LintingHelpers' -ParameterFilter { $PathType -eq 'Leaf' }
86 }
87
88 It 'Falls back to HEAD~1 comparison and returns files' {
89 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1')
90 $result | Should -Contain 'fallback-file.ps1'
91 }
92 }
93
94 Context 'Empty results' {
95 BeforeEach {
96 Mock git {
97 $global:LASTEXITCODE = 0
98 return 'abc123def456789'
99 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
100
101 Mock git {
102 $global:LASTEXITCODE = 0
103 return @()
104 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
105 }
106
107 It 'Returns empty array when no files changed' {
108 $result = Get-ChangedFilesFromGit
109 $result | Should -BeNullOrEmpty
110 }
111 }
112
113 Context 'File existence filtering' {
114 BeforeEach {
115 Mock git {
116 $global:LASTEXITCODE = 0
117 return 'abc123def456789'
118 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
119
120 Mock git {
121 $global:LASTEXITCODE = 0
122 return @('exists.ps1', 'deleted.ps1')
123 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
124
125 Mock Test-Path {
126 param($Path)
127 return $Path -eq 'exists.ps1'
128 } -ModuleName 'LintingHelpers' -ParameterFilter { $PathType -eq 'Leaf' }
129 }
130
131 It 'Excludes files that no longer exist' {
132 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1')
133 $result | Should -Contain 'exists.ps1'
134 $result | Should -Not -Contain 'deleted.ps1'
135 }
136 }
137
138 Context 'Empty and whitespace file entries' {
139 BeforeEach {
140 Mock git {
141 $global:LASTEXITCODE = 0
142 return 'abc123def456789'
143 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
144
145 Mock git {
146 $global:LASTEXITCODE = 0
147 return @('valid.ps1', '', ' ', 'another.ps1')
148 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
149
150 Mock Test-Path { return $true } -ModuleName 'LintingHelpers' -ParameterFilter { $PathType -eq 'Leaf' }
151 }
152
153 It 'Filters out empty and whitespace entries' {
154 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1')
155 $result | Should -Contain 'valid.ps1'
156 $result | Should -Contain 'another.ps1'
157 $result | Should -Not -Contain ''
158 $result | Should -Not -Contain ' '
159 }
160 }
161
162 Context 'Both merge-base and HEAD~1 fail, third fallback' {
163 BeforeEach {
164 # Merge-base fails
165 Mock git {
166 $global:LASTEXITCODE = 128
167 return $null
168 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
169
170 # rev-parse fails for HEAD~1 check
171 Mock git {
172 $global:LASTEXITCODE = 128
173 return $null
174 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'rev-parse' }
175
176 # diff returns files from third fallback (git diff --name-only HEAD)
177 Mock git {
178 $global:LASTEXITCODE = 0
179 return @('unstaged-file.ps1')
180 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
181
182 Mock Test-Path { return $true } -ModuleName 'LintingHelpers' -ParameterFilter { $PathType -eq 'Leaf' }
183 }
184
185 It 'Falls back to git diff --name-only HEAD and returns files' {
186 $result = Get-ChangedFilesFromGit -FileExtensions @('*.ps1')
187 $result | Should -Contain 'unstaged-file.ps1'
188 }
189 }
190
191 Context 'Git diff command fails' {
192 BeforeEach {
193 Mock git {
194 $global:LASTEXITCODE = 0
195 return 'abc123def456789'
196 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
197
198 # Diff fails with non-zero exit code
199 Mock git {
200 $global:LASTEXITCODE = 1
201 return $null
202 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'diff' }
203 }
204
205 It 'Returns empty array when git diff fails' {
206 $result = Get-ChangedFilesFromGit
207 $result | Should -BeNullOrEmpty
208 }
209 }
210
211 Context 'Exception during execution' {
212 BeforeEach {
213 Mock git {
214 throw "Simulated git failure"
215 } -ModuleName 'LintingHelpers' -ParameterFilter { $args[0] -eq 'merge-base' }
216 }
217
218 It 'Catches exceptions and returns empty array' {
219 $result = Get-ChangedFilesFromGit
220 $result | Should -BeNullOrEmpty
221 }
222 }
223}
224
225#endregion
226
227#region Get-FilesRecursive Tests
228
229Describe 'Get-FilesRecursive' {
230 Context 'Basic file enumeration' {
231 BeforeEach {
232 New-Item -Path 'TestDrive:/scripts' -ItemType Directory -Force | Out-Null
233 New-Item -Path 'TestDrive:/scripts/test.ps1' -ItemType File -Force | Out-Null
234 New-Item -Path 'TestDrive:/scripts/readme.md' -ItemType File -Force | Out-Null
235 New-Item -Path 'TestDrive:/scripts/sub' -ItemType Directory -Force | Out-Null
236 New-Item -Path 'TestDrive:/scripts/sub/nested.ps1' -ItemType File -Force | Out-Null
237 }
238
239 It 'Finds files matching Include pattern' {
240 $result = Get-FilesRecursive -Path 'TestDrive:/scripts' -Include @('*.ps1')
241 $result.Count | Should -Be 2
242 $result.Name | Should -Contain 'test.ps1'
243 $result.Name | Should -Contain 'nested.ps1'
244 }
245
246 It 'Finds files with multiple Include patterns' {
247 $result = Get-FilesRecursive -Path 'TestDrive:/scripts' -Include @('*.ps1', '*.md')
248 $result.Count | Should -Be 3
249 }
250
251 It 'Does not include directories in results' {
252 $result = Get-FilesRecursive -Path 'TestDrive:/scripts' -Include @('*')
253 $result | ForEach-Object { $_.PSIsContainer | Should -BeFalse }
254 }
255 }
256
257 Context 'Gitignore filtering' {
258 BeforeEach {
259 New-Item -Path 'TestDrive:/project' -ItemType Directory -Force | Out-Null
260 New-Item -Path 'TestDrive:/project/src' -ItemType Directory -Force | Out-Null
261 New-Item -Path 'TestDrive:/project/src/app.ps1' -ItemType File -Force | Out-Null
262 New-Item -Path 'TestDrive:/project/node_modules' -ItemType Directory -Force | Out-Null
263 New-Item -Path 'TestDrive:/project/node_modules/pkg.ps1' -ItemType File -Force | Out-Null
264 'node_modules/' | Set-Content 'TestDrive:/project/.gitignore'
265 }
266
267 It 'Excludes files matching gitignore patterns' {
268 $result = Get-FilesRecursive -Path 'TestDrive:/project' `
269 -Include @('*.ps1') `
270 -GitIgnorePath 'TestDrive:/project/.gitignore'
271 $result.Name | Should -Contain 'app.ps1'
272 $result.Name | Should -Not -Contain 'pkg.ps1'
273 }
274
275 It 'Returns all files when gitignore path not provided' {
276 $result = Get-FilesRecursive -Path 'TestDrive:/project' -Include @('*.ps1')
277 $result.Count | Should -Be 2
278 }
279 }
280
281 Context 'Invalid paths' {
282 It 'Returns empty for non-existent path' {
283 $result = Get-FilesRecursive -Path 'TestDrive:/nonexistent' -Include @('*.ps1')
284 $result | Should -BeNullOrEmpty
285 }
286 }
287
288 Context 'No gitignore file' {
289 BeforeEach {
290 New-Item -Path 'TestDrive:/simple' -ItemType Directory -Force | Out-Null
291 New-Item -Path 'TestDrive:/simple/file.ps1' -ItemType File -Force | Out-Null
292 }
293
294 It 'Returns files when gitignore does not exist' {
295 $result = Get-FilesRecursive -Path 'TestDrive:/simple' `
296 -Include @('*.ps1') `
297 -GitIgnorePath 'TestDrive:/simple/.gitignore'
298 $result.Count | Should -Be 1
299 }
300 }
301}
302
303#endregion
304
305#region Get-GitIgnorePatterns Tests
306
307Describe 'Get-GitIgnorePatterns' {
308 Context 'Non-existent file' {
309 It 'Returns empty for non-existent file' {
310 $result = Get-GitIgnorePatterns -GitIgnorePath 'TestDrive:/nonexistent/.gitignore'
311 $result | Should -BeNullOrEmpty
312 }
313 }
314
315 Context 'Empty file' {
316 BeforeEach {
317 New-Item -Path 'TestDrive:/.gitignore-empty' -ItemType File -Force | Out-Null
318 }
319
320 It 'Returns empty for empty file' {
321 $result = Get-GitIgnorePatterns -GitIgnorePath 'TestDrive:/.gitignore-empty'
322 $result | Should -BeNullOrEmpty
323 }
324 }
325
326 Context 'Pattern parsing' {
327 It 'Skips comments and empty lines' {
328 @('# Comment', '', 'node_modules/', ' ', '*.log') | Set-Content 'TestDrive:/.gitignore'
329 $result = Get-GitIgnorePatterns -GitIgnorePath 'TestDrive:/.gitignore'
330 $result.Count | Should -Be 2
331 }
332
333 It 'Converts directory patterns correctly' {
334 $gitignorePath = Join-Path $TestDrive '.gitignore-dir'
335 'node_modules/' | Set-Content $gitignorePath
336 $result = @(Get-GitIgnorePatterns -GitIgnorePath $gitignorePath)
337 $sep = [System.IO.Path]::DirectorySeparatorChar
338 # Function wraps directory patterns with platform separator
339 $result[0] | Should -Be "*${sep}node_modules${sep}*"
340 }
341
342 It 'Converts file patterns with paths correctly' {
343 $gitignorePath = Join-Path $TestDrive '.gitignore-path'
344 'build/output.log' | Set-Content $gitignorePath
345 $result = @(Get-GitIgnorePatterns -GitIgnorePath $gitignorePath)
346 $sep = [System.IO.Path]::DirectorySeparatorChar
347 # Function normalizes paths and wraps with wildcards
348 $result[0] | Should -Be "*${sep}build${sep}output.log*"
349 }
350
351 It 'Handles simple file patterns' {
352 $gitignorePath = Join-Path $TestDrive '.gitignore-simple'
353 '*.log' | Set-Content $gitignorePath
354 $result = @(Get-GitIgnorePatterns -GitIgnorePath $gitignorePath)
355 $sep = [System.IO.Path]::DirectorySeparatorChar
356 # Function wraps simple patterns with wildcards
357 $result[0] | Should -Be "*${sep}*.log${sep}*"
358 }
359
360 It 'Processes multiple patterns' {
361 @('node_modules/', 'dist/', '*.tmp', 'logs/debug.log') | Set-Content 'TestDrive:/.gitignore-multi'
362 $result = Get-GitIgnorePatterns -GitIgnorePath 'TestDrive:/.gitignore-multi'
363 $result.Count | Should -Be 4
364 }
365 }
366}
367
368#endregion
369