microsoft/hve-core

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
hve-core-v3.2.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

scripts/tests/Mocks/GitMocks.psm1

444lines · modecode

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