- Replace tests/infrastructure/Dockerfile with tests/Dockerfile.test (single multi-stage Dockerfile for both CI and local dev) - CI container-image job now builds from Dockerfile.test target dev-infra - Add ARM support: PowerShell installed via dotnet tool on arm64, APT package on amd64 - Replace tests/dev.sh (bash) with tests/dev.ps1 (PowerShell) for cross-platform support (Windows, macOS, Linux) - Add -DockerHost parameter for running x86 containers on a remote Docker host from ARM Macs (rsyncs repo, uses SSH Docker transport) - Add -NoCleanup switch to keep nested PVE VMs after integration tests - integration command now provisions nested PVE VMs instead of testing against a pre-existing PVE directly - Share /opt/pve-isos host path between CI and local dev (was separate Docker named volume) - Delete tools/Invoke-Tests.ps1 (unused, overlapped with run-integration.sh) - Add .gitignore entries for Terraform state/artifacts - Update all documentation references Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.4 KiB
Integration Tests
This directory contains Pester 5 integration tests for PSProxmoxVE that exercise the module against a real, live Proxmox VE API endpoint. They are skipped by default and must be opted into explicitly.
Prerequisites
Dedicated test node — never production
Integration tests create and destroy real resources (VMs, snapshots, ISO uploads, network objects, user accounts). You must use a dedicated PVE test cluster or standalone node running Proxmox VE 8.x or 9.x. Never point these tests at a production cluster.
Recommended minimum:
- Single-node cluster or standalone host
- At least one storage pool with
images,iso, androotdircontent types enabled - Network access from the machine running Pester to the PVE API port (default 8006)
Required API Token
Create a dedicated API token on the test node with the permissions listed below. Using a scoped token (rather than the root password) limits blast radius if credentials leak.
pveum user add pester@pve
pveum acl modify / --users pester@pve --roles PVEAdmin
pveum user token add pester@pve pester-ci
Minimum permissions per domain
| Test area | Required privilege(s) |
|---|---|
| Connection | Sys.Audit |
| Nodes | Sys.Audit |
| VMs (read) | VM.Audit |
| VMs (create) | VM.Allocate, VM.Config.Disk, VM.Config.Memory, VM.Config.Network |
| VMs (delete) | VM.Allocate |
| VMs (power) | VM.PowerMgmt |
| VMs (clone) | VM.Clone |
| Storage (read) | Datastore.Audit |
| Storage (ISO) | Datastore.AllocateSpace, Datastore.AllocateTemplate |
| Snapshots | VM.Snapshot, VM.Snapshot.Rollback |
| Network | Sys.Modify |
| Users | User.Modify |
| Templates | VM.Allocate, VM.Clone |
| Cloud-Init | VM.Config.CloudInit |
Environment Variables
Set these before running the integration suite. The first six are required; any missing required variable causes every integration test to be skipped with a clear reason message.
| Variable | Required | Description | Example value |
|---|---|---|---|
PVETEST_HOST |
Yes | Hostname or IP address of the test PVE node | 192.168.1.10 or pve-test.internal |
PVETEST_PORT |
Yes | PVE API port | 8006 |
PVETEST_APITOKEN |
Yes | API token in USER@REALM!TOKENID=UUID format |
pester@pve!pester-ci=xxxxxxxx-xxxx-xxxx-xxxx-xxxx |
PVETEST_NODE |
Yes | Node name as it appears in pvesh get /nodes |
pve-test1 |
PVETEST_STORAGE |
Yes | Storage pool to use for disk and ISO operations | local |
PVETEST_ISO_PATH |
Yes | Local path to a small .iso file used for upload tests |
/tmp/tinycorelinux.iso |
PVETEST_PASSWORD |
No | Root password for cloud-init Linux VM provisioning | (use a strong password) |
PVETEST_CLOUD_IMAGE_URL |
No | URL for cloud image download (defaults to Ubuntu Noble) | https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img |
PVETEST_PVE_VERSION |
No | Expected PVE major version (8 or 9) | 9 |
Setting variables (Bash / zsh)
export PVETEST_HOST="192.168.1.10"
export PVETEST_PORT="8006"
export PVETEST_APITOKEN="pester@pve!pester-ci=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export PVETEST_NODE="pve-test1"
export PVETEST_STORAGE="local"
export PVETEST_ISO_PATH="/tmp/tinycorelinux.iso"
# Optional — required for Linux VM provisioning tests
export PVETEST_PASSWORD="<your-test-password>"
Setting variables (PowerShell)
$env:PVETEST_HOST = '192.168.1.10'
$env:PVETEST_PORT = '8006'
$env:PVETEST_APITOKEN = 'pester@pve!pester-ci=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
$env:PVETEST_NODE = 'pve-test1'
$env:PVETEST_STORAGE = 'local'
$env:PVETEST_ISO_PATH = '/tmp/tinycorelinux.iso'
# Optional — required for Linux VM provisioning tests
$env:PVETEST_PASSWORD = '<your-test-password>'
GitHub Actions / CI
Add the variables as repository Actions secrets and expose them as environment variables in your workflow:
env:
PVETEST_HOST: ${{ secrets.PVETEST_HOST }}
PVETEST_PORT: ${{ secrets.PVETEST_PORT }}
PVETEST_APITOKEN: ${{ secrets.PVETEST_APITOKEN }}
PVETEST_NODE: ${{ secrets.PVETEST_NODE }}
PVETEST_STORAGE: ${{ secrets.PVETEST_STORAGE }}
PVETEST_ISO_PATH: ${{ secrets.PVETEST_ISO_PATH }}
PVETEST_PASSWORD: ${{ secrets.PVETEST_PASSWORD }}
How to Run
The integration suite is tagged Integration. Use the -Tag filter so that the unit
tests and integration tests can be run independently.
Via the dev container (recommended)
./tests/dev.ps1 integration
Directly with Invoke-Pester
# Run integration tests only
Invoke-Pester -Path ./tests/PSProxmoxVE.Tests -Tag Integration -Output Detailed
# Run everything (unit + integration)
Invoke-Pester -Path ./tests/PSProxmoxVE.Tests -Output Detailed
# Run unit tests only (exclude integration)
Invoke-Pester -Path ./tests/PSProxmoxVE.Tests -ExcludeTag Integration -Output Detailed
Warning — Real Resources Are Created and Destroyed
The integration tests:
- Create VMs (named
pester-test-vm,pester-clone-vm,pester-linux-vm) onPVETEST_NODE - Delete those VMs after the test completes (via
AfterAllcleanup) - Provision a Linux VM with cloud-init, guest agent, and disk import (when
PVETEST_PASSWORDis set) - Start and stop VMs, including graceful ACPI shutdown via guest agent
- Create and delete a snapshot on an existing stopped VM
- Upload an ISO to
PVETEST_STORAGE - Download a cloud image to PVE storage (when
PVETEST_PASSWORDis set)
The AfterAll block performs best-effort cleanup. If the test run is interrupted, leftover
VMs named pester-* may remain on the test node and should be removed manually.
Always confirm you are pointing at the correct, isolated test node before running.
Planned Test Coverage
| Domain | Current integration tests | Planned additions |
|---|---|---|
| Connection | Connect via API token, detect server version | Connect via credential (ticket auth), session expiry handling |
| Nodes | List nodes, get node status | Node resource usage metrics |
| VMs | List, create, delete, start, stop, clone | Migrate, resize disk, Get/Set-PveVmConfig, Move-PveVm |
| Storage | List, upload ISO | Get-PveStorageContent, Invoke-PveStorageDownload, Remove ISO |
| Snapshots | Create, list, delete snapshot on stopped VM | Restore snapshot, snapshots on running VM (with vmstate) |
| Network | List node networks | Create bridge, Set-PveNetwork, Invoke-PveNetworkApply |
| SDN | (none yet — requires SDN plugin enabled on test node) | Get/New/Remove zone and vnet |
| Users | List users, verify root@pam present | Create/remove user, assign role, set permission |
| Templates | List templates (count not asserted) | Convert VM to template, deploy VM from template |
| Cloud-Init | Get cloud-init config from a stopped VM (no-throw assertion) | Set cloud-init fields, verify propagation via VM config |
| Tasks | (implicitly exercised via -Wait on lifecycle cmdlets) | Get-PveTask, Wait-PveTask with custom timeout |