microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e69486a5f809ede45c63c0a31358c12912bd5168

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

350lines · modecode

1#Requires -Modules Pester
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4<#
5.SYNOPSIS
6 Pester tests for Markdown-Link-Check.ps1 script
7.DESCRIPTION
8 Tests for markdown link checking wrapper functions:
9 - Get-MarkdownTarget
10 - Get-RelativePrefix
11#>
12
13BeforeAll {
14 $script:ScriptPath = Join-Path $PSScriptRoot '../../linting/Markdown-Link-Check.ps1'
15 . $script:ScriptPath
16
17 # Import LintingHelpers for mocking
18 Import-Module (Join-Path $PSScriptRoot '../../linting/Modules/LintingHelpers.psm1') -Force
19
20 $script:FixtureDir = Join-Path $PSScriptRoot '../Fixtures/Linting'
21}
22
23AfterAll {
24 Remove-Module LintingHelpers -Force -ErrorAction SilentlyContinue
25}
26
27#region Get-MarkdownTarget Tests
28
29Describe 'Get-MarkdownTarget' -Tag 'Unit' {
30 BeforeAll {
31 # Create a temp directory to use as test input
32 $script:TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
33 New-Item -ItemType Directory -Path $script:TempDir -Force | Out-Null
34 }
35
36 AfterAll {
37 Remove-Item -Path $script:TempDir -Recurse -Force -ErrorAction SilentlyContinue
38 }
39
40 Context 'Git-tracked files in repository' {
41 BeforeEach {
42 # Create test markdown files
43 $script:TestFile1 = Join-Path $script:TempDir 'test1.md'
44 $script:TestFile2 = Join-Path $script:TempDir 'test2.md'
45 Set-Content -Path $script:TestFile1 -Value '# Test 1'
46 Set-Content -Path $script:TestFile2 -Value '# Test 2'
47
48 # Mock git to indicate we're in a repo and return tracked files
49 Mock git {
50 if ($args -contains 'rev-parse') {
51 $global:LASTEXITCODE = 0
52 return $script:TempDir
53 }
54 elseif ($args -contains 'ls-files') {
55 $global:LASTEXITCODE = 0
56 return @('test1.md', 'test2.md')
57 }
58 }
59 }
60
61 It 'Returns markdown files when given a directory' {
62 $result = Get-MarkdownTarget -InputPath $script:TempDir
63 $result | Should -Not -BeNullOrEmpty
64 }
65 }
66
67 Context 'Non-git fallback mode' {
68 BeforeEach {
69 # Create test files
70 $script:TestFile = Join-Path $script:TempDir 'readme.md'
71 Set-Content -Path $script:TestFile -Value '# Readme'
72
73 # Mock git to simulate not being in a repo
74 Mock git {
75 $global:LASTEXITCODE = 128
76 return 'fatal: not a git repository'
77 }
78 }
79
80 It 'Falls back to filesystem when not in git repo' {
81 $result = Get-MarkdownTarget -InputPath $script:TempDir
82 $result | Should -Not -BeNullOrEmpty
83 }
84
85 It 'Returns absolute paths' {
86 $result = Get-MarkdownTarget -InputPath $script:TempDir
87 if ($result) {
88 [System.IO.Path]::IsPathRooted($result[0]) | Should -BeTrue
89 }
90 }
91 }
92
93 Context 'Empty input handling' {
94 It 'Returns empty array for null input' {
95 $result = Get-MarkdownTarget -InputPath $null
96 $result | Should -BeNullOrEmpty
97 }
98
99 It 'Returns empty array for empty string input' {
100 $result = Get-MarkdownTarget -InputPath ''
101 $result | Should -BeNullOrEmpty
102 }
103 }
104
105 Context 'Fixture exclusion filtering' {
106 BeforeEach {
107 # Create test files including fixture path
108 $script:IncludeFile = Join-Path $script:TempDir 'docs' 'readme.md'
109 $script:ExcludeFile = Join-Path $script:TempDir 'scripts' 'tests' 'Fixtures' 'test.md'
110
111 New-Item -ItemType Directory -Path (Join-Path $script:TempDir 'docs') -Force | Out-Null
112 New-Item -ItemType Directory -Path (Join-Path $script:TempDir 'scripts' 'tests' 'Fixtures') -Force | Out-Null
113 Set-Content -Path $script:IncludeFile -Value '# Include This'
114 Set-Content -Path $script:ExcludeFile -Value '# Exclude Fixture'
115
116 # Mock git to simulate repository with tracked files including fixtures
117 Mock git {
118 if ($args -contains 'rev-parse') {
119 $global:LASTEXITCODE = 0
120 return $script:TempDir
121 }
122 elseif ($args -contains 'ls-files') {
123 $global:LASTEXITCODE = 0
124 # Return both fixture and non-fixture files
125 return @('docs/readme.md', 'scripts/tests/Fixtures/test.md')
126 }
127 }
128 }
129
130 It 'Filters out test fixture files from results' {
131 # Act
132 $result = Get-MarkdownTarget -InputPath $script:TempDir
133
134 # Assert - Should exclude files in scripts/tests/Fixtures/
135 $fixtureFiles = $result | Where-Object { $_ -like '*Fixtures*' }
136 $fixtureFiles | Should -BeNullOrEmpty
137 }
138
139 It 'Includes non-fixture files in results' {
140 # Act
141 $result = Get-MarkdownTarget -InputPath $script:TempDir
142
143 # Assert - Should include docs files
144 $docsFiles = $result | Where-Object { $_ -like '*docs*readme.md' }
145 $docsFiles | Should -Not -BeNullOrEmpty
146 }
147
148 It 'Correctly applies the notlike filter pattern' {
149 # Test the exact filter pattern used in the code
150 $testPaths = @('docs/readme.md', 'scripts/tests/Fixtures/test.md', 'src/guide.md')
151 $filtered = $testPaths | Where-Object { $_ -notlike 'scripts/tests/Fixtures/*' }
152
153 $filtered | Should -Contain 'docs/readme.md'
154 $filtered | Should -Contain 'src/guide.md'
155 $filtered | Should -Not -Contain 'scripts/tests/Fixtures/test.md'
156 }
157 }
158}
159
160#endregion
161
162#region Get-RelativePrefix Tests
163
164Describe 'Get-RelativePrefix' -Tag 'Unit' {
165 BeforeAll {
166 # Create a temp directory structure for testing relative paths
167 $script:TempRoot = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
168 New-Item -ItemType Directory -Path $script:TempRoot -Force | Out-Null
169 New-Item -ItemType Directory -Path (Join-Path $script:TempRoot 'docs') -Force | Out-Null
170 New-Item -ItemType Directory -Path (Join-Path $script:TempRoot 'docs/guide') -Force | Out-Null
171 New-Item -ItemType Directory -Path (Join-Path $script:TempRoot 'src') -Force | Out-Null
172 }
173
174 AfterAll {
175 Remove-Item -Path $script:TempRoot -Recurse -Force -ErrorAction SilentlyContinue
176 }
177
178 Context 'Nested directory traversal' {
179 It 'Returns relative prefix from subdirectory to root' {
180 $fromPath = Join-Path $script:TempRoot 'docs/guide'
181 $result = Get-RelativePrefix -FromPath $fromPath -ToPath $script:TempRoot
182 $result | Should -Be '../../'
183 }
184
185 It 'Returns relative prefix from single-level directory to root' {
186 $fromPath = Join-Path $script:TempRoot 'docs'
187 $result = Get-RelativePrefix -FromPath $fromPath -ToPath $script:TempRoot
188 $result | Should -Be '../'
189 }
190 }
191
192 Context 'Same directory' {
193 It 'Returns empty string for same directory' {
194 $result = Get-RelativePrefix -FromPath $script:TempRoot -ToPath $script:TempRoot
195 $result | Should -Be ''
196 }
197 }
198
199 Context 'Sibling directories' {
200 It 'Returns correct prefix between sibling directories' {
201 $fromPath = Join-Path $script:TempRoot 'docs'
202 $toPath = Join-Path $script:TempRoot 'src'
203 $result = Get-RelativePrefix -FromPath $fromPath -ToPath $toPath
204 $result | Should -Be '../src/'
205 }
206 }
207
208 Context 'Forward slash normalization' {
209 It 'Returns forward slashes on Windows' {
210 $fromPath = Join-Path $script:TempRoot 'docs/guide'
211 $result = Get-RelativePrefix -FromPath $fromPath -ToPath $script:TempRoot
212 $result | Should -Not -Match '\\'
213 }
214
215 It 'Always has trailing slash when not empty' {
216 $fromPath = Join-Path $script:TempRoot 'docs'
217 $result = Get-RelativePrefix -FromPath $fromPath -ToPath $script:TempRoot
218 if ($result -ne '') {
219 $result | Should -Match '/$'
220 }
221 }
222 }
223}
224
225#endregion
226
227#region Script Integration Tests
228
229Describe 'Markdown-Link-Check Integration' -Tag 'Integration' {
230 Context 'Config file loading' {
231 BeforeAll {
232 $script:ConfigPath = Join-Path $PSScriptRoot '../Fixtures/Linting/link-check-config.json'
233 }
234
235 It 'Config fixture file exists' {
236 Test-Path $script:ConfigPath | Should -BeTrue
237 }
238
239 It 'Config fixture is valid JSON' {
240 { Get-Content $script:ConfigPath | ConvertFrom-Json } | Should -Not -Throw
241 }
242
243 It 'Config contains expected properties' {
244 $config = Get-Content $script:ConfigPath | ConvertFrom-Json
245 $config.PSObject.Properties.Name | Should -Contain 'ignorePatterns'
246 $config.PSObject.Properties.Name | Should -Contain 'replacementPatterns'
247 }
248 }
249
250 Context 'Main execution error handling' {
251 BeforeAll {
252 $script:OriginalGHA = $env:GITHUB_ACTIONS
253 $script:LinkCheckScript = Join-Path $PSScriptRoot '../../linting/Markdown-Link-Check.ps1'
254 }
255
256 AfterAll {
257 if ($null -eq $script:OriginalGHA) {
258 Remove-Item Env:GITHUB_ACTIONS -ErrorAction SilentlyContinue
259 } else {
260 $env:GITHUB_ACTIONS = $script:OriginalGHA
261 }
262 }
263
264 It 'Outputs GitHub error annotation when script fails in CI' {
265 # Arrange
266 $env:GITHUB_ACTIONS = 'true'
267
268 # Create temp directory with no markdown files
269 $emptyDir = Join-Path $TestDrive 'empty-no-md'
270 New-Item -ItemType Directory -Path $emptyDir -Force | Out-Null
271
272 # Mock git to simulate no tracked markdown files
273 Mock git {
274 if ($args -contains 'rev-parse') {
275 $global:LASTEXITCODE = 0
276 return $emptyDir
277 }
278 elseif ($args -contains 'ls-files') {
279 $global:LASTEXITCODE = 0
280 return @() # No markdown files
281 }
282 }
283
284 # Act - Run script with empty directory (will fail with no files found)
285 $output = & $script:LinkCheckScript -Path $emptyDir 2>&1
286
287 # Assert - Should output error
288 $errors = $output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }
289 $errors | Should -Not -BeNullOrEmpty
290 }
291 }
292}
293
294#endregion
295
296#region Invoke-MarkdownLinkCheck Tests
297
298Describe 'Invoke-MarkdownLinkCheck' -Tag 'Unit' {
299 BeforeAll {
300 $script:RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '../../..')).Path
301 $script:FixtureConfig = Join-Path $PSScriptRoot '../Fixtures/Linting/link-check-config.json'
302 }
303
304 Context 'No markdown files found' {
305 It 'Throws when Get-MarkdownTarget returns empty' {
306 Mock Get-MarkdownTarget { return @() }
307 Mock Resolve-Path { return [PSCustomObject]@{ Path = $script:RepoRoot } }
308
309 { Invoke-MarkdownLinkCheck -Path @('nonexistent') -ConfigPath $script:FixtureConfig } |
310 Should -Throw '*No markdown files were found to validate*'
311 }
312 }
313
314 Context 'CLI not installed' {
315 It 'Throws when markdown-link-check binary is missing' {
316 Mock Get-MarkdownTarget { return @('file.md') }
317 Mock Resolve-Path { return [PSCustomObject]@{ Path = $script:RepoRoot } }
318 Mock Test-Path { return $false } -ParameterFilter { $LiteralPath -and $LiteralPath -like '*markdown-link-check*' }
319
320 { Invoke-MarkdownLinkCheck -Path @('file.md') -ConfigPath $script:FixtureConfig } |
321 Should -Throw '*markdown-link-check is not installed*'
322 }
323 }
324
325 Context 'Quiet mode base arguments' {
326 It 'Passes -q flag when Quiet switch is set' {
327 Mock Get-MarkdownTarget { return @('file.md') }
328 Mock Resolve-Path { return [PSCustomObject]@{ Path = $script:RepoRoot } }
329 Mock Test-Path { return $true } -ParameterFilter { $LiteralPath -and $LiteralPath -like '*markdown-link-check*' }
330 Mock Push-Location { }
331 Mock Pop-Location { }
332 Mock Resolve-Path { return [PSCustomObject]@{ Path = "$TestDrive/file.md" } } -ParameterFilter { $LiteralPath -eq 'file.md' }
333 Mock New-Item { } -ParameterFilter { $ItemType -eq 'Directory' }
334 Mock Set-Content { }
335 Mock Write-Host { }
336
337 try {
338 Invoke-MarkdownLinkCheck -Path @('file.md') -ConfigPath $script:FixtureConfig -Quiet
339 }
340 catch {
341 Write-Verbose "CLI execution expected to fail in test environment: $_"
342 }
343
344 Should -Invoke Get-MarkdownTarget -Times 1
345 Should -Invoke Push-Location -Times 1
346 }
347 }
348}
349
350#endregion
351