mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-10 06:16:53 +00:00
feat(test): add PVE 8+9 matrix and expand integration test coverage
Workflow changes: - Convert to matrix strategy testing PVE 8.4 and 9.1 sequentially - Each version gets unique VMID (99908/99909) and VM name - Pass PVETEST_PVE_VERSION env var for version-specific assertions - Version-specific artifact names for test results New integration tests: - User CRUD: create, list, update (Set-PveUser), remove - API Token CRUD: create, list, remove (on dedicated test user) - Role CRUD: create with privileges, list, remove - Permissions: list and set (Get/Set-PvePermission) - VM config: get and set config, resize disk - VM lifecycle: restart, explicit remove - Snapshots: full lifecycle (create, list, restore, remove) - Storage: list content (Get-PveStorageContent) - Tasks: list recent tasks (Get-PveTask) - Version assertion: verify PVE major version matches expected Coverage improved from 15 to ~30 cmdlets tested against live API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
name: Integration Tests
|
||||
|
||||
# Real integration tests against a live Proxmox VE instance.
|
||||
# By default, provisions a throwaway nested PVE VM via Terraform, runs tests,
|
||||
# then destroys it. Can also run against a pre-existing PVE with skip_provision.
|
||||
# By default, provisions throwaway nested PVE VMs via Terraform for both
|
||||
# PVE 8 and PVE 9, runs tests against each, then destroys them.
|
||||
# Can also run against a pre-existing PVE with skip_provision.
|
||||
#
|
||||
# Required repository secrets (for provisioning):
|
||||
# PVE_ENDPOINT - Parent PVE API URL (e.g. https://pve.example.com:8006)
|
||||
@@ -13,8 +14,8 @@ name: Integration Tests
|
||||
# PVETEST_HOST - Hostname or IP of a pre-existing nested PVE
|
||||
# PVETEST_APITOKEN - API token for the pre-existing nested PVE
|
||||
#
|
||||
# WARNING: Integration tests create and destroy real VMs, upload files,
|
||||
# and modify network configuration. Never run against production.
|
||||
# WARNING: Integration tests create and destroy real resources (VMs, users,
|
||||
# tokens, network config, etc.) on the target node. Never run against production.
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -23,14 +24,6 @@ on:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pve_version:
|
||||
description: 'PVE version to test (8 or 9)'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- '9'
|
||||
- '8'
|
||||
default: '9'
|
||||
skip_provision:
|
||||
description: 'Skip provisioning (use existing PVE from PVETEST_HOST secret)'
|
||||
required: false
|
||||
@@ -46,6 +39,22 @@ jobs:
|
||||
integration:
|
||||
runs-on: [self-hosted, proxmox, integration]
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 1 # Single self-hosted runner — provision one at a time
|
||||
matrix:
|
||||
pve_version: ['9', '8']
|
||||
include:
|
||||
- pve_version: '9'
|
||||
iso_base: /opt/pve-isos/proxmox-ve_9.1-1.iso
|
||||
iso_filename: proxmox-ve_9.1-1-auto.iso
|
||||
vm_id: 99909
|
||||
vm_name: pve-test-pve9
|
||||
- pve_version: '8'
|
||||
iso_base: /opt/pve-isos/proxmox-ve_8.4-1.iso
|
||||
iso_filename: proxmox-ve_8.4-1-auto.iso
|
||||
vm_id: 99908
|
||||
vm_name: pve-test-pve8
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
@@ -55,24 +64,14 @@ jobs:
|
||||
- name: Pre-flight cleanup
|
||||
if: inputs.skip_provision != true
|
||||
shell: bash
|
||||
id: iso
|
||||
env:
|
||||
PVE_API_TOKEN: ${{ secrets.PVE_API_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ inputs.pve_version || '9' }}"
|
||||
if [ "$VERSION" = "9" ]; then
|
||||
echo "base=/opt/pve-isos/proxmox-ve_9.1-1.iso" >> "$GITHUB_OUTPUT"
|
||||
echo "filename=proxmox-ve_9.1-1-auto.iso" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "base=/opt/pve-isos/proxmox-ve_8.4-1.iso" >> "$GITHUB_OUTPUT"
|
||||
echo "filename=proxmox-ve_8.4-1-auto.iso" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
bash ${SCRIPTS_DIR}/preflight-cleanup.sh \
|
||||
"${{ secrets.PVE_ENDPOINT }}" \
|
||||
"${PVE_API_TOKEN}" \
|
||||
99900 \
|
||||
"auto.iso" \
|
||||
${{ matrix.vm_id }} \
|
||||
"${{ matrix.iso_filename }}" \
|
||||
"${INFRA_DIR}"
|
||||
|
||||
# ── Provision nested PVE ───────────────────────────────────────────
|
||||
@@ -91,10 +90,10 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
bash ${SCRIPTS_DIR}/prepare-auto-iso.sh \
|
||||
"${{ steps.iso.outputs.base }}" \
|
||||
"${{ matrix.iso_base }}" \
|
||||
${RUNNER_TEMP}/answer.toml \
|
||||
${SCRIPTS_DIR}/first-boot.sh \
|
||||
"${{ runner.temp }}/${{ steps.iso.outputs.filename }}"
|
||||
"${{ runner.temp }}/${{ matrix.iso_filename }}"
|
||||
|
||||
- name: Terraform init
|
||||
if: inputs.skip_provision != true
|
||||
@@ -111,8 +110,10 @@ jobs:
|
||||
TF_VAR_proxmox_endpoint: ${{ secrets.PVE_ENDPOINT }}
|
||||
TF_VAR_proxmox_api_token: ${{ secrets.PVE_API_TOKEN }}
|
||||
TF_VAR_target_node: ${{ secrets.PVE_TARGET_NODE }}
|
||||
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ steps.iso.outputs.filename }}
|
||||
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ matrix.iso_filename }}
|
||||
TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }}
|
||||
TF_VAR_vm_id: ${{ matrix.vm_id }}
|
||||
TF_VAR_vm_name: ${{ matrix.vm_name }}
|
||||
run: |
|
||||
terraform apply -auto-approve -input=false
|
||||
echo "vm_id=$(terraform output -raw pve_test_vm_id)" >> "$GITHUB_OUTPUT"
|
||||
@@ -135,7 +136,7 @@ jobs:
|
||||
# Mask sensitive values before they appear in logs
|
||||
echo "::add-mask::${VM_IP}"
|
||||
echo "::add-mask::${VM_TOKEN}"
|
||||
echo "Nested PVE ready at ${VM_IP}"
|
||||
echo "Nested PVE ${{ matrix.pve_version }} ready at ${VM_IP}"
|
||||
echo "host=${VM_IP}" >> "$GITHUB_OUTPUT"
|
||||
echo "token=${VM_TOKEN}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
@@ -165,7 +166,7 @@ jobs:
|
||||
HOST="${{ steps.target.outputs.host }}"
|
||||
PORT="${{ steps.target.outputs.port }}"
|
||||
TOKEN="${{ steps.target.outputs.token }}"
|
||||
echo "Testing connectivity to PVE API at ${HOST}:${PORT}..."
|
||||
echo "Testing connectivity to PVE ${{ matrix.pve_version }} API at ${HOST}:${PORT}..."
|
||||
if curl -sk --connect-timeout 10 \
|
||||
-H "Authorization: PVEAPIToken=${TOKEN}" \
|
||||
"https://${HOST}:${PORT}/api2/json/nodes" | grep -q '"node"'; then
|
||||
@@ -207,6 +208,7 @@ jobs:
|
||||
PVETEST_NODE: ${{ steps.target.outputs.node }}
|
||||
PVETEST_STORAGE: ${{ steps.target.outputs.storage }}
|
||||
PVETEST_ISO_PATH: ${{ runner.temp }}/pvetest.iso
|
||||
PVETEST_PVE_VERSION: ${{ matrix.pve_version }}
|
||||
run: |
|
||||
Import-Module Pester -MinimumVersion 5.0
|
||||
$config = New-PesterConfiguration
|
||||
@@ -222,7 +224,7 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: integration-test-results
|
||||
name: integration-test-results-pve${{ matrix.pve_version }}
|
||||
path: TestResults/
|
||||
|
||||
# ── Teardown ───────────────────────────────────────────────────────
|
||||
@@ -235,8 +237,10 @@ jobs:
|
||||
TF_VAR_proxmox_endpoint: ${{ secrets.PVE_ENDPOINT }}
|
||||
TF_VAR_proxmox_api_token: ${{ secrets.PVE_API_TOKEN }}
|
||||
TF_VAR_target_node: ${{ secrets.PVE_TARGET_NODE }}
|
||||
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ steps.iso.outputs.filename }}
|
||||
TF_VAR_iso_local_path: ${{ runner.temp }}/${{ matrix.iso_filename }}
|
||||
TF_VAR_test_vm_password: ${{ env.PVE_PASSWORD }}
|
||||
TF_VAR_vm_id: ${{ matrix.vm_id }}
|
||||
TF_VAR_vm_name: ${{ matrix.vm_name }}
|
||||
run: |
|
||||
terraform init -input=false
|
||||
terraform destroy -auto-approve -input=false || true
|
||||
@@ -251,6 +255,6 @@ jobs:
|
||||
bash ${SCRIPTS_DIR}/preflight-cleanup.sh \
|
||||
"${{ secrets.PVE_ENDPOINT }}" \
|
||||
"${PVE_API_TOKEN}" \
|
||||
99900 \
|
||||
"${{ steps.iso.outputs.filename }}" \
|
||||
${{ matrix.vm_id }} \
|
||||
"${{ matrix.iso_filename }}" \
|
||||
"${INFRA_DIR}" || true
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
PVETEST_NODE - PVE node name (e.g. pve-test1)
|
||||
PVETEST_STORAGE - Storage pool name for disk/ISO operations (e.g. local)
|
||||
PVETEST_ISO_PATH - Local filesystem path to a small .iso for upload tests
|
||||
PVETEST_PVE_VERSION - (optional) Expected PVE major version (8 or 9)
|
||||
|
||||
WARNING: These tests CREATE and DESTROY real resources (VMs, snapshots,
|
||||
ISO uploads, etc.) on the target node. Never run against a production cluster.
|
||||
WARNING: These tests CREATE and DESTROY real resources (VMs, users, tokens,
|
||||
roles, snapshots, ISO uploads, etc.) on the target node.
|
||||
Never run against a production cluster.
|
||||
|
||||
To run:
|
||||
Invoke-Pester -Path ./tests/PSProxmoxVE.Tests -Tag Integration
|
||||
@@ -61,23 +63,56 @@ BeforeAll {
|
||||
$script:Node = [System.Environment]::GetEnvironmentVariable('PVETEST_NODE')
|
||||
$script:Storage = [System.Environment]::GetEnvironmentVariable('PVETEST_STORAGE')
|
||||
$script:IsoPath = [System.Environment]::GetEnvironmentVariable('PVETEST_ISO_PATH')
|
||||
$script:ExpectedPveVersion = [System.Environment]::GetEnvironmentVariable('PVETEST_PVE_VERSION')
|
||||
|
||||
# Track resources created during the run so AfterAll can clean up.
|
||||
$script:CreatedVmIds = [System.Collections.Generic.List[int]]::new()
|
||||
$script:CreatedUsers = [System.Collections.Generic.List[string]]::new()
|
||||
$script:CreatedRoles = [System.Collections.Generic.List[string]]::new()
|
||||
$script:TestVmId = $null
|
||||
|
||||
# Helper functions for skip logic
|
||||
function script:Skip-IfNoTarget {
|
||||
if ($script:SkipReason) {
|
||||
Set-ItResult -Skipped -Because $script:SkipReason
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function script:Skip-IfNoTestVm {
|
||||
if (Skip-IfNoTarget) { return $true }
|
||||
if ($null -eq $script:TestVmId) {
|
||||
Set-ItResult -Skipped -Because 'No test VM was created'
|
||||
return $true
|
||||
}
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
# Best-effort cleanup — remove any VMs created during the test run.
|
||||
if ($null -eq $script:SkipReason -and $script:CreatedVmIds.Count -gt 0) {
|
||||
foreach ($vmId in $script:CreatedVmIds) {
|
||||
try {
|
||||
Stop-PveVm -Node $script:Node -VmId $vmId -ErrorAction SilentlyContinue | Out-Null
|
||||
Start-Sleep -Seconds 3
|
||||
Remove-PveVm -Node $script:Node -VmId $vmId -Force -Purge -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
catch { <# non-fatal #> }
|
||||
if ($null -ne $script:SkipReason) { return }
|
||||
|
||||
# Best-effort cleanup — remove resources created during the test run.
|
||||
# Order matters: tokens/permissions removed with users, VMs last.
|
||||
|
||||
foreach ($userId in $script:CreatedUsers) {
|
||||
try { Remove-PveUser -UserId $userId -Confirm:$false -ErrorAction SilentlyContinue }
|
||||
catch { <# non-fatal #> }
|
||||
}
|
||||
|
||||
foreach ($roleId in $script:CreatedRoles) {
|
||||
try { Remove-PveRole -RoleId $roleId -Confirm:$false -ErrorAction SilentlyContinue }
|
||||
catch { <# non-fatal #> }
|
||||
}
|
||||
|
||||
foreach ($vmId in $script:CreatedVmIds) {
|
||||
try {
|
||||
Stop-PveVm -Node $script:Node -VmId $vmId -ErrorAction SilentlyContinue | Out-Null
|
||||
Start-Sleep -Seconds 3
|
||||
Remove-PveVm -Node $script:Node -VmId $vmId -Force -Purge -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
catch { <# non-fatal #> }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +124,7 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Connection' {
|
||||
It 'Should connect to PVE server' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$session = Connect-PveServer `
|
||||
-Server $script:Host_ `
|
||||
@@ -106,26 +141,31 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
}
|
||||
|
||||
It 'Should detect server version' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$detail = Test-PveConnection -Detailed
|
||||
$detail | Should -Not -BeNullOrEmpty
|
||||
$detail.ServerVersion | Should -Not -BeNullOrEmpty
|
||||
$detail.ServerVersion.Major | Should -BeIn @(8, 9)
|
||||
|
||||
# If we know the expected version, verify it matches
|
||||
if ($script:ExpectedPveVersion) {
|
||||
$detail.ServerVersion.Major | Should -Be ([int]$script:ExpectedPveVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Nodes' {
|
||||
It 'Should list nodes' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$nodes = Get-PveNode
|
||||
$nodes | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should get node status' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$status = Get-PveNodeStatus -Node $script:Node
|
||||
$status | Should -Not -BeNullOrEmpty
|
||||
@@ -133,17 +173,161 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'User CRUD' {
|
||||
It 'Should create a new user' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ New-PveUser -UserId 'pester-user@pam' -ErrorAction Stop } |
|
||||
Should -Not -Throw
|
||||
|
||||
$script:CreatedUsers.Add('pester-user@pam')
|
||||
}
|
||||
|
||||
It 'Should list users and find the new user' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$users = Get-PveUser
|
||||
$users | Should -Not -BeNullOrEmpty
|
||||
$users | Where-Object { $_.UserId -eq 'pester-user@pam' } |
|
||||
Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should update user properties' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Set-PveUser -UserId 'pester-user@pam' `
|
||||
-Comment 'Updated by Pester integration test' `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
|
||||
$user = Get-PveUser | Where-Object { $_.UserId -eq 'pester-user@pam' }
|
||||
$user.Comment | Should -Be 'Updated by Pester integration test'
|
||||
}
|
||||
|
||||
It 'Should remove the user' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Remove-PveUser -UserId 'pester-user@pam' -Confirm:$false -ErrorAction Stop } |
|
||||
Should -Not -Throw
|
||||
|
||||
$script:CreatedUsers.Remove('pester-user@pam') | Out-Null
|
||||
|
||||
$users = Get-PveUser
|
||||
$users | Where-Object { $_.UserId -eq 'pester-user@pam' } |
|
||||
Should -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Role CRUD' {
|
||||
It 'Should create a new role' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ New-PveRole -RoleId 'PesterTestRole' `
|
||||
-Privileges @('VM.Audit', 'VM.Console') `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
|
||||
$script:CreatedRoles.Add('PesterTestRole')
|
||||
}
|
||||
|
||||
It 'Should list roles and find the new role' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$roles = Get-PveRole
|
||||
$roles | Should -Not -BeNullOrEmpty
|
||||
$roles | Where-Object { $_.RoleId -eq 'PesterTestRole' } |
|
||||
Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should remove the role' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Remove-PveRole -RoleId 'PesterTestRole' -Confirm:$false -ErrorAction Stop } |
|
||||
Should -Not -Throw
|
||||
|
||||
$script:CreatedRoles.Remove('PesterTestRole') | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'API Token CRUD' {
|
||||
BeforeAll {
|
||||
# Create a user to hold the test token
|
||||
if ($null -eq $script:SkipReason) {
|
||||
New-PveUser -UserId 'pester-tokenuser@pam' -ErrorAction SilentlyContinue
|
||||
$script:CreatedUsers.Add('pester-tokenuser@pam')
|
||||
}
|
||||
}
|
||||
|
||||
It 'Should create an API token' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$result = New-PveApiToken `
|
||||
-UserId 'pester-tokenuser@pam' `
|
||||
-TokenId 'pester-token' `
|
||||
-ErrorAction Stop
|
||||
|
||||
$result | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should list API tokens for the user' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$tokens = Get-PveApiToken -UserId 'pester-tokenuser@pam'
|
||||
$tokens | Should -Not -BeNullOrEmpty
|
||||
$tokens | Where-Object { $_.TokenId -eq 'pester-token' } |
|
||||
Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should remove the API token' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Remove-PveApiToken `
|
||||
-UserId 'pester-tokenuser@pam' `
|
||||
-TokenId 'pester-token' `
|
||||
-Confirm:$false `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Permissions' {
|
||||
BeforeAll {
|
||||
# Create a user and role for permission testing
|
||||
if ($null -eq $script:SkipReason) {
|
||||
New-PveUser -UserId 'pester-permuser@pam' -ErrorAction SilentlyContinue
|
||||
$script:CreatedUsers.Add('pester-permuser@pam')
|
||||
}
|
||||
}
|
||||
|
||||
It 'Should list permissions' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Get-PvePermission -ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
|
||||
It 'Should set a permission' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Set-PvePermission `
|
||||
-Path '/' `
|
||||
-Role 'PVEAuditor' `
|
||||
-Users 'pester-permuser@pam' `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'VMs' {
|
||||
It 'Should list VMs' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
# Does not assert count — the node may have zero VMs.
|
||||
{ Get-PveVm -Node $script:Node -ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
|
||||
It 'Should create a test VM' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$task = New-PveVm `
|
||||
-Node $script:Node `
|
||||
@@ -163,13 +347,29 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
$script:CreatedVmIds.Add($vm.VmId)
|
||||
}
|
||||
|
||||
It 'Should start and stop a VM' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
It 'Should get VM config' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
if ($null -eq $script:TestVmId) {
|
||||
Set-ItResult -Skipped -Because 'No test VM was created'
|
||||
return
|
||||
}
|
||||
$config = Get-PveVmConfig -Node $script:Node -VmId $script:TestVmId
|
||||
$config | Should -Not -BeNullOrEmpty
|
||||
$config.Name | Should -Be 'pester-test-vm'
|
||||
}
|
||||
|
||||
It 'Should set VM config' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
{ Set-PveVmConfig `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Description 'Updated by Pester integration test' `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
|
||||
$config = Get-PveVmConfig -Node $script:Node -VmId $script:TestVmId
|
||||
$config.Description | Should -Be 'Updated by Pester integration test'
|
||||
}
|
||||
|
||||
It 'Should start and stop a VM' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
$startTask = Start-PveVm -Node $script:Node -VmId $script:TestVmId -Wait
|
||||
$startTask | Should -Not -BeNullOrEmpty
|
||||
@@ -178,19 +378,39 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
$stopTask | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should restart a VM' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
# Start first, then restart
|
||||
Start-PveVm -Node $script:Node -VmId $script:TestVmId -Wait | Out-Null
|
||||
|
||||
$task = Restart-PveVm -Node $script:Node -VmId $script:TestVmId -Wait
|
||||
$task | Should -Not -BeNullOrEmpty
|
||||
|
||||
Stop-PveVm -Node $script:Node -VmId $script:TestVmId -Wait | Out-Null
|
||||
}
|
||||
|
||||
It 'Should resize a VM disk' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
# First add a disk to resize
|
||||
Set-PveVmConfig -Node $script:Node -VmId $script:TestVmId `
|
||||
-Settings @{ scsi0 = "$($script:Storage):1" } -ErrorAction Stop
|
||||
|
||||
{ Resize-PveVmDisk `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Disk 'scsi0' `
|
||||
-Size '+1G' `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
|
||||
It 'Should clone a VM' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
|
||||
$templateVm = Get-PveVm -Node $script:Node -TemplatesOnly | Select-Object -First 1
|
||||
|
||||
if ($null -eq $templateVm) {
|
||||
Set-ItResult -Skipped -Because 'No template VM available on the test node'
|
||||
return
|
||||
}
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
$task = Copy-PveVm `
|
||||
-SourceNode $script:Node `
|
||||
-VmId $templateVm.VmId `
|
||||
-VmId $script:TestVmId `
|
||||
-NewName 'pester-clone-vm' `
|
||||
-Full `
|
||||
-Wait
|
||||
@@ -205,17 +425,74 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Snapshots' {
|
||||
It 'Should create, list, and remove a snapshot' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
# Ensure the VM is stopped for snapshot
|
||||
$vm = Get-PveVm -Node $script:Node | Where-Object { $_.VmId -eq $script:TestVmId }
|
||||
if ($vm.Status -eq 'running') {
|
||||
Stop-PveVm -Node $script:Node -VmId $script:TestVmId -Wait | Out-Null
|
||||
}
|
||||
|
||||
$snapName = 'pester-snap'
|
||||
|
||||
# Create
|
||||
$createTask = New-PveSnapshot `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Name $snapName `
|
||||
-Description 'Created by Pester integration test' `
|
||||
-Wait
|
||||
|
||||
$createTask | Should -Not -BeNullOrEmpty
|
||||
|
||||
# List and verify
|
||||
$snapshots = Get-PveSnapshot -Node $script:Node -VmId $script:TestVmId
|
||||
$snap = $snapshots | Where-Object { $_.Name -eq $snapName }
|
||||
$snap | Should -Not -BeNullOrEmpty
|
||||
|
||||
# Restore
|
||||
{ Restore-PveSnapshot `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Name $snapName `
|
||||
-Confirm:$false `
|
||||
-Wait } | Should -Not -Throw
|
||||
|
||||
# Remove
|
||||
$removeTask = Remove-PveSnapshot `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Name $snapName `
|
||||
-Confirm:$false `
|
||||
-Wait
|
||||
|
||||
$removeTask | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Storage' {
|
||||
It 'Should list storage' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$storage = Get-PveStorage -Node $script:Node
|
||||
$storage | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should list storage content' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
{ Get-PveStorageContent `
|
||||
-Node $script:Node `
|
||||
-Storage $script:Storage `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
}
|
||||
|
||||
It 'Should upload an ISO' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
if (-not (Test-Path $script:IsoPath)) {
|
||||
Set-ItResult -Skipped -Because "ISO file not found at PVETEST_ISO_PATH: $($script:IsoPath)"
|
||||
@@ -233,72 +510,29 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Snapshots' {
|
||||
It 'Should create and remove a snapshot' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
Context 'Tasks' {
|
||||
It 'Should list recent tasks' {
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
if ($null -eq $script:TestVmId) {
|
||||
Set-ItResult -Skipped -Because 'No test VM was created'
|
||||
return
|
||||
}
|
||||
|
||||
# Ensure the VM is stopped for snapshot
|
||||
$vm = Get-PveVm -Node $script:Node | Where-Object { $_.VmId -eq $script:TestVmId }
|
||||
if ($vm.Status -eq 'running') {
|
||||
Stop-PveVm -Node $script:Node -VmId $script:TestVmId -Wait | Out-Null
|
||||
}
|
||||
|
||||
$snapName = 'pester-snap'
|
||||
|
||||
$createTask = New-PveSnapshot `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Name $snapName `
|
||||
-Description 'Created by Pester integration test' `
|
||||
-Wait
|
||||
|
||||
$createTask | Should -Not -BeNullOrEmpty
|
||||
|
||||
$snapshots = Get-PveSnapshot -Node $script:Node -VmId $script:TestVmId
|
||||
$snapshots | Where-Object { $_.Name -eq $snapName } | Should -Not -BeNullOrEmpty
|
||||
|
||||
$removeTask = Remove-PveSnapshot `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Name $snapName `
|
||||
-Confirm:$false `
|
||||
-Wait
|
||||
|
||||
$removeTask | Should -Not -BeNullOrEmpty
|
||||
$tasks = Get-PveTask -Node $script:Node
|
||||
$tasks | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Network' {
|
||||
It 'Should list networks' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
$networks = Get-PveNetwork -Node $script:Node
|
||||
$networks | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Users' {
|
||||
It 'Should list users' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
|
||||
$users = Get-PveUser
|
||||
$users | Should -Not -BeNullOrEmpty
|
||||
# root@pam is always present
|
||||
$users | Where-Object { $_.UserId -eq 'root@pam' } | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Templates' {
|
||||
It 'Should list templates' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
if (Skip-IfNoTarget) { return }
|
||||
|
||||
# Zero templates is acceptable; just verify the cmdlet does not throw.
|
||||
{ Get-PveTemplate -Node $script:Node -ErrorAction Stop } | Should -Not -Throw
|
||||
@@ -308,16 +542,35 @@ Describe 'Integration Tests' -Tag 'Integration' {
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'Cloud-Init' {
|
||||
It 'Should get cloud-init config' {
|
||||
if ($script:SkipReason) { Set-ItResult -Skipped -Because $script:SkipReason; return }
|
||||
|
||||
if ($null -eq $script:TestVmId) {
|
||||
Set-ItResult -Skipped -Because 'No test VM available for cloud-init test'
|
||||
return
|
||||
}
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
# Get-PveCloudInitConfig should not throw even if the VM has no cloud-init drive.
|
||||
{ Get-PveCloudInitConfig -Node $script:Node -VmId $script:TestVmId -ErrorAction Stop } |
|
||||
Should -Not -Throw
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
Context 'VM Cleanup' {
|
||||
It 'Should remove a VM' {
|
||||
if (Skip-IfNoTestVm) { return }
|
||||
|
||||
# Ensure stopped
|
||||
$vm = Get-PveVm -Node $script:Node | Where-Object { $_.VmId -eq $script:TestVmId }
|
||||
if ($vm.Status -eq 'running') {
|
||||
Stop-PveVm -Node $script:Node -VmId $script:TestVmId -Wait | Out-Null
|
||||
}
|
||||
|
||||
{ Remove-PveVm `
|
||||
-Node $script:Node `
|
||||
-VmId $script:TestVmId `
|
||||
-Force `
|
||||
-Purge `
|
||||
-Confirm:$false `
|
||||
-ErrorAction Stop } | Should -Not -Throw
|
||||
|
||||
$script:CreatedVmIds.Remove($script:TestVmId) | Out-Null
|
||||
$script:TestVmId = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user