name: Pester Tests

on:
  workflow_call:
    inputs:
      soft-fail:
        description: 'Whether to continue on test failures'
        required: false
        type: boolean
        default: false
      changed-files-only:
        description: 'Only test changed PowerShell files'
        required: false
        type: boolean
        default: true
      code-coverage:
        description: 'Enable code coverage reporting'
        required: false
        type: boolean
        default: false

permissions:
  contents: read

jobs:
  pester:
    name: PowerShell Tests
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write  # Required for Codecov OIDC when enable-code-coverage is true
    steps:
      - name: Checkout code
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
        with:
          persist-credentials: false
          fetch-depth: 0

      - name: Set up Node.js
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: "24"
          cache: "npm"

      - name: Install JavaScript dependencies
        run: npm ci

      - name: Install Pester
        shell: pwsh
        run: |
          Install-Module -Name Pester -RequiredVersion 5.7.1 -Force -Scope CurrentUser
          Import-Module Pester -RequiredVersion 5.7.1

      - name: Install PowerShell-Yaml
        shell: pwsh
        run: |
          Install-Module -Name PowerShell-Yaml -RequiredVersion 0.4.7 -Force -Scope CurrentUser

      - name: Get changed PowerShell files
        id: changed-files
        if: inputs.changed-files-only
        shell: pwsh
        run: |
          & ./scripts/tests/Get-ChangedTestFiles.ps1 -BaseBranch '${{ github.base_ref }}'

      - name: Run Pester Tests
        id: pester
        if: "!inputs.changed-files-only || env.HAS_CHANGES == 'true'"
        shell: pwsh
        run: |
          $params = @{ CI = $true }

          if ('${{ inputs.code-coverage }}' -eq 'true') {
            $params['CodeCoverage'] = $true
          }

          # Use specific test paths if changed-files-only is enabled
          if ('${{ inputs.changed-files-only }}' -eq 'true' -and $env:TEST_PATHS) {
            $testPaths = $env:TEST_PATHS -split ';'
            $params['TestPath'] = $testPaths
          }

          ./scripts/tests/Invoke-PesterTests.ps1 @params

          # Read coverage from summary JSON for downstream steps
          if ('${{ inputs.code-coverage }}' -eq 'true' -and (Test-Path 'logs/pester-summary.json')) {
            $summary = Get-Content 'logs/pester-summary.json' | ConvertFrom-Json
            if ($summary.CoveragePercent) {
              Write-Host "Code coverage: $($summary.CoveragePercent)%"
              if ($env:GITHUB_OUTPUT) {
                $outDir = Split-Path -Parent $env:GITHUB_OUTPUT
                if ($outDir -and -not (Test-Path $outDir)) {
                  New-Item -ItemType Directory -Path $outDir -Force | Out-Null
                }
                if (-not (Test-Path $env:GITHUB_OUTPUT)) {
                  New-Item -ItemType File -Path $env:GITHUB_OUTPUT -Force | Out-Null
                }
                "coverage=$($summary.CoveragePercent)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
              }
            }
          }
        continue-on-error: ${{ inputs.soft-fail }}

      - name: Upload test results
        if: always() && (!inputs.changed-files-only || env.HAS_CHANGES == 'true')
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: pester-test-results
          path: |
            logs/pester-results.xml
            logs/pester-summary.json
            logs/pester-failures.json
          retention-days: 30
          if-no-files-found: ignore

      - name: Upload Coverage Report
        if: inputs.code-coverage && success() && (!inputs.changed-files-only || env.HAS_CHANGES == 'true')
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: coverage-report
          path: logs/coverage.xml
          retention-days: 30
          if-no-files-found: warn

      - name: Upload to Codecov
        if: inputs.code-coverage && success() && (!inputs.changed-files-only || env.HAS_CHANGES == 'true')
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
        with:
          files: logs/coverage.xml
          use_oidc: true
          fail_ci_if_error: false
          verbose: true
          flags: pester
          name: pester-coverage

      - name: Coverage Threshold Check
        if: inputs.code-coverage && success() && (!inputs.changed-files-only || env.HAS_CHANGES == 'true')
        shell: pwsh
        run: |
          $coverageValue = '${{ steps.pester.outputs.coverage }}'
          if ([string]::IsNullOrWhiteSpace($coverageValue)) {
            Write-Warning "Coverage output is empty - skipping threshold check"
            exit 0
          }
          $percentage = [double]$coverageValue
          $threshold = 18  # Informational baseline

          Write-Host "Coverage: $percentage%"
          Write-Host "Baseline threshold: $threshold%"

          if ($percentage -lt $threshold) {
            Write-Warning "Coverage $percentage% is below baseline $threshold% - informational only"
          } else {
            Write-Host "Coverage meets or exceeds baseline threshold" -ForegroundColor Green
          }

      - name: Check results
        if: "!inputs.soft-fail && (!inputs.changed-files-only || env.HAS_CHANGES == 'true')"
        shell: pwsh
        run: |
          if (Test-Path 'logs/pester-summary.json') {
            $summary = Get-Content 'logs/pester-summary.json' | ConvertFrom-Json
            if ($summary.FailedCount -gt 0) {
              Write-Error "Pester tests failed: $($summary.FailedCount) failure(s)"
              exit 1
            }
          }
