Files
PSProxmoxVE/tests/PSProxmoxVE.Tests/Integration/README.md
T
goodolclint-claude[bot] 08ee3ae249 chore: repair the local dev path and delete its dead scaffolding
The local dev environment had drifted badly from CI. Remove the parts that no
longer describe anything real, and make the rest match how CI actually runs.

Delete tests/dev.ps1. It wrapped run-integration.sh, which CI calls directly,
and duplicated the module build that script already performs internally. As a
second entry point it drifted: it still offered the PVE 8 leg retired in #88,
mounted the Docker socket for storage containers replaced by the storage VM in
#87, and pointed its remote-host examples at a runner decommissioned in the ARC
migration. All four documents describing it used a positional syntax that bound
the bare word to -Tests and then fell through to -Shell, so every documented
command silently opened a container shell. Recorded as D019.

Delete tests/infrastructure/runner/, a self-hosted-runner-in-Docker superseded
by Actions Runner Controller.

Make disk_storage and iso_storage required. Their defaults named a NAS that the
lab replaced with Ceph, and CI overrides both from repository variables, so the
defaults only ever misled local runs. require_env now fails at the top of a run
rather than at terraform apply, and the descriptions point at tests/.env.test
because cmd_provision deletes terraform.tfvars before applying.

preflight-cleanup.sh no longer falls back to the literal "local" storage. An
unset TF_VAR_iso_storage now skips only the ISO branch, leaving VM destroy and
state cleanup intact, and emits a workflow annotation: force-cleanup is the
only cleanup CI runs and it wipes Terraform state, so a silent skip strands the
uploaded ISO with nothing left to reclaim it.

Drop docker-ce-cli and the /var/run/docker.sock mount. Nothing in the container
has called docker since #87 moved storage into a VM; the remaining docker calls
run inside that VM over SSH. The CI job image is built from the same target, so
this also removes a third-party apt repository from its supply chain.

Rewrite tests/.env.test.example against what the code now requires, and fix the
documented commands in CLAUDE.md, README.md, copilot-instructions.md and the
integration README.
2026-09-01 16:27:10 -05:00

187 lines
9.8 KiB
Markdown

# 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`, and `rootdir` content 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)
```bash
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)
```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:
```yaml
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 CI container (recommended)
`run-integration.sh` provisions the nested PVE nodes, installs the module, and runs the
suite — the same path CI takes. x86 only.
```bash
pve() {
docker compose -f tests/docker-compose.test.yml --profile infra run --rm dev-infra \
bash tests/infrastructure/scripts/run-integration.sh "$@"
}
pve provision 9
pve test 9 Cluster,VMs # the area filter is optional
pve force-cleanup
```
### Directly with Invoke-Pester
```powershell
# 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`) on `PVETEST_NODE`
- **Delete** those VMs after the test completes (via `AfterAll` cleanup)
- **Provision a Linux VM** with cloud-init, guest agent, and disk import (when `PVETEST_PASSWORD` is 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_PASSWORD` is 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 |