Tests/sessions/Enter-RdpSession.Tests.ps1
|
#Requires -Version 5.1 BeforeAll { <# .SYNOPSIS Test suite for Enter-RdpSession .DESCRIPTION Validates Enter-RdpSession behavior: shadow connections, control/view modes, ShouldProcess, error handling, and Group Policy restrictions. .NOTES Author: Franck SALLET Version: 1.0.0 Last Modified: 2026-03-11 Requires: PowerShell 5.1+, Pester 5.x Permissions: None (mocks CIM operations) #> $script:modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\PSWinOps.psd1' Import-Module -Name $script:modulePath -Force -ErrorAction Stop $script:mockTsSession = [PSCustomObject]@{ SessionId = 2 UserName = 'TestUser' } } Describe -Name 'Enter-RdpSession' -Fixture { Context -Name 'When entering a session successfully in Control mode' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { # FIX: MUST return real CimInstance for Invoke-CimMethod -InputObject return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 0 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should return success result object' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Success | Should -Be $true $result.ReturnCode | Should -Be 0 } It -Name 'Should include RemoteControl action type' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Action | Should -Be 'RemoteControl' } It -Name 'Should default to Control mode' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.ControlMode | Should -Be 'Control' } It -Name 'Should invoke RemoteControl method' -Test { Enter-RdpSession -SessionID 2 -Confirm:$false Should -Invoke -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -Times 1 -Exactly } It -Name 'Should verify target session exists before attempting shadow' -Test { Enter-RdpSession -SessionID 2 -Confirm:$false Should -Invoke -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -Times 2 -Exactly } It -Name 'Should clean up CIM session' -Test { Enter-RdpSession -SessionID 2 -Confirm:$false Should -Invoke -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -Times 1 -Exactly } } Context -Name 'When entering a session in View mode' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 0 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should set ControlMode to View' -Test { $result = Enter-RdpSession -SessionID 2 -ControlMode View -Confirm:$false $result.ControlMode | Should -Be 'View' } } Context -Name 'When target session does not exist' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $null } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith {} Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should write error and not attempt shadow connection' -Test { Enter-RdpSession -SessionID 999 -Confirm:$false -ErrorAction SilentlyContinue Should -Invoke -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -Times 0 -Exactly } It -Name 'Should return no output when session not found' -Test { $result = Enter-RdpSession -SessionID 999 -Confirm:$false -ErrorAction SilentlyContinue $result | Should -BeNullOrEmpty } } Context -Name 'When ShouldProcess is declined' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {} Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should not invoke shadow connection when WhatIf is specified' -Test { Enter-RdpSession -SessionID 2 -WhatIf Should -Invoke -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -Times 0 -Exactly } It -Name 'Should not create CIM session when WhatIf is specified' -Test { Enter-RdpSession -SessionID 2 -WhatIf Should -Invoke -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -Times 0 -Exactly } } Context -Name 'When user rejects the connection request' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 10 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should return failure with return code 10' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Success | Should -Be $false $result.ReturnCode | Should -Be 10 } It -Name 'Should include descriptive error message' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Message | Should -BeLike '*User rejected*' } } Context -Name 'When shadow is disabled by Group Policy' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 11 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should return failure with return code 11' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Success | Should -Be $false $result.ReturnCode | Should -Be 11 } It -Name 'Should indicate Group Policy restriction' -Test { $result = Enter-RdpSession -SessionID 2 -Confirm:$false $result.Message | Should -BeLike '*Group Policy*' } } Context -Name 'When processing pipeline input from Get-ActiveRdpSession' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 0 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should accept SessionID from pipeline' -Test { $mockSession = [PSCustomObject]@{ SessionID = 3; ComputerName = 'SRV01' } $result = $mockSession | Enter-RdpSession -Confirm:$false $result.SessionID | Should -Be 3 } } Context -Name 'When NoUserPrompt switch is specified' -Fixture { BeforeEach { Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith { New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession' } Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith { param($ClassName, $Namespace) if ($ClassName -eq 'Win32_TSSession') { return $script:mockTsSession } elseif ($ClassName -eq 'Win32_TerminalService') { return New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance' } return $null } Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { [PSCustomObject]@{ ReturnValue = 0 } } Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {} } It -Name 'Should complete successfully when policy allows silent shadow' -Test { $result = Enter-RdpSession -SessionID 2 -NoUserPrompt -Confirm:$false $result.Success | Should -Be $true } } } |