Tests/Stop-WorkloadSession.Tests.ps1

Describe 'Stop-WorkloadSession' {

    BeforeAll {
        $modulePath = Join-Path (Join-Path $PSScriptRoot '..') 'WinslopFix.psd1'
        Import-Module $modulePath -Force -ErrorAction Stop
    }

    AfterAll {
        Remove-Module WinslopFix -Force -ErrorAction SilentlyContinue
    }

    Context 'When no processes are running' {

        BeforeAll {
            Mock -ModuleName WinslopFix Get-WinslopFixProcess { return @() } -ParameterFilter {
                $ProcessName -eq 'WorkloadsSessionHost'
            }
        }

        It 'returns nothing when no processes exist' {
            $result = Stop-WorkloadSession -Confirm:$false
            $result | Should -BeNullOrEmpty
        }
    }

    Context 'MemoryThreshold strategy' {

        BeforeAll {
            $mockProcesses = @(
                [PSCustomObject]@{
                    Id                  = 1001
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 600MB  # Exceeds 512MB default
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(30)
                    StartTime           = (Get-Date).AddSeconds(-120)
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru
                
                [PSCustomObject]@{
                    Id                  = 1002
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 200MB  # Under threshold
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(10)
                    StartTime           = (Get-Date).AddSeconds(-60)
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru
            )

            Mock -ModuleName WinslopFix Get-WinslopFixProcess { return $mockProcesses } -ParameterFilter {
                $ProcessName -eq 'WorkloadsSessionHost'
            }
            Mock -ModuleName WinslopFix Write-WinslopFixLog {}
        }

        It 'terminates only processes exceeding the per-process threshold' {
            $results = @(Stop-WorkloadSession -Strategy MemoryThreshold -PerProcessThresholdMB 512 -Confirm:$false)

            # Only PID 1001 (600MB) should be targeted
            $results.Count | Should -Be 1
            $results[0].PID | Should -Be 1001
            $results[0].Result | Should -Be 'Terminated'
        }

        It 'supports -WhatIf without terminating' {
            $results = @(Stop-WorkloadSession -Strategy MemoryThreshold -WhatIf)
            # WhatIf returns nothing in our implementation (targets are logged to verbose/WhatIf stream)
            $results.Count | Should -Be 0
        }
    }

    Context 'Timer strategy' {

        BeforeAll {
            $mockProcesses = @(
                [PSCustomObject]@{
                    Id                  = 2001
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 100MB
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(5)
                    StartTime           = (Get-Date).AddSeconds(-120)  # 120s old
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru

                [PSCustomObject]@{
                    Id                  = 2002
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 100MB
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(2)
                    StartTime           = (Get-Date).AddSeconds(-30)   # 30s old
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru
            )

            Mock -ModuleName WinslopFix Get-WinslopFixProcess { return $mockProcesses } -ParameterFilter {
                $ProcessName -eq 'WorkloadsSessionHost'
            }
            Mock -ModuleName WinslopFix Write-WinslopFixLog {}
        }

        It 'terminates only processes older than TimerSeconds' {
            $results = @(Stop-WorkloadSession -Strategy Timer -TimerSeconds 60 -Confirm:$false)

            # Only PID 2001 (120s) should be targeted, not PID 2002 (30s)
            $results.Count | Should -Be 1
            $results[0].PID | Should -Be 2001
            $results[0].Result | Should -Be 'Terminated'
        }
    }

    Context 'All strategy' {

        BeforeAll {
            $mockProcesses = @(
                [PSCustomObject]@{
                    Id                  = 3001
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 50MB
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(1)
                    StartTime           = (Get-Date).AddSeconds(-10)
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru
                
                [PSCustomObject]@{
                    Id                  = 3002
                    ProcessName         = 'WorkloadsSessionHost'
                    PrivateMemorySize64 = 50MB
                    TotalProcessorTime  = [TimeSpan]::FromSeconds(1)
                    StartTime           = (Get-Date).AddSeconds(-10)
                } | Add-Member -MemberType ScriptMethod -Name Kill -Value {} -PassThru
            )

            Mock -ModuleName WinslopFix Get-WinslopFixProcess { return $mockProcesses } -ParameterFilter {
                $ProcessName -eq 'WorkloadsSessionHost'
            }
            Mock -ModuleName WinslopFix Write-WinslopFixLog {}
        }

        It 'terminates all processes regardless of metrics' {
            $results = @(Stop-WorkloadSession -Strategy All -Confirm:$false)
            $results.Count | Should -Be 2
        }
    }

    Context 'Parameter validation' {

        It 'rejects invalid Strategy values' {
            { Stop-WorkloadSession -Strategy 'Invalid' -Confirm:$false } | Should -Throw
        }

        It 'rejects PerProcessThresholdMB below minimum' {
            { Stop-WorkloadSession -PerProcessThresholdMB 10 -Confirm:$false } | Should -Throw
        }

        It 'rejects TimerSeconds below minimum' {
            { Stop-WorkloadSession -Strategy Timer -TimerSeconds 5 -Confirm:$false } | Should -Throw
        }
    }
}