Commit Graph

53 Commits

Author SHA1 Message Date
Clint Branham 514ce3fc1b fix: Set-PveHaRule requires mandatory Type parameter
PVE 9 API requires 'type' on PUT /cluster/ha/rules/{rule} even for
updates. Added mandatory Type parameter with ValidateSet for
node-affinity and resource-affinity.

Updated integration test and Pester unit tests to pass -Type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:52:23 -05:00
Clint Branham d43387806e refactor: adapt cluster tests to _IntegrationHelper + rename to 16_Cluster
- Rename ZZ.ClusterConfig.Integration.Tests.ps1 → 16_Cluster.Tests.ps1
- Replace manual env var setup with _IntegrationHelper.ps1 + Connect-TestPve
- Use credential auth (root@pam) throughout — no API tokens
- Use $script:PasswordB for node B auth (from helper)
- Access JoinInfo.Nodelist as List<Dictionary> (native types from D013)
- Keep cluster-specific skip helpers (Skip-IfNoCluster, Skip-IfPve9HaGroups)
- Remove redundant Connection context (Connect-TestPve handles it)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:01 -05:00
Clint Branham e7b1c8488c fix: HA rule test creates real VM + HA resource before rule
PVE 9 rejects rules referencing unmanaged resources. Test now creates
a minimal VM, registers it as a disabled HA resource, then creates
the node-affinity rule. Cleanup removes rule, resource, and VM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:01 -05:00
Clint Branham 9636c45a40 fix: remove unsupported affinity param from HA rule test
PVE 9 rejects 'affinity' as an unexpected property on POST
cluster/ha/rules — it's implied by the node-affinity type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:01 -05:00
Clint Branham fc29d01a7a test: add HA rules CRUD integration tests for PVE 9.0+
Full lifecycle: create node-affinity rule, list, get by ID, update
comment, delete, and verify deletion. Uses vm:99999 as a synthetic
resource SID. Tests skip on PVE 8 (rules not available).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:01 -05:00
Clint Branham 23b5334b27 test: rename cluster integration tests to run last
Cluster tests create a 2-node cluster that cannot be torn down via API
(quorum loss). Prefix with ZZ so they execute after all other
integration tests that assume standalone nodes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:01 -05:00
Clint Branham 4d9533ae36 fix: skip node removal in 2-node cluster integration tests
Removing a node from a 2-node PVE cluster via REST API is not supported
— the remaining node loses quorum mid-operation, causing "no quorum!"
errors. PVE requires stopping corosync on the departing node first
(pvecm expected 1), which is not available via the REST API.

Replace removal tests with a final cluster state verification.
Test infrastructure handles cleanup via reprovisioning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham 988b1446b0 fix: use root@pam for cluster node removal in integration tests
Remove-PveClusterConfigNode requires root@pam (not API token).
Fixed both the test context and AfterAll cleanup to connect with
root@pam credentials before attempting node removal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham 4277ad73b9 fix: retry re-auth during cluster join and always reconnect to node A
- Extract re-auth retry into ReauthenticateWithRetry helper (fixes
  cognitive complexity warning)
- Retry auth up to 10x with 3s delay — node B's auth services need
  time to restart after joining the cluster
- Wrap join in try/finally so the test always reconnects to node A,
  even if the join fails

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham 8564e05c00 fix: use root@pam throughout cluster integration tests
All cluster operations (create, status, join, options, cleanup) require
root@pam ticket auth — API tokens lack Sys.Audit and Sys.Modify on /.
Changed test to connect as root@pam once during cluster creation and
stay on that session for the entire lifecycle.

Removed redundant Connect-PveServer calls that switched between
root@pam and API token between contexts.

Added 5s stabilization sleep after cluster creation for corosync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham f77567573c fix: integration test fixes for JArray and auth issues
- Convert JoinInfo.Nodelist to native PS array via ConvertFrom-Json
  (JArray .Item() still hits IEnumerator error in PowerShell)
- Use root@pam for cluster options context (delete requires Sys.Modify)
- Reconnect with API token after options restore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham c7627ee4e1 test: add xUnit, Pester, and integration tests for cluster config + HA
xUnit (47 new tests, 429 total):
- ClusterConfigServiceTests: 25 tests covering all 14 service methods
  including URL encoding, null guards, and error responses
- HaServiceTests: 22 tests covering resources, groups, status, and rules
  with URI encoding verification (vm:100 → vm%3A100)

Pester (187 new tests, 1525 total):
- ClusterConfigCmdlets.Tests.ps1: 11 cmdlets tested
- HaCmdlets.Tests.ps1: 14 cmdlets tested

Integration (ClusterConfig.Integration.Tests.ps1):
- Full 2-node cluster lifecycle with -Wait for task completion
- Uses root@pam ticket auth for cluster create/join operations
- HA group tests skip on PVE 9.0+ (groups migrated to rules)
- JArray indexing uses .Item() for PowerShell compatibility

Cmdlet improvements:
- New-PveCluster, Add-PveClusterConfigNode, Add-PveClusterMember now
  support -Wait switch to block until task completes (via TaskService)
- GetClusterConfig returns JToken to handle array responses on standalone
- OutputType updated to PveTask for task-returning cmdlets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham 4e4bc11872 test: add xUnit, Pester, and integration tests for cluster config + HA
xUnit (47 new tests, 429 total):
- ClusterConfigServiceTests: 25 tests covering all 14 service methods
  including URL encoding, null guards, and error responses
- HaServiceTests: 22 tests covering resources, groups, status, and rules
  with URI encoding verification (vm:100 → vm%3A100)

Pester (187 new tests, 1525 total):
- ClusterConfigCmdlets.Tests.ps1: 11 cmdlets — parameter metadata,
  ValidateSet/ValidateRange/ValidateLength, ShouldProcess, ConfirmImpact
- HaCmdlets.Tests.ps1: 14 cmdlets — same pattern

Integration (ClusterConfig.Integration.Tests.ps1):
- Full 2-node cluster lifecycle: create → join → options → HA groups → cleanup
- Uses root@pam ticket auth for cluster create/join (API tokens lack permission)
- Reconnects with API token after privileged operations
- Skip helpers for standalone (no node B) and non-PVE-9 environments

Fixes:
- GetClusterConfig returns JToken (not JObject) to handle array responses
  from /cluster/config on standalone nodes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 17:40:00 -05:00
Clint Branham 94bc1a9d73 fix: address Copilot review round 3
- Fix bare Skip-IfNoTarget calls in 13_Firewall and 14_Backup
  (missing if/return pattern caused tests to run when they should skip)
- Validate modifier-only switches in dev.ps1 (-Force/-Reprovision
  without an action switch now errors instead of defaulting to -Shell)
- Add force-cleanup to usage text in run-integration.sh
- Add --connect-timeout/--max-time to guest agent curl in wait-for-pve.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 16:27:14 -05:00
Clint Branham 7ffc57e16f fix: address Copilot review — validation, error handling, MAC case
- Add ValidateSet('8','9','all') to dev.ps1 -Version parameter
- Fix Shell warning to reference $DevContainer not $InfraContainer
- Fix Skip-IfNoNodeB to check $PasswordB not $Password
- Pass PVE_TARGET_NODE to wait-for-pve.sh instead of auto-discovering
- Add error default cases to all pve_* helper functions
- Lowercase MAC addresses for answer server matching
- Create answer file paths before terraform destroy in cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:50:07 -05:00
Clint Branham 117b10d222 refactor: make all integration test files self-contained
Each test file now creates and cleans up its own resources:
- 06_VMs: restores own AfterAll cleanup for pester-test-vm
- 07_Snapshots: creates pester-snap-vm, tests snapshots, cleans up
- 09_CloudInit: creates pester-ci-vm with cloud-init drive, cleans up
- 15_Tasks: creates pester-task-vm, tests task CRUD, cleans up

Removed cross-file dependency helpers (Find-TestVm, Register-TestResource)
from _IntegrationHelper.ps1 — no longer needed.

Each file can now run independently via -Tests filter. If a Setup
context fails, subsequent tests in the same file skip gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:40:05 -05:00
Clint Branham 72280e3697 fix: don't clean up test VM in 06_VMs — later files depend on it
06_VMs AfterAll was deleting pester-test-vm, causing 07_Snapshots,
09_CloudInit, and 15_Tasks to fail with VMID 100 (wrong VM or
nonexistent). Now 99_Cleanup handles all pester-* resource removal.

Also fixed cleanup timing:
- Use -Wait on Stop-PveVm/Container before Remove
- Add sleep after removal for API propagation
- 10_Containers AfterAll uses -Wait on stop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:32:51 -05:00
Clint Branham 12874d168d refactor: split monolithic integration tests into numbered per-area files
Split the 1544-line Integration.Tests.ps1 into 18 focused test files
with numeric prefixes for execution ordering.

New shared helper (_IntegrationHelper.ps1):
- Credential-based auth (root@pam) instead of API tokens
- Skip helpers for env var checks
- Resource discovery (Find-TestVm) for cross-file dependencies
- Register-TestResource for env-var state passing between files

Files created:
  00_Connection, 01_Nodes, 02_Users, 03_Storage, 03a_SharedStorage,
  04_Network, 05_SDN, 06_VMs, 07_Snapshots, 08_Templates,
  09_CloudInit, 10_Containers, 11_LinuxVM, 12_OVA, 13_Firewall,
  14_Backup, 15_Tasks, 99_Cleanup

Key changes:
- All tests use root@pam credentials (not API tokens)
- Each file self-contained with own BeforeAll/AfterAll cleanup
- Token CRUD tests now idempotent (remove-before-create)
- 99_Cleanup is safety-net for any leftover pester-* resources
- SharedStorage renamed to 03a_SharedStorage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:51:49 -05:00
copilot-swe-agent[bot] f76ff513e9 fix(storage): implement Portal defaulting for iSCSI, fix Pool/CephPool selection, add NFS format validation
Co-authored-by: GoodOlClint <151449+GoodOlClint@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoodOlClint/PSProxmoxVE/sessions/d84722e4-409e-4ced-bfbc-f323715b8bdc
2026-03-24 20:36:46 +00:00
Clint Branham c385f4a620 fix(tests): correct NFS and iSCSI storage test parameters
NFS and iSCSI are implicitly shared in PVE — the API rejects an
explicit 'shared' parameter. iSCSI uses 'portal' (not 'server') for
the target address.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:57:45 -05:00
Clint Branham 064cc372cf feat(storage): add -Target and -Portal parameters to New-PveStorage for iSCSI
New-PveStorage now supports configuring iSCSI storage backends natively:
  -Target: iSCSI target IQN (e.g. iqn.2024-01.com.example:storage)
  -Portal: iSCSI portal address (host:port, defaults to server:3260)

Also refactored ProcessRecord to use AddIfNotEmpty helper, reducing
cognitive complexity.

Added unit tests for all iSCSI/NFS parameter metadata and a new
SharedStorage.Tests.ps1 integration test file that tests NFS and iSCSI
storage create/verify/status/delete lifecycle against the Docker-based
storage containers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:43:33 -05:00
Clint Branham f21c7f84b7 feat(ci): unify CI and local integration test infrastructure
- 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>
2026-03-24 10:41:28 -05:00
Clint Branham d5f4e13637 fix(security): move hardcoded test password to GitHub Actions secret
- CI workflow: PVE_PASSWORD now reads from secrets.PVE_TEST_PASSWORD
- variables.tf: removed default password, requires TF_VAR env var
- Integration README: examples use <your-test-password> placeholder
- create-api-token.sh: example IP changed to pve.example.com
- .gitignore: added .env/.env.* exclusion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:40:47 -05:00
Clint Branham 89bead7a0e fix: add -Confirm:$false to Restart-PveContainer integration test (F080, F081)
Restart-PveContainer gained ConfirmImpact.High in the F062 fix, which
correctly prompts for confirmation on destructive operations. The
integration test was missing -Confirm:$false, causing a
NullReferenceException in non-interactive CI. The cascading failure also
broke the Copy-PveContainer test (F081).

Also updates CONTRIBUTING.md to reference .NET SDK 10.0+ (was 9.0+) to
match all CI workflows and test project TFM (F083).

Includes scan-6 review report and findings database update (F001-F083).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:48:11 -05:00
Clint Branham d99c3e5490 chore(test): update tests for parameter changes, target net10.0
- xUnit test project now targets net10.0 instead of net9.0 (EOL Nov 2026)
- Updated GuestAgentExtCmdlets tests for SecureString Password parameter
- Added Timeout parameter tests to GuestAgentCmdlets
- Added ConfirmImpact.High assertions for Suspend/Restart-PveVm
- Added -Confirm:$false to Suspend-PveVm integration test call

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 08:16:45 -05:00
Clint Branham 4b503b08db fix: backup job creation requires vmid or -All, fix Enabled default
- Integration test: add -All to New-PveBackupJob call — PVE API
  requires either vmid or all when creating a backup job
- Fix New-PveBackupJob Enabled logic: jobs are enabled by default
  unless user explicitly passes -Enabled:$false (was broken because
  SwitchParameter default=true doesn't set IsPresent)
- Fix XML doc cref warning in NetworkService after RequireSdn removal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 09:07:39 -05:00
Clint Branham 27ecca6251 feat: add two-tier version gating (introduced vs default) for cmdlets
Replace the service-layer RequireSdn hard block with a cmdlet-level
RequireVersion helper in PveCmdletBase that supports two tiers:

- Introduced version: hard fail — the API endpoint doesn't exist
- Default version: warning only — feature exists but may not be
  enabled; users who manually enabled it can proceed

Applied to:
- SDN Zone/VNet/Subnet: introduced 6.2, default 8.0
- SDN IPAM/DNS/Controller: introduced 6.2, default 8.1
- Import-PveVmDisk, Import-PveOva: introduced 8.1

Also fixes integration tests:
- Remove duplicate OVA Import context
- Use systemd calendar format for backup schedule (PVE 9)
- Add SDN IPAM/DNS/Controller read-only integration tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:40:23 -05:00
Clint Branham c6fd0d36fc fix(test): remove duplicate OVA context, use systemd calendar format for backup schedule
- Remove duplicate 'OVA Import' context that used wrong parameter name
  (NewVmId vs VmId) — original OVA test context at line 1227 is correct
- Fix backup job schedule from cron syntax ('0 3 * * 6') to systemd
  calendar event format ('sat 03:00') which PVE 9 requires

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:03:38 -05:00
Clint Branham a72642d203 feat: add firewall, backup, and SDN IPAM/DNS/controller cmdlets
Add 35 new cmdlets bringing the total to 118:
- Firewall (21): rules, groups, aliases, IP sets, options at
  cluster/node/VM/container levels
- Backup (5): ad-hoc vzdump and scheduled backup job CRUD
- SDN IPAM/DNS/Controller (9): plugin management for SDN subsystem

Also includes:
- Fix: Remove-PveRole now has ConfirmImpact.High
- Fix: URL-encode snapshot names in API paths
- Refactor: extract auth header strings to constants in PveHttpClient
- Add PSGallery version badge to README
- Full test coverage: 11 JSON fixtures, xUnit model tests, Pester
  unit tests, and integration tests for firewall/backup/OVA import
- Updated manifest, format file, CHANGELOG, README, API coverage docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:51:49 -05:00
Clint Branham 8d5f33f592 feat: add -Detailed switch to Get-PveVm for accurate pause detection
PVE list endpoint reports Status=running for paused VMs. The new
-Detailed switch queries status/current per VM to populate QmpStatus.

- PveVm.EffectiveStatus computed property: returns QmpStatus ?? Status
- PveVm.Status doc comment explains the limitation
- VmService.EnrichVmStatus() queries status/current endpoint
- Get-PveVm -Detailed enriches each VM with qmpstatus, pid, uptime
- format.ps1xml table view uses EffectiveStatus for the Status column
- Integration test asserts EffectiveStatus after suspend/resume

Verified locally: Get-PveVm -Detailed correctly shows 'paused' for
suspended VMs where Get-PveVm (without -Detailed) shows 'running'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:18:38 -05:00
Clint Branham 5e37737bc4 fix(test): remove Get-PveVm status assertions after suspend/resume
PVE list endpoint (GET /nodes/{node}/qemu) always reports Status=running
for paused VMs — qmpstatus is only available from the status/current
endpoint. The -Wait -Timeout polling uses status/current internally, so
if it returns without throwing, the state transition is confirmed.

Also fix restart recovery: use Resume-PveVm -Wait instead of checking
Status from list endpoint (which never shows 'paused').

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:08:20 -05:00
Clint Branham 0fc15d74ee fix: OVA import — scsi bus, boot order, NIC model from OVF, metadata verification
Locally verified against PVE 9.1 with Ubuntu 24.04 cloud OVA:

- Use scsi bus (not sata) for imported disks, matching PVE UI behavior
- Set boot=order=scsi0 for first disk
- Parse NIC ResourceSubType from OVF (VmxNet3, E1000, E1000e, etc.)
  and map to PVE network model instead of hardcoding virtio
- Add MapNicModel() to OvfMetadata with fallback to e1000 for unknown types
- Integration test verifies name, cores, memory, ostype, scsi0, net0, boot

qmpstatus fix (from previous local testing):
- WaitForStatusTransition polls status/current endpoint for qmpstatus
- PVE reports status=running but qmpstatus=paused for suspended VMs
- SDN subnet ID uses zone prefix: {zone}-{ip}-{prefix}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:56:03 -05:00
Clint Branham a06cd3f2e5 fix: use qmpstatus for suspend state detection, fix SDN subnet ID format
Suspend/Resume:
- Poll status/current endpoint (not VM list) to get qmpstatus
- PVE reports status=running but qmpstatus=paused for suspended VMs
- Verified locally: Suspend-PveVm -Wait -Timeout 30 now works

SDN:
- Subnet IDs use zone prefix, not vnet: {zone}-{ip}-{prefix}
- Add 5s propagation delay before VNet deletion after subnet removal
- Switch to real Ubuntu 24.04 OVA for Import-PveOva testing

Integration tests:
- All VM/container lifecycle calls use -Wait -Timeout
- Remove manual Start-Sleep polling loops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:37:38 -05:00
Clint Branham 2b772ebc65 feat: add -Timeout parameter with state polling to lifecycle cmdlets
All VM and container lifecycle cmdlets (Start, Stop, Restart, Suspend,
Resume, Reset) now poll VM/container status when -Wait is specified,
blocking until the expected state is reached or -Timeout (default 60s)
is exceeded.

Implementation:
- PveCmdletBase.WaitForStatusTransition() — shared helper that waits
  for PVE task completion then polls status via API
- 9 cmdlets updated: 6 VM (Start, Stop, Restart, Suspend, Resume,
  Reset) + 3 container (Start, Stop, Restart)
- -Timeout parameter with [ValidateRange(1, 3600)] on each

Integration tests:
- Replace all manual Start-Sleep + polling loops with -Wait -Timeout
- Switch to real Ubuntu cloud OVA (571 MB) for Import-PveOva testing
- OVA test verifies full import + VM start

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:15:30 -05:00
Clint Branham d1eb674c92 fix(test): fix suspend polling, SDN propagation delays, OVA resilience
- Suspend/Resume: replace fixed sleep with polling loop (up to 20s)
- Restart: ensure VM is running (not paused) before attempting restart
- SDN: add propagation delays between subnet/vnet/zone removal
- OVA: split into OVF parsing test (always works) and import test
  (tolerates synthetic VMDK failures gracefully)
- Dockerfile: add qemu-utils for proper VMDK generation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 15:05:40 -05:00
Clint Branham c5396707eb fix(test): fix suspend, SDN, container, and OVA integration test failures
- Suspend/Resume: move to Linux VM lifecycle context (empty VMs may
  not reliably transition to paused state)
- SDN subnet model: dhcp-range is an array, not string
- SDN subnet delete: use PVE-format ID (vnet-ip-prefix) not CIDR
- Container config: trim trailing newline from PVE description
- OVA: use qemu-img to create valid sparse VMDK (add qemu-utils to
  test container Dockerfile)
- Enable 'images' content type on local storage for OVA import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:49:06 -05:00
Clint Branham 531deafdb7 fix(test): fix integration test failures for SDN, containers, OVA, suspend
- SDN: zone ID cannot contain hyphens — rename 'pester-zone' to 'pesterz'
- SDN: subnet removal uses original CIDR, not transformed ID
- Containers: New-PveContainer now auto-assigns VM ID via /cluster/nextid
  when -VmId is omitted (was missing, unlike New-PveVm)
- OVA: enable 'images' content type on local storage for import-from
- Suspend: add delay before status check, use Should -Not -Throw pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:35:53 -05:00
Clint Branham 1c4c112565 test: expand integration and unit test coverage to 84 test cases
Integration tests (27 contexts, 84 test cases, up from 48 cmdlets to 76):
- VM: Suspend/Resume/Resize
- Containers: full lifecycle (create, config, start, stop, restart, clone)
- Container Snapshots: create, list, restore, remove
- SDN: zone/vnet/subnet CRUD (PVE 8+ version-gated)
- Storage: create/remove directory storage, Invoke-PveStorageDownload
- OVA Import: Import-PveOva with generated test OVA
- Cloud-Init: Invoke-PveCloudInitRegenerate
- Templates: Remove-PveTemplate
- Disconnect-PveServer: verify disconnect + post-disconnect failure

Unit tests:
- Guest agent cmdlets (Test-PveVmGuestAgent, Get-PveVmGuestNetwork,
  Invoke-PveVmGuestExec) — parameter metadata, ShouldProcess, session

Test infrastructure:
- prepare-test-environment.sh generates minimal test OVA (OVF + 1MB VMDK)
- CI workflow passes PVETEST_OVA_PATH to integration tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:20:52 -05:00
Clint Branham f2280c1c88 refactor: rename Send-PveIso to Send-PveFile and move VM provisioning into integration tests
Send-PveFile replaces Send-PveIso with a -ContentType parameter
(iso, vztmpl, import). This is a breaking rename — the module has
not been released yet.

Test infrastructure refactored:
- prepare-test-vm.sh replaced with prepare-test-environment.sh
  (only SSH ops: enable content types, upload cloud-init snippet,
  curl download cloud image)
- VM provisioning moved into integration tests as "Linux VM —
  Provisioning" context: Send-PveFile, New-PveVm, Import-PveVmDisk,
  Set-PveVmConfig, Set-PveCloudInitConfig, Start-PveVm,
  Test-PveVmGuestAgent — all tested as part of the test suite
- PVETEST_LINUX_VMID env var removed (VM created by tests)
- PVETEST_CLOUD_IMAGE_PATH + PVETEST_PASSWORD added

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 13:39:06 -05:00
Clint Branham 088221a5f1 feat: add Import-PveVmDisk cmdlet for disk image import
Imports disk images (qcow2, raw, vmdk) and OVA archives into VMs
via the PVE API using POST config with import-from syntax.

- VmService.ImportDisk() method using POST /nodes/{node}/qemu/{vmid}/config
- Import-PveVmDisk cmdlet with -Source, -Disk, -TargetStorage, -Format, -Wait
- Pester unit tests
- prepare-test-vm.sh updated to use Import-PveVmDisk instead of SSH qm importdisk
- Set-PveNetwork: add required Type parameter (fixes PVE API 400 error)
- Integration test fix: pass -Type bridge to Set-PveNetwork

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:55:36 -05:00
Clint Branham ae55dbf0d7 fix(test): add -Confirm:$false to all ConfirmImpact.High cmdlet calls in tests
ConfirmImpact.High causes interactive confirmation prompts in
non-interactive CI, blocking before ProcessRecord runs. Add
-Confirm:$false to all Stop-PveVm, Reset-PveVm, Remove-*,
Restore-*, and New-PveTemplate calls in both unit and integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:40:54 -05:00
Clint Branham fac796ee43 test: add network CRUD integration tests
- Add bridge create/update/remove/apply test cases
- Add interface type filter test
- Tests exercise New-PveNetwork, Set-PveNetwork, Remove-PveNetwork,
  and Invoke-PveNetworkApply against live PVE

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:22:03 -05:00
Clint Branham b77f08533c fix(test): add delay after template conversion for PVE 9 propagation
PVE 9 may not immediately reflect the template flag in the VM list
after New-PveTemplate returns. Add a 3-second delay before verifying.
PVE 8 passed without the delay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:58:11 -05:00
Clint Branham 518e5c838a feat(test): use new cmdlets in test VM setup and add guest agent tests
prepare-test-vm.sh now uses module cmdlets for 7 of 8 steps:
- Set-PveVmConfig -AdditionalConfig replaces SSH qm set (8 config keys)
- Test-PveVmGuestAgent replaces SSH qm agent ping
- Only SSH remaining: pvesm set, SCP snippet, qm importdisk

New integration tests for guest agent cmdlets:
- Test-PveVmGuestAgent: verify agent responds
- Get-PveVmGuestNetwork: verify interfaces with IPv4 addresses
- Invoke-PveVmGuestExec: run hostname command, verify exit code + output

Also force Node.js 24 for delete-package-versions (no v6 available yet).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:08:52 -05:00
Clint Branham d995479c8e fix(test): add -Confirm:\$false to New-PveTemplate call in integration tests
ConfirmImpact.High causes an interactive prompt in CI without
-Confirm:\$false, leading to a null reference error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:31:17 -05:00
Clint Branham a9f1c066e0 feat(test): add Alpine guest-agent VM for ACPI and template tests
Infrastructure:
- build-test-image.sh: Downloads Alpine cloud image, uses virt-customize
  to install qemu-guest-agent, caches at /opt/pve-images/
- prepare-test-vm.sh: SCPs image to nested PVE, creates VM via
  qm importdisk with cloud-init and agent enabled, waits for agent

Workflow:
- Build Alpine image step (cached on runner, only built once)
- Deploy test VM step after provisioning, passes LINUX_VMID to tests

Tests:
- Guest Agent VM lifecycle: verify running, ACPI restart, ACPI stop
- Templates: convert Linux VM to template, clone from template
  (New-PveTemplate, New-PveVmFromTemplate)
- Fix Get-PveTask: use .Upid property instead of whole PveTask object

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:01:05 -05:00
Clint Branham 8924d521b6 fix(test): correct integration test parameter mismatches
- New-PveRole: Privileges is a comma-separated string, not an array
- Set-PvePermission: uses UgId+Type, not Users
- Get-PveVmConfig: no Name property — test config get/set together
- Restart-PveVm: replace with Reset-PveVm (ACPI timeout on no-OS VM)
- Remove resize disk test (Set-PveVmConfig lacks Settings param)
- Copy-PveVm: must provide NewVmId explicitly
- Get-PveTask: requires Upid — test via Start-PveVm task chain

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:46:08 -05:00
Clint Branham 5e1864a0b0 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>
2026-03-19 10:33:07 -05:00
Clint Branham cc344a3070 fix(test): replace null-coalescing operator for PS 5.1 compatibility
PS 5.1 cannot parse the ?? operator (PS 7.0+), causing a discovery
error even though integration tests are excluded by tag. Replace with
if/else that works on all PS versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:00:18 -05:00
Clint Branham 0dc7f5f833 fix(test): fix assembly loading, remove mock server, resolve test warnings
- Create shared _TestHelper.ps1 for reliable module loading in both
  local dev and CI (fixes System.Runtime 9.0.0.0 FileNotFoundException
  on PS 7.x by trying Import-Module by name first, then local paths)
- Use (Get-Module).ModuleBase for manifest path resolution
- Remove PSProxmoxVE.MockServer project and MockIntegration tests
- Remove MockIntegration from ExcludeTag filters

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:11:33 -05:00