|
| 1 | +#requires -Module @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } |
| 2 | + |
| 3 | +BeforeDiscovery { |
| 4 | + Import-Module (Join-Path $PSScriptRoot 'Shared/TestHelpers.psm1') -Force |
| 5 | + $script:SkipUnsupported = -not (Test-PSDependTypeSupportedHere -DependencyType 'WindowsRSAT') |
| 6 | +} |
| 7 | + |
| 8 | +BeforeAll { |
| 9 | + if (-not $env:BHProjectPath) { |
| 10 | + & "$PSScriptRoot\..\build.ps1" -Task 'Build' |
| 11 | + } |
| 12 | + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue |
| 13 | + Import-Module (Join-Path $env:BHProjectPath $env:BHProjectName) -Force |
| 14 | + |
| 15 | + Import-Module (Join-Path $PSScriptRoot 'Shared/TestHelpers.psm1') -Force |
| 16 | + |
| 17 | + $script:ScriptPath = Join-Path $env:BHProjectPath 'PSDepend/PSDependScripts/WindowsRSAT.ps1' |
| 18 | + |
| 19 | + # Install-WindowsFeature ships only on Windows Server (ServerManager module), |
| 20 | + # and Add-WindowsCapability requires Windows. Inject stubs into the PSDepend |
| 21 | + # module scope so Mock has a command to attach to on hosts that don't ship |
| 22 | + # the real cmdlets (e.g. Windows client when testing the Server dispatch path). |
| 23 | + InModuleScope PSDepend { |
| 24 | + if (-not (Get-Command -Name Install-WindowsFeature -ErrorAction SilentlyContinue)) { |
| 25 | + function script:Install-WindowsFeature { [CmdletBinding()] param([string]$Name) } |
| 26 | + } |
| 27 | + if (-not (Get-Command -Name Add-WindowsCapability -ErrorAction SilentlyContinue)) { |
| 28 | + function script:Add-WindowsCapability { [CmdletBinding()] param([switch]$Online, [string]$Name) } |
| 29 | + } |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +Describe 'WindowsRSAT script' -Tag 'WindowsOnly' -Skip:$SkipUnsupported { |
| 34 | + |
| 35 | + BeforeAll { |
| 36 | + InModuleScope PSDepend { |
| 37 | + Mock Get-Module { } -ParameterFilter { $ListAvailable } |
| 38 | + Mock Install-WindowsFeature { } |
| 39 | + Mock Add-WindowsCapability { } |
| 40 | + Mock Get-CimInstance { [PSCustomObject]@{ ProductType = 3 } } -ParameterFilter { $ClassName -eq 'Win32_OperatingSystem' } |
| 41 | + Mock Import-PSDependModule { } |
| 42 | + Mock Test-Administrator { $true } |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + Context 'PSDependAction = Test only' { |
| 47 | + It 'Returns $false when the module is not installed' { |
| 48 | + $dep = New-PSDependFixture -DependencyName 'ActiveDirectory' -DependencyType 'WindowsRSAT' |
| 49 | + $result = InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 50 | + & $ScriptPath -Dependency $Dep -PSDependAction Test |
| 51 | + } |
| 52 | + $result | Should -Be $false |
| 53 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 0 |
| 54 | + Should -Invoke -CommandName Add-WindowsCapability -ModuleName PSDepend -Times 0 |
| 55 | + } |
| 56 | + |
| 57 | + It 'Returns $true when the module is already available' { |
| 58 | + InModuleScope PSDepend { |
| 59 | + Mock Get-Module { [PSCustomObject]@{ Name = 'ActiveDirectory' } } -ParameterFilter { $ListAvailable } |
| 60 | + } |
| 61 | + $dep = New-PSDependFixture -DependencyName 'ActiveDirectory' -DependencyType 'WindowsRSAT' |
| 62 | + $result = InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 63 | + & $ScriptPath -Dependency $Dep -PSDependAction Test |
| 64 | + } |
| 65 | + $result | Should -Be $true |
| 66 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 0 |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + Context 'PSDependAction = Install on Server' { |
| 71 | + |
| 72 | + It 'Dispatches to Install-WindowsFeature with the mapped name (<ModuleName> -> <Feature>)' -TestCases @( |
| 73 | + @{ ModuleName = 'ActiveDirectory'; Feature = 'RSAT-AD-Powershell' } |
| 74 | + @{ ModuleName = 'BitLocker'; Feature = 'RSAT-Feature-Tools-BitLocker-RemoteAdminTool' } |
| 75 | + @{ ModuleName = 'Hyper-V'; Feature = 'RSAT-Hyper-V-Tools' } |
| 76 | + @{ ModuleName = 'GroupPolicy'; Feature = 'GPMC' } |
| 77 | + ) { |
| 78 | + param($ModuleName, $Feature) |
| 79 | + |
| 80 | + $dep = New-PSDependFixture -DependencyName $ModuleName -DependencyType 'WindowsRSAT' |
| 81 | + InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 82 | + & $ScriptPath -Dependency $Dep -PSDependAction Install |
| 83 | + } |
| 84 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 1 -Exactly -ParameterFilter { |
| 85 | + $Name -eq $Feature |
| 86 | + } |
| 87 | + Should -Invoke -CommandName Add-WindowsCapability -ModuleName PSDepend -Times 0 |
| 88 | + } |
| 89 | + |
| 90 | + It 'Throws when the module name is not in the mapping table' { |
| 91 | + $dep = New-PSDependFixture -DependencyName 'NotARealModule' -DependencyType 'WindowsRSAT' |
| 92 | + { |
| 93 | + InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 94 | + & $ScriptPath -Dependency $Dep -PSDependAction Install |
| 95 | + } |
| 96 | + } | Should -Throw '*Unknown Module*' |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + Context 'PSDependAction = Install on Workstation' { |
| 101 | + |
| 102 | + BeforeAll { |
| 103 | + InModuleScope PSDepend { |
| 104 | + Mock Get-CimInstance { [PSCustomObject]@{ ProductType = 1 } } -ParameterFilter { $ClassName -eq 'Win32_OperatingSystem' } |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + It 'Dispatches to Add-WindowsCapability with the mapped name (<ModuleName> -> <Capability>)' -TestCases @( |
| 109 | + @{ ModuleName = 'ActiveDirectory'; Capability = 'Rsat.ActiveDirectory.DS-LDS.Tools' } |
| 110 | + @{ ModuleName = 'BitLocker'; Capability = 'Rsat.BitLocker.Recovery.Tools' } |
| 111 | + @{ ModuleName = 'GroupPolicy'; Capability = 'Rsat.GroupPolicy.Management.Tools' } |
| 112 | + ) { |
| 113 | + param($ModuleName, $Capability) |
| 114 | + |
| 115 | + $dep = New-PSDependFixture -DependencyName $ModuleName -DependencyType 'WindowsRSAT' |
| 116 | + InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 117 | + & $ScriptPath -Dependency $Dep -PSDependAction Install |
| 118 | + } |
| 119 | + Should -Invoke -CommandName Add-WindowsCapability -ModuleName PSDepend -Times 1 -Exactly -ParameterFilter { |
| 120 | + $Name -eq $Capability |
| 121 | + } |
| 122 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 0 |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + Context 'PSDependAction = Install gated by admin check' { |
| 127 | + It 'Throws when Test-Administrator returns $false' { |
| 128 | + InModuleScope PSDepend { |
| 129 | + Mock Test-Administrator { $false } |
| 130 | + } |
| 131 | + $dep = New-PSDependFixture -DependencyName 'ActiveDirectory' -DependencyType 'WindowsRSAT' |
| 132 | + { |
| 133 | + InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 134 | + & $ScriptPath -Dependency $Dep -PSDependAction Install |
| 135 | + } |
| 136 | + } | Should -Throw '*admin*' |
| 137 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 0 |
| 138 | + Should -Invoke -CommandName Add-WindowsCapability -ModuleName PSDepend -Times 0 |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + Context 'PSDependAction = Test, Install short-circuits when installed' { |
| 143 | + It 'Skips Install when the module is already available' { |
| 144 | + InModuleScope PSDepend { |
| 145 | + Mock Get-Module { [PSCustomObject]@{ Name = 'ActiveDirectory' } } -ParameterFilter { $ListAvailable } |
| 146 | + } |
| 147 | + $dep = New-PSDependFixture -DependencyName 'ActiveDirectory' -DependencyType 'WindowsRSAT' |
| 148 | + InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } { |
| 149 | + & $ScriptPath -Dependency $Dep -PSDependAction Test, Install |
| 150 | + } |
| 151 | + Should -Invoke -CommandName Install-WindowsFeature -ModuleName PSDepend -Times 0 |
| 152 | + Should -Invoke -CommandName Add-WindowsCapability -ModuleName PSDepend -Times 0 |
| 153 | + } |
| 154 | + } |
| 155 | +} |
0 commit comments