Tests/sessions/Disconnect-RdpSession.Tests.ps1

#Requires -Version 5.1

BeforeAll {
    # FIX: chemin corrigé (... → ..\..)
    $script:modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\PSWinOps.psd1'
    Import-Module -Name $script:modulePath -Force -ErrorAction Stop

    $script:mockSuccessResult = [PSCustomObject]@{ ReturnValue = 0 }
    # NOTE: mockTsService supprimé - remplacé par New-MockObject directement dans les mocks
}

Describe -Name 'Disconnect-RdpSession' -Fixture {

    Context -Name 'When disconnecting a session successfully' -Fixture {
        BeforeEach {
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                # FIX: CimInstance requis car passé à Invoke-CimMethod -InputObject
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith {
                return $script:mockSuccessResult
            }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should return success result object' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false
            $result | Should -Not -BeNullOrEmpty
            $result.Success | Should -Be $true
            $result.ReturnCode | Should -Be 0
            $result.SessionID | Should -Be 2
        }

        It -Name 'Should include correct action type' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false
            $result.Action | Should -Be 'Disconnect'
        }

        It -Name 'Should invoke DisconnectSession method with correct parameters' -Test {
            Disconnect-RdpSession -SessionID 2 -Confirm:$false
            Should -Invoke -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -Times 1 -Exactly -ParameterFilter {
                $MethodName -eq 'DisconnectSession' -and
                $Arguments.SessionId -eq 2
            }
        }

        It -Name 'Should clean up CIM session' -Test {
            Disconnect-RdpSession -SessionID 2 -Confirm:$false
            Should -Invoke -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -Times 1 -Exactly
        }

        It -Name 'Should include timestamp in result' -Test {
            $beforeTime = Get-Date
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false
            $afterTime = Get-Date
            $result.Timestamp | Should -BeOfType ([datetime])
            $result.Timestamp | Should -BeGreaterOrEqual $beforeTime
            $result.Timestamp | Should -BeLessOrEqual $afterTime
        }
    }

    Context -Name 'When ShouldProcess is declined' -Fixture {
        BeforeEach {
            # FIX: New-MockObject à la place de PSCustomObject
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { return $script:mockSuccessResult }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should not invoke disconnect when WhatIf is specified' -Test {
            Disconnect-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 {
            Disconnect-RdpSession -SessionID 2 -WhatIf
            Should -Invoke -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -Times 0 -Exactly
        }

        It -Name 'Should not return any result when WhatIf is specified' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -WhatIf
            $result | Should -BeNullOrEmpty
        }
    }

    Context -Name 'When disconnecting multiple sessions' -Fixture {
        BeforeEach {
            # FIX: New-MockObject à la place de PSCustomObject
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { return $script:mockSuccessResult }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should process all session IDs' -Test {
            $result = Disconnect-RdpSession -SessionID 2, 3, 5 -Confirm:$false
            $result.Count | Should -Be 3
            $result[0].SessionID | Should -Be 2
            $result[1].SessionID | Should -Be 3
            $result[2].SessionID | Should -Be 5
        }

        It -Name 'Should invoke method once per session' -Test {
            Disconnect-RdpSession -SessionID 2, 3 -Confirm:$false
            Should -Invoke -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -Times 2 -Exactly
        }

        It -Name 'Should create and clean up CIM session once per session' -Test {
            Disconnect-RdpSession -SessionID 2, 3, 5 -Confirm:$false
            Should -Invoke -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -Times 3 -Exactly
            Should -Invoke -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -Times 3 -Exactly
        }
    }

    Context -Name 'When CIM operation fails' -Fixture {
        BeforeEach {
            # FIX: New-MockObject à la place de PSCustomObject
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                $exception = [Microsoft.Management.Infrastructure.CimException]::new('The WS-Management service cannot process the request.')
                throw $exception
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { return $script:mockSuccessResult }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should write error and not throw' -Test {
            { Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue } | Should -Not -Throw
        }

        It -Name 'Should clean up CIM session even when Get-CimInstance fails' -Test {
            Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue
            Should -Invoke -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -Times 1 -Exactly
        }

        It -Name 'Should not return result object when CIM operation fails' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue
            $result | Should -BeNullOrEmpty
        }
    }

    Context -Name 'When access is denied' -Fixture {
        BeforeEach {
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                throw [System.UnauthorizedAccessException]::new('Access denied')
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { return $script:mockSuccessResult }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should write error and not throw' -Test {
            { Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue } | Should -Not -Throw
        }

        It -Name 'Should not invoke Get-CimInstance when session creation fails' -Test {
            Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue
            Should -Invoke -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -Times 0 -Exactly
        }

        It -Name 'Should not return result object when access is denied' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false -ErrorAction SilentlyContinue
            $result | Should -BeNullOrEmpty
        }
    }

    Context -Name 'When using custom credential' -Fixture {
        BeforeEach {
            $secureString = New-Object System.Security.SecureString
            $script:testCredential = [PSCredential]::new('TestUser', $secureString)

            # FIX: New-MockObject à la place de PSCustomObject
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith { return $script:mockSuccessResult }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should pass credential to New-CimSession' -Test {
            Disconnect-RdpSession -SessionID 2 -ComputerName 'RemoteServer' -Credential $script:testCredential -Confirm:$false
            Should -Invoke -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -Times 1 -Exactly -ParameterFilter {
                $Credential -eq $script:testCredential -and
                $ComputerName -eq 'RemoteServer'
            }
        }
    }

    Context -Name 'When disconnect operation returns failure code' -Fixture {
        BeforeEach {
            # FIX: New-MockObject à la place de PSCustomObject
            Mock -CommandName 'New-CimSession' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimSession'
            }
            Mock -CommandName 'Get-CimInstance' -ModuleName 'PSWinOps' -MockWith {
                New-MockObject -Type 'Microsoft.Management.Infrastructure.CimInstance'
            }
            Mock -CommandName 'Invoke-CimMethod' -ModuleName 'PSWinOps' -MockWith {
                [PSCustomObject]@{ ReturnValue = 1 }
            }
            Mock -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -MockWith {}
        }

        It -Name 'Should return result with Success set to false' -Test {
            $result = Disconnect-RdpSession -SessionID 2 -Confirm:$false
            $result.Success | Should -Be $false
            $result.ReturnCode | Should -Be 1
        }

        It -Name 'Should still clean up CIM session after failure' -Test {
            Disconnect-RdpSession -SessionID 2 -Confirm:$false
            Should -Invoke -CommandName 'Remove-CimSession' -ModuleName 'PSWinOps' -Times 1 -Exactly
        }
    }
}