Testing Architecture
Overview
HVE Core uses Pester 5.x for PowerShell testing with a mirror directory structure that maps production scripts to their corresponding test files. The test infrastructure supports isolated unit testing through mock utilities and enforces an 80% code coverage threshold.
Directory Structure
Test files follow a mirror pattern where each script directory has a corresponding tests/ subdirectory:
scripts/
├── collections/
│ └── *.ps1
├── extension/
│ ├── Package-Extension.ps1
│ └── Prepare-Extension.ps1
├── lib/
│ └── Get-VerifiedDownload.ps1
├── linting/
│ └── *.ps1
├── plugins/
│ └── *.ps1
├── security/
│ └── *.ps1
└── tests/
├── collections/
├── extension/
├── lib/
├── linting/
├── plugins/
├── security/
├── Fixtures/
├── Mocks/
│ └── GitMocks.psm1
└── pester.config.ps1
Test files use the .Tests.ps1 suffix convention, enabling automatic discovery by Pester.
Pester Configuration
The configuration file at scripts/tests/pester.config.ps1 defines test execution behavior:
# Key configuration settings
$configuration.Run.TestExtension = '.Tests.ps1'
$configuration.Filter.ExcludeTag = @('Integration', 'Slow')
$configuration.CodeCoverage.CoveragePercentTarget = 80
Coverage Configuration
Code coverage analyzes scripts in production directories while excluding test files:
| Setting | Value |
|---|---|
| Coverage target | 80% minimum |
| Output format | JaCoCo XML |
| Output path | logs/coverage.xml |
| Excluded patterns | *.Tests.ps1 |
Coverage directories include linting/, security/, lib/, extension/, plugins/, collections/, and tests/.
Test Output
| Output Type | Format | Path |
|---|---|---|
| Test results | NUnitXml | logs/pester-results.xml |
| Coverage report | JaCoCo | logs/coverage.xml |
Test Utilities
LintingHelpers Module
The LintingHelpers.psm1 module provides shared functions for linting scripts and tests:
| Function | Purpose |
|---|---|
Get-ChangedFilesFromGit | Detects changed files using merge-base with fallback strategies |
Get-FilesRecursive | Finds files via git ls-files with Get-ChildItem fallback |
Get-GitIgnorePatterns | Parses .gitignore into PowerShell wildcard patterns |
Write-GitHubAnnotation | Writes GitHub Actions annotations for errors and warnings |
Set-GitHubOutput | Sets GitHub Actions output variables |
Set-GitHubEnv | Sets GitHub Actions environment variables |
GitMocks Module
The GitMocks.psm1 module provides reusable mock helpers for Git CLI and GitHub Actions testing.
Environment Management
| Function | Purpose |
|---|---|
Save-GitHubEnvironment | Saves current GitHub Actions environment variables |
Restore-GitHubEnvironment | Restores saved environment state |
Initialize-MockGitHubEnvironment | Creates mock GitHub Actions environment with temp files |
Clear-MockGitHubEnvironment | Removes GitHub Actions environment variables |
Remove-MockGitHubFiles | Cleans up temp files from mock initialization |
Git Mocks
| Function | Purpose |
|---|---|
Initialize-GitMocks | Sets up standard git command mocks for a module |
Set-GitMockChangedFiles | Updates files returned by git diff mock |
Set-GitMockMergeBaseFailure | Simulates merge-base failure for fallback testing |
Test Data
| Function | Purpose |
|---|---|
New-MockFileList | Generates mock file paths for testing |
Get-MockGitDiffScenario | Returns predefined scenarios for git diff testing |
Environment Save/Restore Pattern
Tests that modify environment variables follow this pattern:
BeforeAll {
Import-Module "$PSScriptRoot/../Mocks/GitMocks.psm1" -Force
}
BeforeEach {
Save-GitHubEnvironment
$script:MockFiles = Initialize-MockGitHubEnvironment
}
AfterEach {
Remove-MockGitHubFiles -MockFiles $script:MockFiles
Restore-GitHubEnvironment
}
Running Tests
npm Scripts
| Command | Description |
|---|---|
npm run test:ps | Run all Pester tests |
Direct Pester Invocation
Run tests with default configuration:
Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1)
Run tests with code coverage:
Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1 -CodeCoverage)
Run tests in CI mode with exit codes and NUnit output:
Invoke-Pester -Configuration (& ./scripts/tests/pester.config.ps1 -CI -CodeCoverage)
Run a specific test file:
Invoke-Pester -Path ./scripts/tests/linting/Invoke-PSScriptAnalyzer.Tests.ps1
Test Utility Scripts
Two wrapper scripts in scripts/tests/ streamline test execution:
Invoke-PesterTests.ps1orchestrates full test runs with configuration loading, code coverage, CI output formatting, and result file generation. Thenpm run test:pscommand calls this script.Get-ChangedTestFiles.ps1identifies test files affected by recent changes, enabling targeted test runs during development or in pull request workflows.
See scripts/tests/README.md for parameters and usage details.
Skills Testing
Skill scripts use a co-located test pattern instead of the mirror directory structure used by scripts/. Each skill contains its own tests/ subdirectory:
.github/skills/<skill-name>/
├── scripts/
│ ├── convert.ps1
│ └── convert.sh
└── tests/
└── convert.Tests.ps1
Coverage Integration
The Pester configuration at scripts/tests/pester.config.ps1 resolves skill scripts from the repository root for code coverage analysis. When you include a skill tests/ directory in an Invoke-Pester -Path argument or test run configuration, Pester discovers the skill test files through the .Tests.ps1 naming convention.
Coverage path resolution for skills uses the repository root rather than $scriptRoot (which points to scripts/):
$repoRoot = Split-Path $scriptRoot -Parent
$skillScripts = Get-ChildItem -Path (Join-Path $repoRoot '.github/skills') `
-Include '*.ps1', '*.psm1' -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch '\.Tests\.ps1$' }
Packaging Exclusion
Co-located tests/ directories are excluded from the VSIX extension package by Package-Extension.ps1. After copying a skill directory, the packaging script removes any tests/ subdirectories from the destination.
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.