testing.md

February 27, 2026 · View on GitHub

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:

SettingValue
Coverage target80% minimum
Output formatJaCoCo XML
Output pathlogs/coverage.xml
Excluded patterns*.Tests.ps1

Coverage directories include linting/, security/, lib/, extension/, plugins/, collections/, and tests/.

Test Output

Output TypeFormatPath
Test resultsNUnitXmllogs/pester-results.xml
Coverage reportJaCoCologs/coverage.xml

Test Utilities

LintingHelpers Module

The LintingHelpers.psm1 module provides shared functions for linting scripts and tests:

FunctionPurpose
Get-ChangedFilesFromGitDetects changed files using merge-base with fallback strategies
Get-FilesRecursiveFinds files via git ls-files with Get-ChildItem fallback
Get-GitIgnorePatternsParses .gitignore into PowerShell wildcard patterns
Write-GitHubAnnotationWrites GitHub Actions annotations for errors and warnings
Set-GitHubOutputSets GitHub Actions output variables
Set-GitHubEnvSets GitHub Actions environment variables

GitMocks Module

The GitMocks.psm1 module provides reusable mock helpers for Git CLI and GitHub Actions testing.

Environment Management

FunctionPurpose
Save-GitHubEnvironmentSaves current GitHub Actions environment variables
Restore-GitHubEnvironmentRestores saved environment state
Initialize-MockGitHubEnvironmentCreates mock GitHub Actions environment with temp files
Clear-MockGitHubEnvironmentRemoves GitHub Actions environment variables
Remove-MockGitHubFilesCleans up temp files from mock initialization

Git Mocks

FunctionPurpose
Initialize-GitMocksSets up standard git command mocks for a module
Set-GitMockChangedFilesUpdates files returned by git diff mock
Set-GitMockMergeBaseFailureSimulates merge-base failure for fallback testing

Test Data

FunctionPurpose
New-MockFileListGenerates mock file paths for testing
Get-MockGitDiffScenarioReturns 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

CommandDescription
npm run test:psRun 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.ps1 orchestrates full test runs with configuration loading, code coverage, CI output formatting, and result file generation. The npm run test:ps command calls this script.
  • Get-ChangedTestFiles.ps1 identifies 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.