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/Mocks/GitMocks.psm1

425lines · modecode

1# GitMocks.psm1
2#
3# Purpose: Reusable mock helpers for Git CLI and GitHub Actions testing in Pester
4# Author: HVE Core Team
5#
6
7#region Environment State Management
8
9function Save-GitHubEnvironment {
10 <#
11 .SYNOPSIS
12 Saves current GitHub Actions environment variables for later restoration.
13 #>
14 [CmdletBinding()]
15 param()
16
17 $script:SavedEnvironment = @{
18 GITHUB_ACTIONS = $env:GITHUB_ACTIONS
19 GITHUB_OUTPUT = $env:GITHUB_OUTPUT
20 GITHUB_ENV = $env:GITHUB_ENV
21 GITHUB_STEP_SUMMARY = $env:GITHUB_STEP_SUMMARY
22 GITHUB_BASE_REF = $env:GITHUB_BASE_REF
23 GITHUB_HEAD_REF = $env:GITHUB_HEAD_REF
24 GITHUB_WORKSPACE = $env:GITHUB_WORKSPACE
25 GITHUB_REPOSITORY = $env:GITHUB_REPOSITORY
26 }
27
28 Write-Verbose "Saved GitHub environment state"
29}
30
31function Restore-GitHubEnvironment {
32 <#
33 .SYNOPSIS
34 Restores GitHub Actions environment variables to saved state.
35 #>
36 [CmdletBinding()]
37 param()
38
39 if (-not $script:SavedEnvironment) {
40 Write-Warning "No saved environment state found"
41 return
42 }
43
44 foreach ($key in $script:SavedEnvironment.Keys) {
45 if ($null -eq $script:SavedEnvironment[$key]) {
46 Remove-Item -Path "env:$key" -ErrorAction SilentlyContinue
47 }
48 else {
49 Set-Item -Path "env:$key" -Value $script:SavedEnvironment[$key]
50 }
51 }
52
53 Write-Verbose "Restored GitHub environment state"
54}
55
56function Initialize-MockGitHubEnvironment {
57 <#
58 .SYNOPSIS
59 Sets up a mock GitHub Actions environment with temp files.
60
61 .DESCRIPTION
62 Creates temporary files for GITHUB_OUTPUT, GITHUB_ENV, and GITHUB_STEP_SUMMARY,
63 then sets all standard GitHub Actions environment variables to simulate a CI
64 environment. Returns a hashtable of temp file paths for verification in tests.
65 Use with Remove-MockGitHubFiles for cleanup in AfterEach blocks.
66
67 .PARAMETER BaseRef
68 The base branch for PR context (default: main).
69
70 .PARAMETER HeadRef
71 The head branch for PR context (default: feature/test-branch).
72
73 .PARAMETER Workspace
74 The workspace path (default: current directory).
75
76 .PARAMETER Repository
77 The repository name (default: microsoft/hve-core).
78
79 .OUTPUTS
80 Hashtable containing paths to temp files for verification.
81 #>
82 [CmdletBinding()]
83 param(
84 [string]$BaseRef = 'main',
85 [string]$HeadRef = 'feature/test-branch',
86 [string]$Workspace = $PWD.Path,
87 [string]$Repository = 'microsoft/hve-core'
88 )
89
90 $tempDir = [System.IO.Path]::GetTempPath()
91 $guid = [System.Guid]::NewGuid().ToString('N').Substring(0, 8)
92
93 $mockFiles = @{
94 Output = Join-Path $tempDir "github_output_$guid.txt"
95 Env = Join-Path $tempDir "github_env_$guid.txt"
96 Summary = Join-Path $tempDir "github_summary_$guid.md"
97 }
98
99 # Create empty files
100 $mockFiles.Values | ForEach-Object {
101 New-Item -Path $_ -ItemType File -Force | Out-Null
102 }
103
104 # Set environment variables
105 $env:GITHUB_ACTIONS = 'true'
106 $env:GITHUB_OUTPUT = $mockFiles.Output
107 $env:GITHUB_ENV = $mockFiles.Env
108 $env:GITHUB_STEP_SUMMARY = $mockFiles.Summary
109 $env:GITHUB_BASE_REF = $BaseRef
110 $env:GITHUB_HEAD_REF = $HeadRef
111 $env:GITHUB_WORKSPACE = $Workspace
112 $env:GITHUB_REPOSITORY = $Repository
113
114 Write-Verbose "Initialized mock GitHub environment"
115 return $mockFiles
116}
117
118function Clear-MockGitHubEnvironment {
119 <#
120 .SYNOPSIS
121 Removes GitHub Actions environment variables (simulates local/non-CI environment).
122 #>
123 [CmdletBinding()]
124 param()
125
126 @(
127 'GITHUB_ACTIONS',
128 'GITHUB_OUTPUT',
129 'GITHUB_ENV',
130 'GITHUB_STEP_SUMMARY',
131 'GITHUB_BASE_REF',
132 'GITHUB_HEAD_REF',
133 'GITHUB_WORKSPACE',
134 'GITHUB_REPOSITORY'
135 ) | ForEach-Object {
136 Remove-Item -Path "env:$_" -ErrorAction SilentlyContinue
137 }
138
139 Write-Verbose "Cleared GitHub environment variables"
140}
141
142function Remove-MockGitHubFiles {
143 <#
144 .SYNOPSIS
145 Cleans up temp files created by Initialize-MockGitHubEnvironment.
146
147 .PARAMETER MockFiles
148 Hashtable returned from Initialize-MockGitHubEnvironment.
149 #>
150 [CmdletBinding()]
151 param(
152 [Parameter(Mandatory = $true)]
153 [hashtable]$MockFiles
154 )
155
156 $MockFiles.Values | ForEach-Object {
157 if ($_ -and (Test-Path $_)) {
158 Remove-Item $_ -Force -ErrorAction SilentlyContinue
159 }
160 }
161
162 Write-Verbose "Removed mock GitHub files"
163}
164
165#endregion
166
167#region Git Mock Helpers
168
169function Initialize-GitMocks {
170 <#
171 .SYNOPSIS
172 Sets up standard git command mocks for a test context.
173
174 .DESCRIPTION
175 Configures Pester mocks for git merge-base, git diff --name-only, and git rev-parse
176 commands within a specified module scope. Enables isolated testing of scripts that
177 depend on git CLI output without requiring an actual git repository. The mocks set
178 $LASTEXITCODE appropriately based on configured exit codes. Optionally mocks Test-Path
179 for file existence checks when MockTestPath is specified.
180
181 .PARAMETER ModuleName
182 The module to inject mocks into (e.g., 'LintingHelpers').
183
184 .PARAMETER MergeBase
185 SHA to return from git merge-base (default: 'abc123def456789').
186
187 .PARAMETER ChangedFiles
188 Array of file paths to return from git diff.
189
190 .PARAMETER MergeBaseExitCode
191 Exit code for merge-base command (0 = success).
192
193 .PARAMETER DiffExitCode
194 Exit code for diff command (0 = success).
195
196 .PARAMETER MockTestPath
197 Also mock Test-Path to return true for file existence checks.
198 #>
199 [CmdletBinding()]
200 param(
201 [Parameter(Mandatory = $true)]
202 [string]$ModuleName,
203
204 [string]$MergeBase = 'abc123def456789',
205
206 [string[]]$ChangedFiles = @('scripts/linting/Test-Script.ps1', 'docs/README.md'),
207
208 [int]$MergeBaseExitCode = 0,
209
210 [int]$DiffExitCode = 0,
211
212 [switch]$MockTestPath
213 )
214
215 # Store values for closure
216 $mergeBaseValue = $MergeBase
217 $mergeBaseExit = $MergeBaseExitCode
218 $changedFilesValue = $ChangedFiles
219 $diffExit = $DiffExitCode
220
221 # Mock merge-base
222 Mock git {
223 $global:LASTEXITCODE = $mergeBaseExit
224 if ($mergeBaseExit -eq 0) {
225 return $mergeBaseValue
226 }
227 return $null
228 } -ModuleName $ModuleName -ParameterFilter {
229 $args[0] -eq 'merge-base'
230 }
231
232 # Mock diff --name-only
233 Mock git {
234 $global:LASTEXITCODE = $diffExit
235 return $changedFilesValue
236 } -ModuleName $ModuleName -ParameterFilter {
237 $args[0] -eq 'diff' -and ($args -contains '--name-only')
238 }
239
240 # Mock rev-parse (fallback for HEAD~1)
241 Mock git {
242 $global:LASTEXITCODE = 0
243 return 'HEAD~1-sha'
244 } -ModuleName $ModuleName -ParameterFilter {
245 $args[0] -eq 'rev-parse'
246 }
247
248 # Optionally mock Test-Path for file existence checks
249 if ($MockTestPath) {
250 Mock Test-Path {
251 return $true
252 } -ModuleName $ModuleName -ParameterFilter {
253 # Match explicit -PathType Leaf OR no PathType specified (default file check)
254 $PathType -eq 'Leaf' -or $null -eq $PathType
255 }
256 }
257
258 Write-Verbose "Initialized git mocks for module: $ModuleName"
259}
260
261function Set-GitMockChangedFiles {
262 <#
263 .SYNOPSIS
264 Updates the changed files returned by git diff mock.
265
266 .PARAMETER ModuleName
267 The module to inject mocks into.
268
269 .PARAMETER Files
270 Array of file paths to return.
271 #>
272 [CmdletBinding()]
273 param(
274 [Parameter(Mandatory = $true)]
275 [string]$ModuleName,
276
277 [Parameter(Mandatory = $true)]
278 [string[]]$Files
279 )
280
281 $filesValue = $Files
282
283 Mock git {
284 $global:LASTEXITCODE = 0
285 return $filesValue
286 } -ModuleName $ModuleName -ParameterFilter {
287 $args[0] -eq 'diff' -and ($args -contains '--name-only')
288 }
289}
290
291function Set-GitMockMergeBaseFailure {
292 <#
293 .SYNOPSIS
294 Configures git mock to simulate merge-base failure (triggers fallback logic).
295
296 .PARAMETER ModuleName
297 The module to inject mocks into.
298
299 .PARAMETER ExitCode
300 Exit code for merge-base failure (default: 128).
301 #>
302 [CmdletBinding()]
303 param(
304 [Parameter(Mandatory = $true)]
305 [string]$ModuleName,
306
307 [int]$ExitCode = 128
308 )
309
310 $exitCodeValue = $ExitCode
311
312 Mock git {
313 $global:LASTEXITCODE = $exitCodeValue
314 return $null
315 } -ModuleName $ModuleName -ParameterFilter {
316 $args[0] -eq 'merge-base'
317 }
318}
319
320#endregion
321
322#region Test Data Generators
323
324function New-MockFileList {
325 <#
326 .SYNOPSIS
327 Generates a list of mock file paths for testing.
328
329 .PARAMETER Count
330 Number of files to generate.
331
332 .PARAMETER Extensions
333 Array of extensions to cycle through.
334
335 .PARAMETER BasePath
336 Base path prefix for generated files.
337
338 .OUTPUTS
339 Array of file path strings.
340 #>
341 [CmdletBinding()]
342 param(
343 [int]$Count = 5,
344
345 [string[]]$Extensions = @('.ps1', '.md', '.json'),
346
347 [string]$BasePath = 'scripts'
348 )
349
350 $files = @()
351 for ($i = 1; $i -le $Count; $i++) {
352 $ext = $Extensions[($i - 1) % $Extensions.Count]
353 $files += "$BasePath/file$i$ext"
354 }
355 return $files
356}
357
358function Get-MockGitDiffScenario {
359 <#
360 .SYNOPSIS
361 Returns predefined test scenarios for git diff testing.
362
363 .PARAMETER Scenario
364 The scenario name to return.
365
366 .OUTPUTS
367 Array of file paths for the specified scenario.
368 #>
369 [CmdletBinding()]
370 param(
371 [Parameter(Mandatory = $true)]
372 [ValidateSet('Empty', 'SingleFile', 'MultipleFiles', 'MixedExtensions', 'DeepPaths')]
373 [string]$Scenario
374 )
375
376 switch ($Scenario) {
377 'Empty' {
378 return @()
379 }
380 'SingleFile' {
381 return @('scripts/linting/Test.ps1')
382 }
383 'MultipleFiles' {
384 return @(
385 'scripts/linting/Script1.ps1',
386 'scripts/linting/Script2.ps1',
387 'scripts/linting/Script3.ps1'
388 )
389 }
390 'MixedExtensions' {
391 return @(
392 'scripts/linting/Script.ps1',
393 'docs/README.md',
394 'config/settings.json',
395 'scripts/security/Check.ps1'
396 )
397 }
398 'DeepPaths' {
399 return @(
400 'scripts/linting/Modules/Helpers/Utils.psm1',
401 'scripts/linting/Modules/Helpers/Tests/Utils.Tests.ps1',
402 'docs/api/v1/endpoints/users.md'
403 )
404 }
405 }
406}
407
408#endregion
409
410# Export functions
411Export-ModuleMember -Function @(
412 # Environment management
413 'Save-GitHubEnvironment',
414 'Restore-GitHubEnvironment',
415 'Initialize-MockGitHubEnvironment',
416 'Clear-MockGitHubEnvironment',
417 'Remove-MockGitHubFiles',
418 # Git mocks
419 'Initialize-GitMocks',
420 'Set-GitMockChangedFiles',
421 'Set-GitMockMergeBaseFailure',
422 # Test data
423 'New-MockFileList',
424 'Get-MockGitDiffScenario'
425)
426