private/tests/Invoke-ZtTest.ps1

function Invoke-ZtTest {
    <#
    .SYNOPSIS
        Execute individual tests and collect execution statistics.
 
    .DESCRIPTION
        Execute individual tests and collect execution statistics.
        This command is expected to be run from background runspaces launched by Start-ZtTestExecution.
 
        Use Get-ZtTestStatistics to retrieve the results of these executions.
 
    .PARAMETER Test
        The test object to process.
        Expects an object as returned by Get-ZtTest.
 
    .PARAMETER Database
        The Database used for accessing cached tenant data.
 
    .PARAMETER LogsPath
        Path to the logs folder where per-test log files are written.
        If not specified, no log files are written.
 
    .EXAMPLE
        PS C:\> Invoke-ZtTest -Test $_ -Database $global:database -LogsPath $logsPath
 
        Executes the current test with the globally cached database connection and writes a log file.
    #>

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

        [DuckDB.NET.Data.DuckDBConnection]
        $Database,

        [string]
        $LogsPath
    )
    begin {
        $previousMessages = Get-PSFMessage -Runspace ([runspace]::DefaultRunspace.InstanceId)
        $result = [PSCustomObject]@{
            PSTypeName = 'ZeroTrustAssessment.TestStatistics'
            TestID     = $Test.TestID
            Test       = $Test

            # Performance Metrics, in case we want to identify problematic tests
            Start      = $null
            End        = $null
            Duration   = $null

            # What Happened?
            Success    = $true
            Error      = $null
            Messages   = $null

            # Test should have no output, but we'll catch it anyways, just in case
            Output     = $null
        }
    }
    process {
        Write-PSFMessage -Message "Processing test '{0}'" -StringValues $Test.TestID -Target $Test -Tag start

        # Check if the function exists and what parameters it has
        $command = Get-Command $Test.Command -ErrorAction SilentlyContinue
        if (-not $command) {
            Write-PSFMessage -Level Warning -Message "Test command for test '{0}' not found" -StringValues $Test.TestID -Target $Test
        }

        $dbParam = @{}
        if (($null -ne $command) -and $command.Parameters.ContainsKey("Database") -and $Database) {
            $dbParam.Database = $Database
        }

        # Write stub log file and progress entry so hanging tests are visible
        if ($LogsPath) {
            Write-ZtTestProgress -TestID $Test.TestID -LogsPath $LogsPath -Action Started
            try {
                $stubPath = Join-Path $LogsPath "$($Test.TestID).md"
                [System.IO.File]::WriteAllText($stubPath, "# Test: $($Test.TestID) - Started at $((Get-Date).ToString('yyyy-MM-dd HH:mm:ss.fff'))$([System.Environment]::NewLine)")
            }
            catch {
                Write-PSFMessage -Level Warning -Message "Failed to write stub test log for test '{0}': {1}" -StringValues $Test.TestID, $_ -Tag log
            }
        }

        try {
            # Set Current Test for "Add-ZtTestResultDetail to pick up"
            $script:__ztCurrentTest = $Test

            $result.Start = Get-Date
            $result.Output = & $command @dbParam -ErrorAction Stop
        }
        catch {
            Write-PSFMessage -Level Warning -Message "Error executing test '{0}'" -StringValues $Test.TestID -Target $Test -ErrorRecord $_
            $result.Success = $false
            $result.Error = $_
        }
        finally {
            $result.End = Get-Date
            $result.Duration = $result.End - $result.Start

            # Reset marker in an assured way, to prevent confusion about the current test being executed
            $script:__ztCurrentTest = $null
        }
        Write-PSFMessage -Message "Processing test '{0}' - Concluded" -StringValues $Test.TestID -Target $Test -Tag end
    }
    end {
        $result.Messages = Get-PSFMessage -Runspace ([runspace]::DefaultRunspace.InstanceId) | Where-Object { $_ -notin $previousMessages }
        Write-ZtTestStatistics -Result $result

        # Write per-test log file (overwrites stub) and progress entry
        if ($LogsPath) {
            Write-ZtTestLog -Result $result -LogsPath $LogsPath
            $progressAction = if ($result.Success) { 'Completed' } else { 'Failed' }
            $progressError = if (-not $result.Success -and $result.Error) { "$($result.Error)" } else { $null }
            Write-ZtTestProgress -TestID $result.TestID -LogsPath $LogsPath -Action $progressAction -Duration $result.Duration -ErrorMessage $progressError
        }

        $result
    }
}