Skip to content

Commit adc3f6a

Browse files
tablackburnclaude
andcommitted
test(WindowsRSAT): add Pester tests for dispatch and mapping
Adds Tests/WindowsRSAT.Type.Tests.ps1 with 12 tests covering: - Test-only action: returns $false when missing, $true when available - Install on Server (ProductType=3): dispatches to Install-WindowsFeature with the correct mapped name for ActiveDirectory (baseline) plus BitLocker, Hyper-V, and GroupPolicy/GPMC (the three Server-side fixes from the preceding commits) - Install on Workstation (ProductType=1): dispatches to Add-WindowsCapability with the correct mapped name - Unknown module name throws "Unknown Module" - Install gated by admin check: Test-Administrator -> $false throws - Test, Install short-circuits when the module is already available Test-PSDependTypeSupportedHere skips the entire Describe on non-Windows. The script's admin check is mocked via Test-Administrator (extracted in the preceding refactor commit) so all 12 tests run regardless of the test runner's elevation status. Install-WindowsFeature ships only on Windows Server (ServerManager module). The BeforeAll injects stub functions into the PSDepend module scope when the real cmdlets are missing, so Pester's Mock has a command to attach to on hosts that don't ship them (e.g. Win 11 client when testing the Server dispatch path locally). Verified locally on Win 11 (non-admin): 12 passed, 0 failed, 0 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2df5fb3 commit adc3f6a

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

Tests/WindowsRSAT.Type.Tests.ps1

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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

Comments
 (0)