microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/add-pester-code-coverage

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/tests/linting/Link-Lang-Check.Tests.ps1

492lines · modecode

1#Requires -Modules Pester
2<#
3.SYNOPSIS
4 Pester tests for Link-Lang-Check.ps1 script
5.DESCRIPTION
6 Tests for language path link checker functions:
7 - Get-GitTextFile
8 - Find-LinksInFile
9 - Repair-LinksInFile
10 - Repair-AllLink
11 - ConvertTo-JsonOutput
12#>
13
14BeforeAll {
15 # Extract functions from script using AST
16 $scriptPath = Join-Path $PSScriptRoot '../../linting/Link-Lang-Check.ps1'
17 $scriptContent = Get-Content -Path $scriptPath -Raw
18 $ast = [System.Management.Automation.Language.Parser]::ParseInput($scriptContent, [ref]$null, [ref]$null)
19 $functions = $ast.FindAll({ param($node) $node -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)
20
21 foreach ($func in $functions) {
22 . ([scriptblock]::Create($func.Extent.Text))
23 }
24
25 $script:FixtureDir = Join-Path $PSScriptRoot '../Fixtures/Linting'
26}
27
28#region Get-GitTextFile Tests
29
30Describe 'Get-GitTextFile' -Tag 'Unit' {
31 Context 'Git command succeeds' {
32 BeforeEach {
33 Mock git {
34 $global:LASTEXITCODE = 0
35 return @('file1.md', 'file2.ps1', 'subdir/file3.txt')
36 } -ParameterFilter { $args -contains '--name-only' }
37 }
38
39 It 'Returns array of file paths' {
40 $result = Get-GitTextFile
41 $result | Should -BeOfType [string]
42 $result.Count | Should -Be 3
43 }
44
45 It 'Includes all returned files' {
46 $result = Get-GitTextFile
47 $result | Should -Contain 'file1.md'
48 $result | Should -Contain 'file2.ps1'
49 $result | Should -Contain 'subdir/file3.txt'
50 }
51 }
52
53 Context 'Git command fails' {
54 BeforeEach {
55 Mock git {
56 $global:LASTEXITCODE = 128
57 return 'fatal: not a git repository'
58 } -ParameterFilter { $args -contains '--name-only' }
59
60 Mock Write-Error {}
61 }
62
63 It 'Returns empty array on git error' {
64 $result = Get-GitTextFile
65 $result | Should -BeNullOrEmpty
66 }
67 }
68
69 Context 'Empty repository' {
70 BeforeEach {
71 Mock git {
72 $global:LASTEXITCODE = 0
73 return @()
74 } -ParameterFilter { $args -contains '--name-only' }
75 }
76
77 It 'Returns empty array for empty repo' {
78 $result = Get-GitTextFile
79 $result | Should -BeNullOrEmpty
80 }
81 }
82}
83
84#endregion
85
86#region Find-LinksInFile Tests
87
88Describe 'Find-LinksInFile' -Tag 'Unit' {
89 BeforeAll {
90 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
91 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
92 }
93
94 AfterAll {
95 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
96 }
97
98 Context 'File with en-us links' {
99 BeforeEach {
100 $script:TestFile = Join-Path $script:TempDir 'test-links.md'
101 @'
102# Test Document
103
104Visit https://docs.microsoft.com/en-us/azure for Azure docs.
105Also see https://learn.microsoft.com/en-us/dotnet/api for .NET API.
106'@ | Set-Content -Path $script:TestFile
107 }
108
109 It 'Finds all en-us links' {
110 $result = Find-LinksInFile -FilePath $script:TestFile
111 $result.Count | Should -Be 2
112 }
113
114 It 'Returns correct file path' {
115 $result = Find-LinksInFile -FilePath $script:TestFile
116 $result[0].File | Should -Be $script:TestFile
117 }
118
119 It 'Returns correct line numbers' {
120 $result = Find-LinksInFile -FilePath $script:TestFile
121 $result[0].LineNumber | Should -Be 3
122 $result[1].LineNumber | Should -Be 4
123 }
124
125 It 'Provides fixed URL without en-us' {
126 $result = Find-LinksInFile -FilePath $script:TestFile
127 $result[0].FixedUrl | Should -Not -Match 'en-us/'
128 $result[0].FixedUrl | Should -Be 'https://docs.microsoft.com/azure'
129 }
130 }
131
132 Context 'File without en-us links' {
133 BeforeEach {
134 $script:CleanFile = Join-Path $script:TempDir 'clean-links.md'
135 @'
136# Clean Document
137
138Visit https://docs.microsoft.com/azure for docs.
139'@ | Set-Content -Path $script:CleanFile
140 }
141
142 It 'Returns empty array when no en-us links found' {
143 $result = Find-LinksInFile -FilePath $script:CleanFile
144 $result | Should -BeNullOrEmpty
145 }
146 }
147
148 Context 'Nonexistent file' {
149 It 'Returns empty array for nonexistent file' {
150 $result = Find-LinksInFile -FilePath 'C:\nonexistent\file.md'
151 $result | Should -BeNullOrEmpty
152 }
153 }
154
155 Context 'Multiple links on same line' {
156 BeforeEach {
157 $script:MultiLinkFile = Join-Path $script:TempDir 'multi-links.md'
158 'See https://docs.microsoft.com/en-us/a and https://docs.microsoft.com/en-us/b here.' |
159 Set-Content -Path $script:MultiLinkFile
160 }
161
162 It 'Finds all links on same line' {
163 $result = Find-LinksInFile -FilePath $script:MultiLinkFile
164 $result.Count | Should -Be 2
165 $result[0].LineNumber | Should -Be 1
166 $result[1].LineNumber | Should -Be 1
167 }
168 }
169}
170
171#endregion
172
173#region Repair-LinksInFile Tests
174
175Describe 'Repair-LinksInFile' -Tag 'Unit' {
176 BeforeAll {
177 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
178 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
179 }
180
181 AfterAll {
182 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
183 }
184
185 Context 'File with links to repair' {
186 BeforeEach {
187 $script:RepairFile = Join-Path $script:TempDir 'repair-test.md'
188 'Visit https://docs.microsoft.com/en-us/azure for docs.' |
189 Set-Content -Path $script:RepairFile
190
191 $script:Links = @(
192 [PSCustomObject]@{
193 OriginalUrl = 'https://docs.microsoft.com/en-us/azure'
194 FixedUrl = 'https://docs.microsoft.com/azure'
195 }
196 )
197 }
198
199 It 'Returns true when file is modified' {
200 $result = Repair-LinksInFile -FilePath $script:RepairFile -Links $script:Links
201 $result | Should -BeTrue
202 }
203
204 It 'Replaces en-us in file content' {
205 Repair-LinksInFile -FilePath $script:RepairFile -Links $script:Links
206 $content = Get-Content -Path $script:RepairFile -Raw
207 $content | Should -Not -Match 'en-us/'
208 $content | Should -Match 'https://docs.microsoft.com/azure'
209 }
210 }
211
212 Context 'File with no changes needed' {
213 BeforeEach {
214 $script:NoChangeFile = Join-Path $script:TempDir 'no-change.md'
215 'Visit https://docs.microsoft.com/azure for docs.' |
216 Set-Content -Path $script:NoChangeFile
217
218 $script:NoMatchLinks = @(
219 [PSCustomObject]@{
220 OriginalUrl = 'https://example.com/en-us/page'
221 FixedUrl = 'https://example.com/page'
222 }
223 )
224 }
225
226 It 'Returns false when no changes made' {
227 $result = Repair-LinksInFile -FilePath $script:NoChangeFile -Links $script:NoMatchLinks
228 $result | Should -BeFalse
229 }
230 }
231
232 Context 'Nonexistent file' {
233 It 'Returns false for nonexistent file' {
234 $links = @([PSCustomObject]@{ OriginalUrl = 'a'; FixedUrl = 'b' })
235 $result = Repair-LinksInFile -FilePath 'C:\nonexistent\file.md' -Links $links
236 $result | Should -BeFalse
237 }
238 }
239}
240
241#endregion
242
243#region Repair-AllLink Tests
244
245Describe 'Repair-AllLink' -Tag 'Unit' {
246 BeforeAll {
247 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
248 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
249 }
250
251 AfterAll {
252 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
253 }
254
255 Context 'Multiple files with links' {
256 BeforeEach {
257 $script:File1 = Join-Path $script:TempDir 'file1.md'
258 $script:File2 = Join-Path $script:TempDir 'file2.md'
259
260 'Link: https://docs.microsoft.com/en-us/a' | Set-Content -Path $script:File1
261 'Link: https://docs.microsoft.com/en-us/b' | Set-Content -Path $script:File2
262
263 $script:AllLinks = @(
264 [PSCustomObject]@{
265 File = $script:File1
266 LineNumber = 1
267 OriginalUrl = 'https://docs.microsoft.com/en-us/a'
268 FixedUrl = 'https://docs.microsoft.com/a'
269 },
270 [PSCustomObject]@{
271 File = $script:File2
272 LineNumber = 1
273 OriginalUrl = 'https://docs.microsoft.com/en-us/b'
274 FixedUrl = 'https://docs.microsoft.com/b'
275 }
276 )
277 }
278
279 It 'Returns count of modified files' {
280 $result = Repair-AllLink -AllLinks $script:AllLinks
281 $result | Should -Be 2
282 }
283
284 It 'Modifies all files' {
285 Repair-AllLink -AllLinks $script:AllLinks
286 (Get-Content $script:File1 -Raw) | Should -Not -Match 'en-us/'
287 (Get-Content $script:File2 -Raw) | Should -Not -Match 'en-us/'
288 }
289 }
290
291 Context 'Empty links array' {
292 It 'Returns zero for empty input' {
293 $result = Repair-AllLink -AllLinks @()
294 $result | Should -Be 0
295 }
296 }
297}
298
299#endregion
300
301#region ConvertTo-JsonOutput Tests
302
303Describe 'ConvertTo-JsonOutput' -Tag 'Unit' {
304 Context 'Valid link objects' {
305 BeforeEach {
306 $script:Links = @(
307 [PSCustomObject]@{
308 File = 'test.md'
309 LineNumber = 5
310 OriginalUrl = 'https://example.com/en-us/page'
311 FixedUrl = 'https://example.com/page'
312 }
313 )
314 }
315
316 It 'Returns array of objects' {
317 $result = ConvertTo-JsonOutput -Links $script:Links
318 $result | Should -BeOfType [PSCustomObject]
319 }
320
321 It 'Uses snake_case property names' {
322 $result = ConvertTo-JsonOutput -Links $script:Links
323 $result[0].PSObject.Properties.Name | Should -Contain 'file'
324 $result[0].PSObject.Properties.Name | Should -Contain 'line_number'
325 $result[0].PSObject.Properties.Name | Should -Contain 'original_url'
326 }
327
328 It 'Excludes FixedUrl from output' {
329 $result = ConvertTo-JsonOutput -Links $script:Links
330 $result[0].PSObject.Properties.Name | Should -Not -Contain 'FixedUrl'
331 $result[0].PSObject.Properties.Name | Should -Not -Contain 'fixed_url'
332 }
333
334 It 'Preserves values correctly' {
335 $result = ConvertTo-JsonOutput -Links $script:Links
336 $result[0].file | Should -Be 'test.md'
337 $result[0].line_number | Should -Be 5
338 $result[0].original_url | Should -Be 'https://example.com/en-us/page'
339 }
340 }
341
342 Context 'Empty input' {
343 It 'Returns empty array for empty input' {
344 $result = ConvertTo-JsonOutput -Links @()
345 $result | Should -BeNullOrEmpty
346 }
347 }
348}
349
350#endregion
351
352#region ExcludePaths Filtering Tests
353
354Describe 'ExcludePaths Filtering' -Tag 'Integration' {
355 BeforeAll {
356 $script:ScriptPath = Join-Path $PSScriptRoot '../../linting/Link-Lang-Check.ps1'
357 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
358 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
359 }
360
361 AfterAll {
362 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
363 }
364
365 Context 'Script invocation with ExcludePaths' {
366 BeforeEach {
367 # Create test directory structure
368 $script:TestsDir = Join-Path $script:TempDir 'scripts/tests/linting'
369 $script:DocsDir = Join-Path $script:TempDir 'docs'
370 New-Item -ItemType Directory -Path $script:TestsDir -Force | Out-Null
371 New-Item -ItemType Directory -Path $script:DocsDir -Force | Out-Null
372
373 # Create test file with en-us link (should be excluded)
374 $testFile = Join-Path $script:TestsDir 'test.md'
375 'Link: https://docs.microsoft.com/en-us/test' | Set-Content -Path $testFile
376
377 # Create docs file with en-us link (should be included)
378 $docsFile = Join-Path $script:DocsDir 'readme.md'
379 'Link: https://docs.microsoft.com/en-us/azure' | Set-Content -Path $docsFile
380 }
381
382 It 'Excludes files matching single pattern' {
383 Push-Location $script:TempDir
384 try {
385 # Initialize git repo for Get-GitTextFile to work
386 git init --quiet 2>$null
387 git add -A 2>$null
388 git commit -m 'init' --quiet 2>$null
389
390 # Script outputs JSON by default (when not in -Fix mode)
391 $result = & $script:ScriptPath -ExcludePaths 'scripts/tests/**' 2>$null
392 $jsonResult = $result | ConvertFrom-Json -ErrorAction SilentlyContinue
393
394 # Should only find the docs file, not the tests file
395 if ($null -ne $jsonResult -and $jsonResult.Count -gt 0) {
396 $jsonResult | ForEach-Object { $_.file } | Should -Not -Match 'scripts/tests'
397 }
398 }
399 finally {
400 Pop-Location
401 }
402 }
403
404 It 'Excludes files matching multiple patterns' {
405 Push-Location $script:TempDir
406 try {
407 # Create additional directory to exclude
408 $buildDir = Join-Path $script:TempDir 'build'
409 New-Item -ItemType Directory -Path $buildDir -Force | Out-Null
410 $buildFile = Join-Path $buildDir 'output.md'
411 'Link: https://docs.microsoft.com/en-us/build' | Set-Content -Path $buildFile
412
413 git add -A 2>$null
414 git commit -m 'add build' --quiet 2>$null
415
416 $result = & $script:ScriptPath -ExcludePaths @('scripts/tests/**', 'build/**') 2>$null
417 $jsonResult = $result | ConvertFrom-Json -ErrorAction SilentlyContinue
418
419 if ($null -ne $jsonResult -and $jsonResult.Count -gt 0) {
420 $files = $jsonResult | ForEach-Object { $_.file }
421 $files | Should -Not -Match 'scripts/tests'
422 $files | Should -Not -Match 'build/'
423 }
424 }
425 finally {
426 Pop-Location
427 }
428 }
429
430 It 'Processes all files when ExcludePaths is empty' {
431 Push-Location $script:TempDir
432 try {
433 $result = & $script:ScriptPath 2>$null
434 $jsonResult = $result | ConvertFrom-Json -ErrorAction SilentlyContinue
435
436 # Should find links in both test and docs files
437 $jsonResult.Count | Should -BeGreaterOrEqual 2
438 }
439 finally {
440 Pop-Location
441 }
442 }
443 }
444
445 Context 'Pattern matching behavior' {
446 It 'Matches glob pattern with double asterisk' {
447 # Test the -like pattern matching used in the script
448 $testPaths = @(
449 'scripts/tests/linting/test.md',
450 'scripts/tests/security/check.ps1',
451 'scripts/linting/main.ps1',
452 'docs/readme.md'
453 )
454 $pattern = 'scripts/tests/**'
455
456 $excluded = $testPaths | Where-Object { $_ -like $pattern }
457 $included = $testPaths | Where-Object { $_ -notlike $pattern }
458
459 $excluded | Should -Contain 'scripts/tests/linting/test.md'
460 $excluded | Should -Contain 'scripts/tests/security/check.ps1'
461 $included | Should -Contain 'scripts/linting/main.ps1'
462 $included | Should -Contain 'docs/readme.md'
463 }
464
465 It 'Matches multiple patterns correctly' {
466 $testPaths = @(
467 'scripts/tests/test.md',
468 'build/output.md',
469 'node_modules/pkg/file.js',
470 'src/main.ps1'
471 )
472 $patterns = @('scripts/tests/**', 'build/**', 'node_modules/**')
473
474 $included = $testPaths | Where-Object {
475 $path = $_
476 $isExcluded = $false
477 foreach ($p in $patterns) {
478 if ($path -like $p) {
479 $isExcluded = $true
480 break
481 }
482 }
483 -not $isExcluded
484 }
485
486 $included.Count | Should -Be 1
487 $included | Should -Contain 'src/main.ps1'
488 }
489 }
490}
491
492#endregion
493