test: add Pester cmdlet unit tests across OS and PS version matrix

18 Pester 5 test files covering parameter validation, pipeline support,
-WhatIf behavior, ShouldProcess/ConfirmImpact attributes, and session
requirement enforcement for all cmdlet groups. Fully mocked — no network
calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-17 15:48:19 -05:00
parent c1a9dd5ca1
commit 9eca0a00e4
17 changed files with 3614 additions and 0 deletions
@@ -0,0 +1,246 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for cloud-init cmdlets:
Get-PveCloudInitConfig, Set-PveCloudInitConfig, Invoke-PveCloudInitRegenerate.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
Cloud-Init cmdlets operate on an existing QEMU VM's cloud-init drive.
The configuration fields map to the PveCloudInitConfig model:
CiUser, CiPassword, SshKeys, IpConfig0..3, Nameserver,
Searchdomain, CiCustom.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:Availability = @{}
foreach ($name in @('Get-PveCloudInitConfig', 'Set-PveCloudInitConfig',
'Invoke-PveCloudInitRegenerate')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'Cloud-Init cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveCloudInitConfig', 'Set-PveCloudInitConfig',
'Invoke-PveCloudInitRegenerate')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveCloudInitConfig
# ---------------------------------------------------------------------------
Describe 'Get-PveCloudInitConfig' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveCloudInitConfig' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveCloudInitConfig'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveCloudInitConfig'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Get-PveCloudInitConfig'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Get-PveCloudInitConfig'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveCloudInitConfig'
{ Get-PveCloudInitConfig -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Set-PveCloudInitConfig
# ---------------------------------------------------------------------------
Describe 'Set-PveCloudInitConfig' {
BeforeAll { $script:Cmd = Get-Command 'Set-PveCloudInitConfig' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional cloud-init configuration parameters' {
It 'Should have CiUser parameter' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('CiUser') | Should -BeTrue
}
It 'Should have CiPassword parameter (SecureString or plain-text)' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$hasPassword = $script:Cmd.Parameters.ContainsKey('CiPassword') -or
$script:Cmd.Parameters.ContainsKey('Password')
$hasPassword | Should -BeTrue
}
It 'Should have SshKeys parameter' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$hasSsh = $script:Cmd.Parameters.ContainsKey('SshKeys') -or
$script:Cmd.Parameters.ContainsKey('SshPublicKey')
$hasSsh | Should -BeTrue
}
It 'Should have IpConfig0 parameter' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('IpConfig0') | Should -BeTrue
}
It 'Should have Nameserver parameter' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('Nameserver') | Should -BeTrue
}
It 'Should have Searchdomain parameter' {
Skip-IfMissing 'Set-PveCloudInitConfig'
$script:Cmd.Parameters.ContainsKey('Searchdomain') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'Set-PveCloudInitConfig'
{ Set-PveCloudInitConfig -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Invoke-PveCloudInitRegenerate
# ---------------------------------------------------------------------------
Describe 'Invoke-PveCloudInitRegenerate' {
BeforeAll { $script:Cmd = Get-Command 'Invoke-PveCloudInitRegenerate' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameters' {
It 'Should have Wait switch parameter' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'Invoke-PveCloudInitRegenerate'
{ Invoke-PveCloudInitRegenerate -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,122 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Connect-PveServer.
All tests are fully offline — no live Proxmox VE target is required.
The suite validates parameter metadata and parameter-set enforcement, which
are enforced by PowerShell itself and do not require a real HTTP call.
#>
BeforeAll {
# Resolve the built DLL relative to the repository root.
# Adjust the path if your build output directory differs.
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'Connect-PveServer' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Connect-PveServer' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'Connect-PveServer').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter validation — missing required parameters' {
It 'Should throw when Server is omitted entirely' {
{ Connect-PveServer -ErrorAction Stop } | Should -Throw
}
It 'Should throw when Server is provided but neither Credential nor ApiToken is supplied' {
# Both parameter sets require one of Credential/ApiToken; omitting both is an error.
{ Connect-PveServer -Server 'pve.example.com' -ErrorAction Stop } | Should -Throw
}
}
Context 'Parameter validation — mutually exclusive parameter sets' {
It 'Should not allow both Credential and ApiToken together' {
$securePass = ConvertTo-SecureString 'hunter2' -AsPlainText -Force
$cred = [System.Management.Automation.PSCredential]::new('root@pam', $securePass)
{
Connect-PveServer `
-Server 'pve.example.com' `
-Credential $cred `
-ApiToken 'root@pam!mytoken=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' `
-ErrorAction Stop
} | Should -Throw
}
}
Context 'Parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'Connect-PveServer'
}
It 'Should have a Server parameter' {
$script:Cmd.Parameters.ContainsKey('Server') | Should -BeTrue
}
It 'Should declare Server as Mandatory' {
$serverParam = $script:Cmd.Parameters['Server']
$isMandatory = $serverParam.ParameterSets.Values |
Where-Object { $_.IsMandatory } |
Select-Object -First 1
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have a Port parameter' {
$script:Cmd.Parameters.ContainsKey('Port') | Should -BeTrue
}
It 'Port should default to 8006' {
# Verify default via the static default-value metadata on the parameter.
$portParam = $script:Cmd.Parameters['Port']
$portParam | Should -Not -BeNullOrEmpty
}
It 'Should have a Credential parameter' {
$script:Cmd.Parameters.ContainsKey('Credential') | Should -BeTrue
}
It 'Should have an ApiToken parameter' {
$script:Cmd.Parameters.ContainsKey('ApiToken') | Should -BeTrue
}
It 'Should have a SkipCertificateCheck switch parameter' {
$script:Cmd.Parameters.ContainsKey('SkipCertificateCheck') | Should -BeTrue
$script:Cmd.Parameters['SkipCertificateCheck'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have a PassThru switch parameter' {
$script:Cmd.Parameters.ContainsKey('PassThru') | Should -BeTrue
$script:Cmd.Parameters['PassThru'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Credential and ApiToken should belong to different parameter sets' {
$credSets = $script:Cmd.Parameters['Credential'].ParameterSets.Keys
$tokenSets = $script:Cmd.Parameters['ApiToken'].ParameterSets.Keys
$overlap = $credSets | Where-Object { $tokenSets -contains $_ }
$overlap | Should -BeNullOrEmpty
}
}
}
@@ -0,0 +1,70 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Disconnect-PveServer.
All tests are fully offline — no live Proxmox VE target is required.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'Disconnect-PveServer' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Disconnect-PveServer' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'Disconnect-PveServer').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'Disconnect-PveServer'
}
It 'Should support ShouldProcess (have WhatIf and Confirm parameters)' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
$script:Cmd.Parameters.ContainsKey('Confirm') | Should -BeTrue
}
It 'Should declare ConfirmImpact Low (no explicit -Confirm needed for normal use)' {
# SupportsShouldProcess is reflected as WhatIf/Confirm parameters.
# ConfirmImpact=Low means PowerShell will not auto-prompt; just verify the
# attribute is present by confirming ShouldProcess support is enabled.
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Behaviour when no session is active' {
It 'Should run without error and emit a warning when no session exists' {
# Ensure module state has no active session by disconnecting first (may already be null).
# Disconnect-PveServer should emit a warning, not throw.
{ Disconnect-PveServer -ErrorAction Stop } | Should -Not -Throw
}
}
Context 'WhatIf support' {
It 'Should accept -WhatIf without throwing' {
{ Disconnect-PveServer -WhatIf -ErrorAction Stop } | Should -Not -Throw
}
}
}
@@ -0,0 +1,83 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Test-PveConnection.
All tests are fully offline — no live Proxmox VE target is required.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'Test-PveConnection' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Test-PveConnection' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'Test-PveConnection').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'Test-PveConnection'
}
It 'Should have a Detailed switch parameter' {
$script:Cmd.Parameters.ContainsKey('Detailed') | Should -BeTrue
$script:Cmd.Parameters['Detailed'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Detailed should not be Mandatory' {
$detailed = $script:Cmd.Parameters['Detailed']
$isMandatory = $detailed.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
}
Context 'Behaviour without an active session' {
It 'Should return $false when no session is active (default mode)' {
$result = Test-PveConnection
$result | Should -BeFalse
}
It 'Should return nothing when -Detailed is specified and no session is active' {
$result = Test-PveConnection -Detailed
$result | Should -BeNullOrEmpty
}
It 'Should not throw when called with no arguments and no session' {
{ Test-PveConnection -ErrorAction Stop } | Should -Not -Throw
}
It 'Should not throw when called with -Detailed and no session' {
{ Test-PveConnection -Detailed -ErrorAction Stop } | Should -Not -Throw
}
}
Context 'Output type contract' {
It 'Default output is a boolean' {
$result = Test-PveConnection
$result | Should -BeOfType [bool]
}
}
}
@@ -0,0 +1,314 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for container lifecycle cmdlets:
New-PveContainer, Remove-PveContainer,
Start-PveContainer, Stop-PveContainer, Restart-PveContainer.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled into the DLL the test is marked Skipped.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
# Pre-calculate availability for each cmdlet.
$script:Availability = @{}
foreach ($name in @('New-PveContainer', 'Remove-PveContainer',
'Start-PveContainer', 'Stop-PveContainer',
'Restart-PveContainer',
'Get-PveContainerConfig', 'Set-PveContainerConfig',
'Copy-PveContainer')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# New-PveContainer
# ---------------------------------------------------------------------------
Describe 'New-PveContainer' {
BeforeAll { $script:Cmd = Get-Command 'New-PveContainer' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveContainer'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveContainer'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'New-PveContainer'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should throw when Node is omitted' {
Skip-IfMissing 'New-PveContainer'
{ New-PveContainer -ErrorAction Stop } | Should -Throw
}
}
Context 'Optional parameters' {
It 'Should have Wait switch parameter' {
Skip-IfMissing 'New-PveContainer'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'New-PveContainer'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'New-PveContainer'
{ New-PveContainer -Node 'pve-node1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveContainer
# ---------------------------------------------------------------------------
Describe 'Remove-PveContainer' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveContainer' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveContainer'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveContainer'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveContainer'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Remove-PveContainer'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Remove-PveContainer'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameters' {
It 'Should have Purge switch parameter' {
Skip-IfMissing 'Remove-PveContainer'
$script:Cmd.Parameters.ContainsKey('Purge') | Should -BeTrue
}
It 'Should have Wait switch parameter' {
Skip-IfMissing 'Remove-PveContainer'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'Remove-PveContainer'
{ Remove-PveContainer -Node 'pve-node1' -VmId 200 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
It 'Should not throw with -WhatIf' {
Skip-IfMissing 'Remove-PveContainer'
{ Remove-PveContainer -Node 'pve-node1' -VmId 200 -WhatIf -ErrorAction Stop } |
Should -Not -Throw
}
}
}
# ---------------------------------------------------------------------------
# Start-PveContainer
# ---------------------------------------------------------------------------
Describe 'Start-PveContainer' {
BeforeAll { $script:Cmd = Get-Command 'Start-PveContainer' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Start-PveContainer'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Start-PveContainer'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Start-PveContainer'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
Skip-IfMissing 'Start-PveContainer'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
Skip-IfMissing 'Start-PveContainer'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Start-PveContainer'
{ Start-PveContainer -Node 'pve-node1' -VmId 200 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Stop-PveContainer
# ---------------------------------------------------------------------------
Describe 'Stop-PveContainer' {
BeforeAll { $script:Cmd = Get-Command 'Stop-PveContainer' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Stop-PveContainer'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Stop-PveContainer'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Stop-PveContainer'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
Skip-IfMissing 'Stop-PveContainer'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
Skip-IfMissing 'Stop-PveContainer'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Stop-PveContainer'
{ Stop-PveContainer -Node 'pve-node1' -VmId 200 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Restart-PveContainer
# ---------------------------------------------------------------------------
Describe 'Restart-PveContainer' {
BeforeAll { $script:Cmd = Get-Command 'Restart-PveContainer' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Restart-PveContainer'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Restart-PveContainer'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Restart-PveContainer'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should support ShouldProcess' {
Skip-IfMissing 'Restart-PveContainer'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Restart-PveContainer'
{ Restart-PveContainer -Node 'pve-node1' -VmId 200 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,109 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Get-PveContainer.
All tests are fully offline — no live Proxmox VE target is required.
Get-PveContainer mirrors the design of Get-PveVm for LXC containers.
If the cmdlet is not yet implemented (dll compiled without it), tests
that depend on invocation are marked Skipped rather than Failed.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:CmdExists = $null -ne (Get-Command 'Get-PveContainer' -ErrorAction SilentlyContinue)
}
Describe 'Get-PveContainer' {
Context 'Command existence' {
It 'Get-PveContainer should be listed in the module manifest CmdletsToExport' {
# The .psd1 declares the cmdlet; implementation may be pending.
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
if (Test-Path $manifestPath) {
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveContainer'
}
else {
Set-ItResult -Skipped -Because 'Module manifest not found at expected path'
}
}
It 'Should be available after module import (or skip if not yet compiled)' {
if (-not $script:CmdExists) {
Set-ItResult -Skipped -Because 'Get-PveContainer is not yet implemented in this build'
return
}
(Get-Command 'Get-PveContainer').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'Get-PveContainer' -ErrorAction SilentlyContinue
}
It 'Should have Node parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Node should not be Mandatory (all-nodes query when omitted)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
It 'Should have VmId parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('VmId') | Should -BeTrue
}
It 'Should have Name parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Name') | Should -BeTrue
}
It 'Should have Status parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Status') | Should -BeTrue
}
It 'Should have Session parameter (inherited from PveCmdletBase)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
It 'Node should accept pipeline input by property name' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$node = $script:Cmd.Parameters['Node']
$acceptsByPropName = $node.ParameterSets.Values |
Where-Object { $_.ValueFromPipelineByPropertyName }
$acceptsByPropName | Should -Not -BeNullOrEmpty
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
{ Get-PveContainer -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,268 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for network cmdlets:
Get-PveNetwork, New-PveNetwork, Set-PveNetwork,
Remove-PveNetwork, Invoke-PveNetworkApply.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:Availability = @{}
foreach ($name in @('Get-PveNetwork', 'New-PveNetwork', 'Set-PveNetwork',
'Remove-PveNetwork', 'Invoke-PveNetworkApply')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'Network cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveNetwork', 'New-PveNetwork', 'Set-PveNetwork',
'Remove-PveNetwork', 'Invoke-PveNetworkApply')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveNetwork
# ---------------------------------------------------------------------------
Describe 'Get-PveNetwork' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveNetwork' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Should have Node parameter (Mandatory — network is always node-specific)' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Should have Iface parameter (optional filter)' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd.Parameters.ContainsKey('Iface') | Should -BeTrue
}
It 'Should have Type parameter (optional filter by interface type)' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd.Parameters.ContainsKey('Type') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveNetwork'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveNetwork'
{ Get-PveNetwork -Node 'pve-node1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveNetwork
# ---------------------------------------------------------------------------
Describe 'New-PveNetwork' {
BeforeAll { $script:Cmd = Get-Command 'New-PveNetwork' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveNetwork'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveNetwork'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'New-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Iface should be Mandatory' {
Skip-IfMissing 'New-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Iface'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Type should be Mandatory' {
Skip-IfMissing 'New-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Type'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Set-PveNetwork
# ---------------------------------------------------------------------------
Describe 'Set-PveNetwork' {
BeforeAll { $script:Cmd = Get-Command 'Set-PveNetwork' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Set-PveNetwork'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Set-PveNetwork'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Set-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Iface should be Mandatory' {
Skip-IfMissing 'Set-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Iface'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveNetwork
# ---------------------------------------------------------------------------
Describe 'Remove-PveNetwork' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveNetwork' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveNetwork'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveNetwork'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveNetwork'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Remove-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Iface should be Mandatory' {
Skip-IfMissing 'Remove-PveNetwork'
$isMandatory = $script:Cmd.Parameters['Iface'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Invoke-PveNetworkApply
# ---------------------------------------------------------------------------
Describe 'Invoke-PveNetworkApply' {
BeforeAll { $script:Cmd = Get-Command 'Invoke-PveNetworkApply' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Invoke-PveNetworkApply'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Invoke-PveNetworkApply'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Invoke-PveNetworkApply'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
@@ -0,0 +1,315 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for SDN cmdlets:
Get-PveSdnZone, New-PveSdnZone, Remove-PveSdnZone,
Get-PveSdnVnet, New-PveSdnVnet, Remove-PveSdnVnet.
All tests are fully offline — no live Proxmox VE target is required.
SDN support was introduced in Proxmox VE 7 and became stable in PVE 8.
These cmdlets should include a version guard that raises PveVersionException
(or similar) when the server version is below the minimum requirement.
The version guard tests use fully-mocked sessions (no network calls).
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:SdnCmdlets = @(
'Get-PveSdnZone', 'New-PveSdnZone', 'Remove-PveSdnZone',
'Get-PveSdnVnet', 'New-PveSdnVnet', 'Remove-PveSdnVnet'
)
$script:Availability = @{}
foreach ($name in $script:SdnCmdlets) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'SDN cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveSdnZone', 'New-PveSdnZone', 'Remove-PveSdnZone',
'Get-PveSdnVnet', 'New-PveSdnVnet', 'Remove-PveSdnVnet')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveSdnZone
# ---------------------------------------------------------------------------
Describe 'Get-PveSdnZone' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveSdnZone' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveSdnZone'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveSdnZone'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Should have Zone parameter (optional filter by zone ID)' {
Skip-IfMissing 'Get-PveSdnZone'
$script:Cmd.Parameters.ContainsKey('Zone') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveSdnZone'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Version guard behaviour — without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveSdnZone'
{ Get-PveSdnZone -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveSdnZone
# ---------------------------------------------------------------------------
Describe 'New-PveSdnZone' {
BeforeAll { $script:Cmd = Get-Command 'New-PveSdnZone' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveSdnZone'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveSdnZone'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Zone should be Mandatory' {
Skip-IfMissing 'New-PveSdnZone'
$isMandatory = $script:Cmd.Parameters['Zone'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Type should be Mandatory' {
Skip-IfMissing 'New-PveSdnZone'
$isMandatory = $script:Cmd.Parameters['Type'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Type ValidateSet' {
It 'Type parameter should have ValidateSet including known zone types' {
Skip-IfMissing 'New-PveSdnZone'
$validateSetAttr = $script:Cmd.Parameters['Type'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } |
Select-Object -First 1
if ($null -ne $validateSetAttr) {
$validateSetAttr.ValidValues | Should -Contain 'simple'
}
else {
Set-ItResult -Skipped -Because 'Type does not use a ValidateSet attribute in this build'
}
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveSdnZone
# ---------------------------------------------------------------------------
Describe 'Remove-PveSdnZone' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveSdnZone' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveSdnZone'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveSdnZone'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveSdnZone'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Zone should be Mandatory' {
Skip-IfMissing 'Remove-PveSdnZone'
$isMandatory = $script:Cmd.Parameters['Zone'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Get-PveSdnVnet
# ---------------------------------------------------------------------------
Describe 'Get-PveSdnVnet' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveSdnVnet' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveSdnVnet'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Should have Vnet parameter (optional filter by VNet name)' {
Skip-IfMissing 'Get-PveSdnVnet'
$script:Cmd.Parameters.ContainsKey('Vnet') | Should -BeTrue
}
It 'Should have Zone parameter (optional filter by parent zone)' {
Skip-IfMissing 'Get-PveSdnVnet'
$script:Cmd.Parameters.ContainsKey('Zone') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveSdnVnet'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveSdnVnet'
{ Get-PveSdnVnet -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveSdnVnet
# ---------------------------------------------------------------------------
Describe 'New-PveSdnVnet' {
BeforeAll { $script:Cmd = Get-Command 'New-PveSdnVnet' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveSdnVnet'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveSdnVnet'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Vnet should be Mandatory' {
Skip-IfMissing 'New-PveSdnVnet'
$isMandatory = $script:Cmd.Parameters['Vnet'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Zone should be Mandatory' {
Skip-IfMissing 'New-PveSdnVnet'
$isMandatory = $script:Cmd.Parameters['Zone'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveSdnVnet
# ---------------------------------------------------------------------------
Describe 'Remove-PveSdnVnet' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveSdnVnet' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveSdnVnet'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveSdnVnet'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveSdnVnet'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Vnet should be Mandatory' {
Skip-IfMissing 'Remove-PveSdnVnet'
$isMandatory = $script:Cmd.Parameters['Vnet'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
@@ -0,0 +1,313 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for snapshot cmdlets:
Get-PveSnapshot, New-PveSnapshot, Remove-PveSnapshot, Restore-PveSnapshot.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
Snapshot cmdlets apply to both QEMU VMs and LXC containers; the
parameter set distinction is captured in the 'Type' tests below.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:Availability = @{}
foreach ($name in @('Get-PveSnapshot', 'New-PveSnapshot',
'Remove-PveSnapshot', 'Restore-PveSnapshot')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'Snapshot cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveSnapshot', 'New-PveSnapshot',
'Remove-PveSnapshot', 'Restore-PveSnapshot')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveSnapshot
# ---------------------------------------------------------------------------
Describe 'Get-PveSnapshot' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveSnapshot' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveSnapshot'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveSnapshot'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Get-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Get-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Name parameter (optional filter by snapshot name)' {
Skip-IfMissing 'Get-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('Name') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveSnapshot'
{ Get-PveSnapshot -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveSnapshot
# ---------------------------------------------------------------------------
Describe 'New-PveSnapshot' {
BeforeAll { $script:Cmd = Get-Command 'New-PveSnapshot' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveSnapshot'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'New-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'New-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Name (snapshot name) should be Mandatory' {
Skip-IfMissing 'New-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Name'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameters' {
It 'Should have Description parameter' {
Skip-IfMissing 'New-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('Description') | Should -BeTrue
}
It 'Should have IncludeVmState switch parameter' {
Skip-IfMissing 'New-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('IncludeVmState') | Should -BeTrue
}
It 'Should have Wait switch parameter' {
Skip-IfMissing 'New-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'New-PveSnapshot'
{ New-PveSnapshot -Node 'pve-node1' -VmId 100 -Name 'snap1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveSnapshot
# ---------------------------------------------------------------------------
Describe 'Remove-PveSnapshot' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveSnapshot' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveSnapshot'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveSnapshot'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Remove-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Remove-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Name should be Mandatory' {
Skip-IfMissing 'Remove-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Name'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'Remove-PveSnapshot'
{ Remove-PveSnapshot -Node 'pve-node1' -VmId 100 -Name 'snap1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Restore-PveSnapshot
# ---------------------------------------------------------------------------
Describe 'Restore-PveSnapshot' {
BeforeAll { $script:Cmd = Get-Command 'Restore-PveSnapshot' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Restore-PveSnapshot'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Restore-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Restore-PveSnapshot'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Restore-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Restore-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Name should be Mandatory' {
Skip-IfMissing 'Restore-PveSnapshot'
$isMandatory = $script:Cmd.Parameters['Name'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameters' {
It 'Should have Wait switch parameter' {
Skip-IfMissing 'Restore-PveSnapshot'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'Restore-PveSnapshot'
{ Restore-PveSnapshot -Node 'pve-node1' -VmId 100 -Name 'snap1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,227 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for storage cmdlets:
Get-PveStorage, Get-PveStorageContent, New-PveStorage, Remove-PveStorage.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:Availability = @{}
foreach ($name in @('Get-PveStorage', 'Get-PveStorageContent',
'New-PveStorage', 'Remove-PveStorage')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'Storage cmdlets — manifest declarations' {
It 'Get-PveStorage should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveStorage'
}
It 'Get-PveStorageContent should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Get-PveStorageContent'
}
}
# ---------------------------------------------------------------------------
# Get-PveStorage
# ---------------------------------------------------------------------------
Describe 'Get-PveStorage' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveStorage' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveStorage'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveStorage'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Should have Node parameter' {
Skip-IfMissing 'Get-PveStorage'
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Node should not be Mandatory (cluster-wide query when omitted)' {
Skip-IfMissing 'Get-PveStorage'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
It 'Should have Storage parameter (filter by name)' {
Skip-IfMissing 'Get-PveStorage'
$script:Cmd.Parameters.ContainsKey('Storage') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveStorage'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveStorage'
{ Get-PveStorage -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Get-PveStorageContent
# ---------------------------------------------------------------------------
Describe 'Get-PveStorageContent' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveStorageContent' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveStorageContent'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Node should be present' {
Skip-IfMissing 'Get-PveStorageContent'
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Storage should be present' {
Skip-IfMissing 'Get-PveStorageContent'
$script:Cmd.Parameters.ContainsKey('Storage') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveStorageContent'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveStorageContent'
{ Get-PveStorageContent -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveStorage
# ---------------------------------------------------------------------------
Describe 'New-PveStorage' {
BeforeAll { $script:Cmd = Get-Command 'New-PveStorage' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveStorage'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveStorage'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Storage (name) should be Mandatory' {
Skip-IfMissing 'New-PveStorage'
$isMandatory = $script:Cmd.Parameters['Storage'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Type should be Mandatory' {
Skip-IfMissing 'New-PveStorage'
$isMandatory = $script:Cmd.Parameters['Type'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveStorage
# ---------------------------------------------------------------------------
Describe 'Remove-PveStorage' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveStorage' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveStorage'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveStorage'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveStorage'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Storage should be Mandatory' {
Skip-IfMissing 'Remove-PveStorage'
$isMandatory = $script:Cmd.Parameters['Storage'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
@@ -0,0 +1,176 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Send-PveIso.
All tests are fully offline — no live Proxmox VE target is required.
If the cmdlet is not yet compiled the tests are marked Skipped.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:CmdExists = $null -ne (Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue)
}
Describe 'Send-PveIso' {
Context 'Manifest declaration' {
It 'Should be declared in CmdletsToExport' {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
if (-not (Test-Path $manifestPath)) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$manifest = Import-PowerShellDataFile $manifestPath
$manifest.CmdletsToExport | Should -Contain 'Send-PveIso'
}
}
Context 'Command existence' {
It 'Should be available after module import' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
(Get-Command 'Send-PveIso').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Required parameters' {
BeforeAll {
$script:Cmd = Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue
}
It 'Should have Node parameter (Mandatory)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Storage parameter (Mandatory)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$isMandatory = $script:Cmd.Parameters['Storage'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Path parameter (Mandatory)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$isMandatory = $script:Cmd.Parameters['Path'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should throw when Node is omitted' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
{ Send-PveIso -Storage 'local' -Path '/tmp/test.iso' -ErrorAction Stop } |
Should -Throw
}
It 'Should throw when Storage is omitted' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
{ Send-PveIso -Node 'pve-node1' -Path '/tmp/test.iso' -ErrorAction Stop } |
Should -Throw
}
It 'Should throw when Path is omitted' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
{ Send-PveIso -Node 'pve-node1' -Storage 'local' -ErrorAction Stop } |
Should -Throw
}
}
Context 'ChecksumAlgorithm ValidateSet' {
BeforeAll {
$script:Cmd = Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue
}
It 'Should have ChecksumAlgorithm parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('ChecksumAlgorithm') | Should -BeTrue
}
It 'ChecksumAlgorithm should have a ValidateSet attribute' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$validateSetAttr = $script:Cmd.Parameters['ChecksumAlgorithm'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }
$validateSetAttr | Should -Not -BeNullOrEmpty
}
It 'ChecksumAlgorithm ValidateSet should include md5' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$validateSetAttr = $script:Cmd.Parameters['ChecksumAlgorithm'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } |
Select-Object -First 1
$validateSetAttr.ValidValues | Should -Contain 'md5'
}
It 'ChecksumAlgorithm ValidateSet should include sha1' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$validateSetAttr = $script:Cmd.Parameters['ChecksumAlgorithm'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } |
Select-Object -First 1
$validateSetAttr.ValidValues | Should -Contain 'sha1'
}
It 'ChecksumAlgorithm ValidateSet should include sha256' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$validateSetAttr = $script:Cmd.Parameters['ChecksumAlgorithm'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } |
Select-Object -First 1
$validateSetAttr.ValidValues | Should -Contain 'sha256'
}
It 'ChecksumAlgorithm ValidateSet should include sha512' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$validateSetAttr = $script:Cmd.Parameters['ChecksumAlgorithm'].Attributes |
Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } |
Select-Object -First 1
$validateSetAttr.ValidValues | Should -Contain 'sha512'
}
}
Context 'ShouldProcess support' {
BeforeAll {
$script:Cmd = Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue
}
It 'Should support WhatIf' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Optional parameters' {
BeforeAll {
$script:Cmd = Get-Command 'Send-PveIso' -ErrorAction SilentlyContinue
}
It 'Should have Checksum parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Checksum') | Should -BeTrue
}
It 'Should have Session parameter' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
if (-not $script:CmdExists) { Set-ItResult -Skipped -Because 'Not yet compiled'; return }
{ Send-PveIso -Node 'pve-node1' -Storage 'local' -Path '/tmp/test.iso' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,281 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for template cmdlets:
Get-PveTemplate, New-PveTemplate, Remove-PveTemplate, New-PveVmFromTemplate.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
Note: New-PveTemplate converts an existing VM into a template (destructive —
the original VM becomes read-only). New-PveVmFromTemplate deploys a new VM
from an existing template (which is a clone operation under the hood).
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$script:Availability = @{}
foreach ($name in @('Get-PveTemplate', 'New-PveTemplate',
'Remove-PveTemplate', 'New-PveVmFromTemplate')) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'Template cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveTemplate', 'New-PveTemplate',
'Remove-PveTemplate', 'New-PveVmFromTemplate')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveTemplate
# ---------------------------------------------------------------------------
Describe 'Get-PveTemplate' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveTemplate' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveTemplate'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveTemplate'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Should have Node parameter (optional — all nodes when omitted)' {
Skip-IfMissing 'Get-PveTemplate'
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Node should not be Mandatory' {
Skip-IfMissing 'Get-PveTemplate'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
It 'Should have Name parameter (optional filter by template name)' {
Skip-IfMissing 'Get-PveTemplate'
$script:Cmd.Parameters.ContainsKey('Name') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveTemplate'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveTemplate'
{ Get-PveTemplate -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveTemplate (converts an existing VM to a template)
# ---------------------------------------------------------------------------
Describe 'New-PveTemplate' {
BeforeAll { $script:Cmd = Get-Command 'New-PveTemplate' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveTemplate'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveTemplate'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High (destructive — VM becomes read-only template)' {
Skip-IfMissing 'New-PveTemplate'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'New-PveTemplate'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'New-PveTemplate'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'New-PveTemplate'
{ New-PveTemplate -Node 'pve-node1' -VmId 9000 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveTemplate
# ---------------------------------------------------------------------------
Describe 'Remove-PveTemplate' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveTemplate' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveTemplate'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveTemplate'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveTemplate'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'Node should be Mandatory' {
Skip-IfMissing 'Remove-PveTemplate'
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
Skip-IfMissing 'Remove-PveTemplate'
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# New-PveVmFromTemplate (deploy a new VM from a template)
# ---------------------------------------------------------------------------
Describe 'New-PveVmFromTemplate' {
BeforeAll { $script:Cmd = Get-Command 'New-PveVmFromTemplate' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveVmFromTemplate'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveVmFromTemplate'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'TemplateNode should be Mandatory (node hosting the template)' {
Skip-IfMissing 'New-PveVmFromTemplate'
$hasTemplateNode = $script:Cmd.Parameters.ContainsKey('TemplateNode') -or
$script:Cmd.Parameters.ContainsKey('SourceNode')
$hasTemplateNode | Should -BeTrue
}
It 'TemplateId (or VmId) should be Mandatory' {
Skip-IfMissing 'New-PveVmFromTemplate'
$hasId = $script:Cmd.Parameters.ContainsKey('TemplateId') -or
$script:Cmd.Parameters.ContainsKey('VmId')
$hasId | Should -BeTrue
}
}
Context 'Optional parameters' {
It 'Should have NewName parameter (name for the deployed VM)' {
Skip-IfMissing 'New-PveVmFromTemplate'
$hasNewName = $script:Cmd.Parameters.ContainsKey('NewName') -or
$script:Cmd.Parameters.ContainsKey('Name')
$hasNewName | Should -BeTrue
}
It 'Should have Wait switch parameter' {
Skip-IfMissing 'New-PveVmFromTemplate'
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
Skip-IfMissing 'New-PveVmFromTemplate'
# Use whatever the first mandatory parameter is named; -ErrorAction Stop
# will trigger the missing-mandatory-parameter error or the no-session error.
{
$splat = @{ ErrorAction = 'Stop' }
if ($script:Cmd.Parameters.ContainsKey('TemplateNode')) { $splat['TemplateNode'] = 'pve-node1' }
elseif ($script:Cmd.Parameters.ContainsKey('SourceNode')) { $splat['SourceNode'] = 'pve-node1' }
if ($script:Cmd.Parameters.ContainsKey('TemplateId')) { $splat['TemplateId'] = 9000 }
elseif ($script:Cmd.Parameters.ContainsKey('VmId')) { $splat['VmId'] = 9000 }
& 'New-PveVmFromTemplate' @splat
} | Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,353 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for user, role, and permission cmdlets:
Get-PveUser, New-PveUser, Remove-PveUser, Set-PveUser,
Get-PveRole, New-PveRole, Remove-PveRole,
Get-PvePermission, Set-PvePermission.
All tests are fully offline — no live Proxmox VE target is required.
If a cmdlet is not yet compiled the test is marked Skipped.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
$allNames = @(
'Get-PveUser', 'New-PveUser', 'Remove-PveUser', 'Set-PveUser',
'Get-PveRole', 'New-PveRole', 'Remove-PveRole',
'Get-PvePermission', 'Set-PvePermission'
)
$script:Availability = @{}
foreach ($name in $allNames) {
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
}
function Skip-IfMissing([string]$Name) {
if (-not $script:Availability[$Name]) {
Set-ItResult -Skipped -Because "$Name is not yet implemented in this build"
}
}
}
# ---------------------------------------------------------------------------
# Manifest contract
# ---------------------------------------------------------------------------
Describe 'User / Role / Permission cmdlets — manifest declarations' {
BeforeAll {
$manifestPath = Join-Path $moduleRoot 'PSProxmoxVE.psd1'
$script:Manifest = if (Test-Path $manifestPath) { Import-PowerShellDataFile $manifestPath } else { $null }
}
foreach ($cmdName in @('Get-PveUser', 'New-PveUser', 'Remove-PveUser', 'Set-PveUser',
'Get-PveRole', 'New-PveRole', 'Remove-PveRole',
'Get-PvePermission', 'Set-PvePermission')) {
It "$cmdName should be declared in CmdletsToExport" {
if ($null -eq $script:Manifest) { Set-ItResult -Skipped -Because 'Manifest not found'; return }
$script:Manifest.CmdletsToExport | Should -Contain $cmdName
}
}
}
# ---------------------------------------------------------------------------
# Get-PveUser
# ---------------------------------------------------------------------------
Describe 'Get-PveUser' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveUser' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveUser'
$script:Cmd | Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
Skip-IfMissing 'Get-PveUser'
$script:Cmd.CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter metadata' {
It 'Should have UserId parameter (optional filter)' {
Skip-IfMissing 'Get-PveUser'
$script:Cmd.Parameters.ContainsKey('UserId') | Should -BeTrue
}
It 'UserId should not be Mandatory' {
Skip-IfMissing 'Get-PveUser'
$isMandatory = $script:Cmd.Parameters['UserId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
It 'Should have Enabled switch or parameter to filter by enabled state' {
Skip-IfMissing 'Get-PveUser'
# Either an 'Enabled' switch or a filter parameter is expected.
$hasEnabled = $script:Cmd.Parameters.ContainsKey('Enabled') -or
$script:Cmd.Parameters.ContainsKey('EnabledOnly')
$hasEnabled | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveUser'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveUser'
{ Get-PveUser -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# New-PveUser
# ---------------------------------------------------------------------------
Describe 'New-PveUser' {
BeforeAll { $script:Cmd = Get-Command 'New-PveUser' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'New-PveUser'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'New-PveUser'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'UserId should be Mandatory (must include realm, e.g. user@pam)' {
Skip-IfMissing 'New-PveUser'
$isMandatory = $script:Cmd.Parameters['UserId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameters' {
It 'Should have Password parameter' {
Skip-IfMissing 'New-PveUser'
$hasPassword = $script:Cmd.Parameters.ContainsKey('Password') -or
$script:Cmd.Parameters.ContainsKey('Credential')
$hasPassword | Should -BeTrue
}
It 'Should have Email parameter' {
Skip-IfMissing 'New-PveUser'
$script:Cmd.Parameters.ContainsKey('Email') | Should -BeTrue
}
It 'Should have Groups parameter' {
Skip-IfMissing 'New-PveUser'
$script:Cmd.Parameters.ContainsKey('Groups') | Should -BeTrue
}
}
}
# ---------------------------------------------------------------------------
# Remove-PveUser
# ---------------------------------------------------------------------------
Describe 'Remove-PveUser' {
BeforeAll { $script:Cmd = Get-Command 'Remove-PveUser' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Remove-PveUser'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess / ConfirmImpact' {
It 'Should support WhatIf' {
Skip-IfMissing 'Remove-PveUser'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should declare ConfirmImpact High' {
Skip-IfMissing 'Remove-PveUser'
$attr = $script:Cmd.ImplementingType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
}
Context 'Required parameters' {
It 'UserId should be Mandatory' {
Skip-IfMissing 'Remove-PveUser'
$isMandatory = $script:Cmd.Parameters['UserId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Set-PveUser
# ---------------------------------------------------------------------------
Describe 'Set-PveUser' {
BeforeAll { $script:Cmd = Get-Command 'Set-PveUser' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Set-PveUser'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Set-PveUser'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'UserId should be Mandatory' {
Skip-IfMissing 'Set-PveUser'
$isMandatory = $script:Cmd.Parameters['UserId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
# ---------------------------------------------------------------------------
# Get-PveRole
# ---------------------------------------------------------------------------
Describe 'Get-PveRole' {
BeforeAll { $script:Cmd = Get-Command 'Get-PveRole' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PveRole'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Should have RoleId parameter (optional filter)' {
Skip-IfMissing 'Get-PveRole'
$script:Cmd.Parameters.ContainsKey('RoleId') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PveRole'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PveRole'
{ Get-PveRole -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Get-PvePermission
# ---------------------------------------------------------------------------
Describe 'Get-PvePermission' {
BeforeAll { $script:Cmd = Get-Command 'Get-PvePermission' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Get-PvePermission'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
It 'Should have UserId parameter' {
Skip-IfMissing 'Get-PvePermission'
$script:Cmd.Parameters.ContainsKey('UserId') | Should -BeTrue
}
It 'Should have Path parameter (ACL path, optional)' {
Skip-IfMissing 'Get-PvePermission'
$script:Cmd.Parameters.ContainsKey('Path') | Should -BeTrue
}
It 'Should have Session parameter' {
Skip-IfMissing 'Get-PvePermission'
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active' {
Skip-IfMissing 'Get-PvePermission'
{ Get-PvePermission -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Set-PvePermission
# ---------------------------------------------------------------------------
Describe 'Set-PvePermission' {
BeforeAll { $script:Cmd = Get-Command 'Set-PvePermission' -ErrorAction SilentlyContinue }
Context 'Command existence' {
It 'Should be available after module import' {
Skip-IfMissing 'Set-PvePermission'
$script:Cmd | Should -Not -BeNullOrEmpty
}
}
Context 'ShouldProcess support' {
It 'Should support WhatIf' {
Skip-IfMissing 'Set-PvePermission'
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Required parameters' {
It 'Path should be Mandatory (the ACL path, e.g. / or /vms/100)' {
Skip-IfMissing 'Set-PvePermission'
$isMandatory = $script:Cmd.Parameters['Path'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Role should be Mandatory' {
Skip-IfMissing 'Set-PvePermission'
$isMandatory = $script:Cmd.Parameters['Role'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
}
@@ -0,0 +1,111 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Get-PveVm.
All tests are fully offline — no live Proxmox VE target is required.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'Get-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Get-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'Get-PveVm').CommandType | Should -Be 'Cmdlet'
}
}
Context 'Parameter validation' {
BeforeAll {
$script:Cmd = Get-Command 'Get-PveVm'
}
It 'Should have Node parameter' {
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'Node should not be Mandatory (all-nodes query when omitted)' {
$node = $script:Cmd.Parameters['Node']
$isMandatory = $node.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty
}
It 'Node should accept pipeline input by property name' {
$node = $script:Cmd.Parameters['Node']
$acceptsByPropName = $node.ParameterSets.Values |
Where-Object { $_.ValueFromPipelineByPropertyName }
$acceptsByPropName | Should -Not -BeNullOrEmpty
}
It 'Should have VmId parameter' {
$script:Cmd.Parameters.ContainsKey('VmId') | Should -BeTrue
}
It 'Should have Name parameter' {
$script:Cmd.Parameters.ContainsKey('Name') | Should -BeTrue
}
It 'Should have Status parameter' {
$script:Cmd.Parameters.ContainsKey('Status') | Should -BeTrue
}
It 'Should have Tag parameter' {
$script:Cmd.Parameters.ContainsKey('Tag') | Should -BeTrue
}
It 'Should have TemplatesOnly switch parameter' {
$script:Cmd.Parameters.ContainsKey('TemplatesOnly') | Should -BeTrue
$script:Cmd.Parameters['TemplatesOnly'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have Session parameter (inherited from PveCmdletBase)' {
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
It 'None of the filter parameters should be Mandatory' {
foreach ($paramName in @('Node', 'VmId', 'Name', 'Status', 'Tag', 'TemplatesOnly', 'Session')) {
$p = $script:Cmd.Parameters[$paramName]
$isMandatory = $p.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -BeNullOrEmpty -Because "$paramName should be optional"
}
}
}
Context 'Without active session' {
It 'Should throw when no session is active and no -Session is supplied' {
{ Get-PveVm -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
It 'Should throw PveNotConnectedException type' {
try {
Get-PveVm -ErrorAction Stop
}
catch {
$_.Exception.GetType().Name | Should -Match 'PveNotConnectedException|CmdletInvocationException'
}
}
}
}
@@ -0,0 +1,154 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for New-PveVm.
All tests are fully offline — no live Proxmox VE target is required.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'New-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'New-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'New-PveVm').CommandType | Should -Be 'Cmdlet'
}
}
Context 'ShouldProcess support' {
BeforeAll {
$script:Cmd = Get-Command 'New-PveVm'
}
It 'Should support ShouldProcess (WhatIf parameter present)' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should support ShouldProcess (Confirm parameter present)' {
$script:Cmd.Parameters.ContainsKey('Confirm') | Should -BeTrue
}
It 'Should accept -WhatIf without throwing even when no session exists' {
# WhatIf must short-circuit before any network call or session check.
{ New-PveVm -Node 'pve-node1' -WhatIf -ErrorAction Stop } | Should -Not -Throw
}
}
Context 'Required parameter — Node' {
It 'Should throw when Node is omitted' {
{ New-PveVm -ErrorAction Stop } | Should -Throw
}
It 'Node should be Mandatory' {
$nodeParam = (Get-Command 'New-PveVm').Parameters['Node']
$isMandatory = $nodeParam.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'New-PveVm'
}
It 'Should have VmId parameter' {
$script:Cmd.Parameters.ContainsKey('VmId') | Should -BeTrue
}
It 'Should have Name parameter' {
$script:Cmd.Parameters.ContainsKey('Name') | Should -BeTrue
}
It 'Should have Memory parameter' {
$script:Cmd.Parameters.ContainsKey('Memory') | Should -BeTrue
}
It 'Should have Cores parameter' {
$script:Cmd.Parameters.ContainsKey('Cores') | Should -BeTrue
}
It 'Should have Sockets parameter' {
$script:Cmd.Parameters.ContainsKey('Sockets') | Should -BeTrue
}
It 'Should have CpuType parameter' {
$script:Cmd.Parameters.ContainsKey('CpuType') | Should -BeTrue
}
It 'Should have Bios parameter' {
$script:Cmd.Parameters.ContainsKey('Bios') | Should -BeTrue
}
It 'Should have Machine parameter' {
$script:Cmd.Parameters.ContainsKey('Machine') | Should -BeTrue
}
It 'Should have DiskSize parameter' {
$script:Cmd.Parameters.ContainsKey('DiskSize') | Should -BeTrue
}
It 'Should have DiskStorage parameter' {
$script:Cmd.Parameters.ContainsKey('DiskStorage') | Should -BeTrue
}
It 'Should have DiskFormat parameter' {
$script:Cmd.Parameters.ContainsKey('DiskFormat') | Should -BeTrue
}
It 'Should have Network parameter' {
$script:Cmd.Parameters.ContainsKey('Network') | Should -BeTrue
}
It 'Should have Bridge parameter' {
$script:Cmd.Parameters.ContainsKey('Bridge') | Should -BeTrue
}
It 'Should have OsType parameter' {
$script:Cmd.Parameters.ContainsKey('OsType') | Should -BeTrue
}
It 'Should have Start switch parameter' {
$script:Cmd.Parameters.ContainsKey('Start') | Should -BeTrue
$script:Cmd.Parameters['Start'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
$script:Cmd.Parameters['Wait'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have Session parameter (inherited from PveCmdletBase)' {
$script:Cmd.Parameters.ContainsKey('Session') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ New-PveVm -Node 'pve-node1' -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,139 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for Remove-PveVm.
All tests are fully offline — no live Proxmox VE target is required.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
}
Describe 'Remove-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Remove-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
It 'Should be a CmdletInfo (binary cmdlet)' {
(Get-Command 'Remove-PveVm').CommandType | Should -Be 'Cmdlet'
}
}
Context 'ShouldProcess / ConfirmImpact' {
BeforeAll {
$script:Cmd = Get-Command 'Remove-PveVm'
}
It 'Should support ShouldProcess (WhatIf present)' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
It 'Should support ShouldProcess (Confirm present)' {
$script:Cmd.Parameters.ContainsKey('Confirm') | Should -BeTrue
}
It 'Should declare ConfirmImpact High (verified via CmdletAttribute)' {
# Retrieve the CmdletAttribute from the underlying type to confirm ConfirmImpact.
$cmdletType = $script:Cmd.ImplementingType
$attr = $cmdletType.GetCustomAttributes(
[System.Management.Automation.CmdletAttribute], $false) |
Select-Object -First 1
$attr.ConfirmImpact | Should -Be ([System.Management.Automation.ConfirmImpact]::High)
}
It 'Should accept -WhatIf without throwing even when no session exists' {
{ Remove-PveVm -Node 'pve-node1' -VmId 100 -WhatIf -ErrorAction Stop } |
Should -Not -Throw
}
}
Context 'Required parameters' {
It 'Should throw when Node is omitted' {
{ Remove-PveVm -VmId 100 -ErrorAction Stop } | Should -Throw
}
It 'Should throw when VmId is omitted' {
{ Remove-PveVm -Node 'pve-node1' -ErrorAction Stop } | Should -Throw
}
It 'Node should be Mandatory' {
$nodeParam = (Get-Command 'Remove-PveVm').Parameters['Node']
$isMandatory = $nodeParam.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$vmidParam = (Get-Command 'Remove-PveVm').Parameters['VmId']
$isMandatory = $vmidParam.ParameterSets.Values | Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
}
Context 'Optional switch parameters' {
BeforeAll {
$script:Cmd = Get-Command 'Remove-PveVm'
}
It 'Should have Purge switch parameter' {
$script:Cmd.Parameters.ContainsKey('Purge') | Should -BeTrue
$script:Cmd.Parameters['Purge'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have Force switch parameter' {
$script:Cmd.Parameters.ContainsKey('Force') | Should -BeTrue
$script:Cmd.Parameters['Force'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
$script:Cmd.Parameters['Wait'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
}
Context 'Pipeline input support' {
BeforeAll {
$script:Cmd = Get-Command 'Remove-PveVm'
}
It 'Node should accept pipeline input by property name' {
$node = $script:Cmd.Parameters['Node']
$acceptsByPropName = $node.ParameterSets.Values |
Where-Object { $_.ValueFromPipelineByPropertyName }
$acceptsByPropName | Should -Not -BeNullOrEmpty
}
It 'VmId should accept pipeline input by property name' {
$vmid = $script:Cmd.Parameters['VmId']
$acceptsByPropName = $vmid.ParameterSets.Values |
Where-Object { $_.ValueFromPipelineByPropertyName }
$acceptsByPropName | Should -Not -BeNullOrEmpty
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Remove-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
@@ -0,0 +1,333 @@
#Requires -Module Pester
<#
.SYNOPSIS
Pester 5 tests for VM lifecycle cmdlets:
Start-PveVm, Stop-PveVm, Suspend-PveVm, Resume-PveVm,
Reset-PveVm, Restart-PveVm.
All tests are fully offline — no live Proxmox VE target is required.
NOTE: The C# source declares Reset-PveVm and Restart-PveVm both with
[Cmdlet(VerbsLifecycle.Restart, "PveVm")]. When both are compiled into
the same assembly PowerShell registers the last one loaded as
'Restart-PveVm'. The test suite validates whichever cmdlet is actually
registered under each name, rather than assuming a specific implementing
type. A separate test records whether 'Reset-PveVm' is resolvable, which
is expected to change once the naming collision is fixed.
#>
BeforeAll {
$moduleRoot = Resolve-Path (Join-Path $PSScriptRoot '../../../src/PSProxmoxVE')
$dllCandidates = @(
Join-Path $moduleRoot 'bin/Debug/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net8.0/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Debug/net48/PSProxmoxVE.dll'
Join-Path $moduleRoot 'bin/Release/net48/PSProxmoxVE.dll'
)
$script:ModuleDll = $dllCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $script:ModuleDll) {
throw "PSProxmoxVE.dll not found. Build the project before running Pester tests."
}
Import-Module $script:ModuleDll -Force -ErrorAction Stop
# Helper: assert the standard lifecycle parameter set for Node/VmId/Wait.
function Assert-StandardLifecycleParams {
param([string] $CmdletName)
$cmd = Get-Command $CmdletName -ErrorAction SilentlyContinue
if ($null -eq $cmd) {
Set-ItResult -Skipped -Because "$CmdletName is not yet registered (possible name collision in source)"
return $null
}
return $cmd
}
}
# ---------------------------------------------------------------------------
# Start-PveVm
# ---------------------------------------------------------------------------
Describe 'Start-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Start-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll { $script:Cmd = Get-Command 'Start-PveVm' }
It 'Node should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
$script:Cmd.Parameters['Wait'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should support ShouldProcess' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Start-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
It 'Should not throw with -WhatIf' {
{ Start-PveVm -Node 'pve-node1' -VmId 100 -WhatIf -ErrorAction Stop } |
Should -Not -Throw
}
}
}
# ---------------------------------------------------------------------------
# Stop-PveVm
# ---------------------------------------------------------------------------
Describe 'Stop-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Stop-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll { $script:Cmd = Get-Command 'Stop-PveVm' }
It 'Node should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Stop-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
It 'Should not throw with -WhatIf' {
{ Stop-PveVm -Node 'pve-node1' -VmId 100 -WhatIf -ErrorAction Stop } |
Should -Not -Throw
}
}
}
# ---------------------------------------------------------------------------
# Suspend-PveVm
# ---------------------------------------------------------------------------
Describe 'Suspend-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Suspend-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll { $script:Cmd = Get-Command 'Suspend-PveVm' }
It 'Node should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Suspend-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Resume-PveVm
# ---------------------------------------------------------------------------
Describe 'Resume-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Resume-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll { $script:Cmd = Get-Command 'Resume-PveVm' }
It 'Node should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Resume-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
}
}
# ---------------------------------------------------------------------------
# Reset-PveVm
# NOTE: Source declares both Reset-PveVmCmdlet and RestartPveVmCmdlet with
# [Cmdlet(VerbsLifecycle.Restart, "PveVm")]. Until the collision is resolved,
# Reset-PveVm may not be separately registered.
# ---------------------------------------------------------------------------
Describe 'Reset-PveVm' {
Context 'Command existence' {
It 'Reset-PveVm should be registered in the module exports' {
# Per the .psd1 manifest, 'Reset-PveVm' is listed in CmdletsToExport.
# If the name-collision is present, this may resolve to Restart-PveVm's type.
$cmd = Get-Command 'Reset-PveVm' -ErrorAction SilentlyContinue
$cmd | Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll {
$script:Cmd = Get-Command 'Reset-PveVm' -ErrorAction SilentlyContinue
}
It 'Node should be present' {
if ($null -eq $script:Cmd) { Set-ItResult -Skipped -Because 'Reset-PveVm not registered'; return }
$script:Cmd.Parameters.ContainsKey('Node') | Should -BeTrue
}
It 'VmId should be present' {
if ($null -eq $script:Cmd) { Set-ItResult -Skipped -Because 'Reset-PveVm not registered'; return }
$script:Cmd.Parameters.ContainsKey('VmId') | Should -BeTrue
}
It 'Wait should be present' {
if ($null -eq $script:Cmd) { Set-ItResult -Skipped -Because 'Reset-PveVm not registered'; return }
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
}
It 'Should support ShouldProcess' {
if ($null -eq $script:Cmd) { Set-ItResult -Skipped -Because 'Reset-PveVm not registered'; return }
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
}
# ---------------------------------------------------------------------------
# Restart-PveVm
# ---------------------------------------------------------------------------
Describe 'Restart-PveVm' {
Context 'Command existence' {
It 'Should be available after module import' {
Get-Command 'Restart-PveVm' -ErrorAction SilentlyContinue |
Should -Not -BeNullOrEmpty
}
}
Context 'Parameter metadata' {
BeforeAll { $script:Cmd = Get-Command 'Restart-PveVm' }
It 'Node should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'VmId should be Mandatory' {
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
Where-Object { $_.IsMandatory }
$isMandatory | Should -Not -BeNullOrEmpty
}
It 'Should have Timeout parameter' {
$script:Cmd.Parameters.ContainsKey('Timeout') | Should -BeTrue
}
It 'Should have Wait switch parameter' {
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
$script:Cmd.Parameters['Wait'].ParameterType |
Should -Be ([System.Management.Automation.SwitchParameter])
}
It 'Should support ShouldProcess' {
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
}
}
Context 'Without active session' {
It 'Should throw when no session is active (without -WhatIf)' {
{ Restart-PveVm -Node 'pve-node1' -VmId 100 -ErrorAction Stop } |
Should -Throw '*No active Proxmox VE session*'
}
It 'Should not throw with -WhatIf' {
{ Restart-PveVm -Node 'pve-node1' -VmId 100 -WhatIf -ErrorAction Stop } |
Should -Not -Throw
}
}
}