Payload/scripts/harness/harness-run-slice-tests.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Repo-root runner for slice-scoped Vitest files (shared RED/GREEN command_path).

.DESCRIPTION
    Use with harness-step so RED and GREEN share one command_path for continuity.
    Pass -ExpectExit 1 on RED and 0 on GREEN. Prefer real failing tests on RED;
    do not wrap a bare exit 1.

.EXAMPLE
    pwsh -File scripts/harness-invoke.ps1 harness-step.ps1 -Feature saved-search-queries -Slice mo-save-list -Phase RED -Name slice-tests -Command .\scripts\harness-run-slice-tests.ps1 -Arguments '-TestFiles','tests/a.test.js' -ExpectExit 1 -TestFiles tests/a.test.js
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [string[]]$TestFiles,

    [string]$RepoRoot = ''
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module (Join-Path $PSScriptRoot 'lib\RepoRuntime.psm1') -Force
Import-Module (Join-Path $PSScriptRoot 'lib\HarnessContract.psm1') -Force

if ([string]::IsNullOrWhiteSpace($RepoRoot)) {
    $RepoRoot = Get-HarnessRepoRoot -StartPath $PSScriptRoot
}

Initialize-RepoShellEnvironment -LogPrefix 'harness-run-slice-tests' | Out-Null

$TestFiles = @(Expand-HarnessSensorTestFiles -TestFiles $TestFiles)

$resolved = @()
foreach ($f in $TestFiles) {
    $full = if ([System.IO.Path]::IsPathRooted($f)) { $f } else { Join-HarnessPath -Root $RepoRoot -Child $f }
    if (-not (Test-Path -LiteralPath $full)) {
        throw "Test file not found: $f"
    }
    $resolved += $f
}

Push-Location $RepoRoot
try {
    $pesterFiles = @($resolved | Where-Object { $_ -match '\.Tests\.ps1$' -or $_ -match '\.tests\.ps1$' })
    if ($pesterFiles.Count -eq $resolved.Count -and $pesterFiles.Count -gt 0) {
        Import-Module Pester -MinimumVersion 5.0.0
        $config = New-PesterConfiguration
        $config.Run.Path = @($pesterFiles | ForEach-Object {
                if ([System.IO.Path]::IsPathRooted($_)) { $_ } else { Join-HarnessPath -Root $RepoRoot -Child $_ }
            })
        $config.Run.PassThru = $true
        $config.Output.Verbosity = 'Minimal'
        $result = Invoke-Pester -Configuration $config
        if ($null -eq $result) {
            exit 1
        }
        exit [int]$result.FailedCount
    }

    $npmArgs = @('test', '--') + $resolved
    & npm @npmArgs
    exit $LASTEXITCODE
}
finally {
    Pop-Location
}