Skip to content

[Issue]: Add Pester unit testing framework for PowerShell scripts #190

@WilliamBerryiii

Description

@WilliamBerryiii

Issue Description

The repository contains 12 PowerShell scripts with approximately 50+ exported and internal functions that lack unit test coverage. While PSScriptAnalyzer runs on every PR for static analysis/linting, there are no Pester tests to validate the actual behavior of these functions.

Current State:

  • 12 PowerShell scripts across scripts/ subdirectories
  • 1 PowerShell module (LintingHelpers.psm1) with 7 exported functions
  • 50+ internal functions with testable logic
  • PSScriptAnalyzer runs on PRs (linting only)
  • No Pester tests exist
  • No Pester workflow exists

Requested Implementation:

  1. Create test infrastructure

    • Add scripts/tests/ directory for Pester test files
    • Create *.Tests.ps1 files following Pester naming conventions
    • Add Pester configuration file (pester.config.ps1 or PesterConfiguration)
  2. Create reusable Pester workflow (.github/workflows/pester-tests.yml)

    • Run on PR when *.ps1 or *.psm1 files change
    • Support changed-files-only input for targeted testing
    • Output test results in NUnit/JUnit format for GitHub annotations
    • Support soft-fail input for gradual rollout
  3. Add Pester job to PR validation (.github/workflows/pr-validation.yml)

    • New job: PowerShell Tests using the reusable workflow
    • Trigger only when PowerShell files change (path filter)
  4. Priority test coverage for high-value functions:

    Script Functions to Test Priority
    LintingHelpers.psm1 Get-ChangedFilesFromGit, Get-FilesRecursive, Get-GitIgnorePatterns High
    Validate-MarkdownFrontmatter.ps1 Get-MarkdownFrontmatter, Test-MarkdownFooter, Test-FrontmatterValidation High
    Test-DependencyPinning.ps1 Test-SHAPinning, Get-DependencyViolation, Get-RemediationSuggestion High
    Test-SHAStaleness.ps1 Compare-ToolVersion, Get-ToolStaleness Medium
    Link-Lang-Check.ps1 Find-LinksInFile, Repair-LinksInFile Medium
    Get-VerifiedDownload.ps1 Hash verification logic, extraction paths Medium
    Generate-PrReference.ps1 Resolve-ComparisonReference, Get-CommitEntry Low

Acceptance Criteria:

  • Pester 5.x installed and configured
  • At least one *.Tests.ps1 file per script directory
  • Reusable workflow for Pester tests
  • PR validation includes PowerShell test job
  • Tests run only when PowerShell files change
  • Code coverage reporting (optional, stretch goal)

Benefits:

  • Catch regressions before they reach main
  • Enable confident refactoring of PowerShell utilities
  • Validate edge cases (e.g., malformed frontmatter, missing git context)
  • Align with industry standard PowerShell testing practices

Additional Context

Testable Artifacts Inventory:

scripts/
├── dev-tools/
│   └── Generate-PrReference.ps1          # 5 functions
├── extension/
│   ├── Package-Extension.ps1             # Script logic
│   └── Prepare-Extension.ps1             # 1 function
├── lib/
│   └── Get-VerifiedDownload.ps1          # Verification + extraction logic
├── linting/
│   ├── Invoke-LinkLanguageCheck.ps1      # Orchestration script
│   ├── Invoke-PSScriptAnalyzer.ps1       # Orchestration script
│   ├── Link-Lang-Check.ps1               # 5 functions
│   ├── Markdown-Link-Check.ps1           # 2 functions
│   ├── Validate-MarkdownFrontmatter.ps1  # 8 functions
│   └── Modules/
│       └── LintingHelpers.psm1           # 7 exported functions
└── security/
    ├── Test-DependencyPinning.ps1        # 9 functions
    ├── Test-SHAStaleness.ps1             # 10 functions
    └── Update-ActionSHAPinning.ps1       # 12 functions

Example Pester Test Structure:

# scripts/tests/LintingHelpers.Tests.ps1
BeforeAll {
    Import-Module "$PSScriptRoot/../linting/Modules/LintingHelpers.psm1" -Force
}

Describe 'Get-GitIgnorePatterns' {
    Context 'when .gitignore exists' {
        It 'should parse comment lines correctly' {
            # Test implementation
        }
        
        It 'should handle directory patterns with trailing slash' {
            # Test implementation
        }
    }
    
    Context 'when .gitignore does not exist' {
        It 'should return empty array' {
            # Test implementation
        }
    }
}

References:

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions