diff --git a/.gitignore b/.gitignore index f19e575..50cd147 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ nupkg/ BenchmarkDotNet.Artifacts/ .env .env.* +!.env.*.example +tests/.env.test diff --git a/tests/.env.test.example b/tests/.env.test.example new file mode 100644 index 0000000..a56a714 --- /dev/null +++ b/tests/.env.test.example @@ -0,0 +1,21 @@ +# PSProxmoxVE integration test configuration. +# Copy to .env.test and fill in values. This file is gitignored. + +# ── For testing against a pre-existing PVE (./tests/dev.sh integration) ── +PVETEST_HOST=pve.example.com +PVETEST_PORT=8006 +PVETEST_APITOKEN=user@realm!tokenid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +PVETEST_NODE=pve1 +PVETEST_STORAGE=local +PVETEST_PASSWORD= + +# ── For full provisioning (./tests/dev.sh provision, x86 only) ─────────── +# PVE_ENDPOINT=https://pve.example.com:8006 +# PVE_API_TOKEN=user@realm!tokenid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +# PVE_TARGET_NODE=pve1 +# PVE_PASSWORD= + +# ── Optional overrides ────────────────────────────────────────────────── +# CACHE_DIR=/opt/pve-isos +# WORK_DIR=/tmp/pve-integration +# PVE_VERSIONS=9 8 diff --git a/tests/Dockerfile.test b/tests/Dockerfile.test index da67d13..728803e 100644 --- a/tests/Dockerfile.test +++ b/tests/Dockerfile.test @@ -1,36 +1,53 @@ -# Replicates the GitHub Actions CI environment for PS 7.x unit tests -FROM mcr.microsoft.com/powershell:7.5-ubuntu-24.04 +# PSProxmoxVE development and integration testing container +# +# Two targets: +# dev - .NET SDK + PowerShell + Pester (build & test, works on ARM/x86) +# dev-infra - Adds Terraform + PVE provisioning tools (x86 only) +# +# Usage: +# # For build + test (works on Mac M-series): +# docker compose -f tests/docker-compose.test.yml up -d +# +# # For full CI flow including provisioning (x86 only): +# docker compose -f tests/docker-compose.test.yml --profile infra up -d -# Install .NET SDK 9.0, build module, install Pester, copy to module path -RUN apt-get update && apt-get install -y wget \ - && wget https://dot.net/v1/dotnet-install.sh -O /tmp/dotnet-install.sh \ - && chmod +x /tmp/dotnet-install.sh \ - && /tmp/dotnet-install.sh --channel 9.0 --install-dir /usr/share/dotnet \ - && ln -sf /usr/share/dotnet/dotnet /usr/local/bin/dotnet \ - && rm /tmp/dotnet-install.sh \ - && rm -rf /var/lib/apt/lists/* +# ── Base: .NET SDK + PowerShell + Pester ──────────────────────────── +FROM mcr.microsoft.com/dotnet/sdk:10.0-noble AS dev + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl jq openssh-client ca-certificates apt-transport-https gnupg \ + && curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \ + | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg \ + && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] \ + https://packages.microsoft.com/ubuntu/24.04/prod noble main" \ + > /etc/apt/sources.list.d/microsoft-prod.list \ + && apt-get update && apt-get install -y --no-install-recommends powershell \ + && rm -rf /var/lib/apt/lists/* \ + && pwsh -NoProfile -Command \ + 'Set-PSRepository -Name PSGallery -InstallationPolicy Trusted; \ + Install-Module -Name Pester -MinimumVersion 5.0 -Scope AllUsers -Force' \ + && mkdir -p /usr/local/share/powershell/Modules/PSProxmoxVE WORKDIR /repo -COPY . . +CMD ["pwsh", "-NoProfile"] -RUN dotnet publish src/PSProxmoxVE/PSProxmoxVE.csproj \ - --configuration Release --framework net9.0 --output ./publish/net9.0 \ - && rm -f ./publish/net9.0/PSProxmoxVE.deps.json \ - && pwsh -NoProfile -Command ' \ - Install-Module -Name Pester -MinimumVersion 5.0 -Force -Scope AllUsers; \ - $modulePath = "/usr/local/share/powershell/Modules/PSProxmoxVE"; \ - New-Item -ItemType Directory -Path $modulePath -Force | Out-Null; \ - Copy-Item -Path ./publish/net9.0/* -Destination $modulePath -Recurse -Force' +# ── Full: adds Terraform + PVE provisioning tools (x86 only) ─────── +FROM dev AS dev-infra -# Unset DOTNET_ROOT so PS uses its own bundled runtime -ENV DOTNET_ROOT="" -ENV DOTNET_MULTILEVEL_LOOKUP="" - -CMD ["pwsh", "-NoProfile", "-Command", " \ - Import-Module Pester -MinimumVersion 5.0; \ - $config = New-PesterConfiguration; \ - $config.Run.Path = 'tests/PSProxmoxVE.Tests'; \ - $config.Run.Exit = $true; \ - $config.Filter.ExcludeTag = @('Integration'); \ - $config.Output.Verbosity = 'Detailed'; \ - Invoke-Pester -Configuration $config"] +RUN apt-get update && apt-get install -y --no-install-recommends \ + dosfstools mtools sshpass python3 xorriso gnupg \ + && curl -fsSL https://apt.releases.hashicorp.com/gpg \ + | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg \ + && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \ + https://apt.releases.hashicorp.com noble main" \ + > /etc/apt/sources.list.d/hashicorp.list \ + && curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg \ + -o /usr/share/keyrings/proxmox-release-bookworm.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/proxmox-release-bookworm.gpg] \ + http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \ + > /etc/apt/sources.list.d/proxmox-pve.list \ + && apt-get update && apt-get install -y --no-install-recommends \ + terraform proxmox-auto-install-assistant qemu-utils \ + && rm -rf /var/lib/apt/lists/* diff --git a/tests/dev.sh b/tests/dev.sh new file mode 100755 index 0000000..5bf9693 --- /dev/null +++ b/tests/dev.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# Helper script for the dev/test containers. +# +# Usage: +# ./tests/dev.sh # Start dev container and open pwsh shell +# ./tests/dev.sh build # Build the module inside the container +# ./tests/dev.sh test # Run unit tests +# ./tests/dev.sh integration # Run integration tests against existing PVE (needs .env.test) +# ./tests/dev.sh provision # Full CI flow: provision → test → cleanup (x86 only) +# ./tests/dev.sh cleanup # Destroy provisioned VMs (x86 only) +# ./tests/dev.sh stop # Stop all containers +# ./tests/dev.sh rebuild # Rebuild container image(s) + +set -euo pipefail +cd "$(dirname "$0")/.." + +COMPOSE="docker compose -f tests/docker-compose.test.yml" +DEV_CONTAINER="psproxmoxve-dev" +INFRA_CONTAINER="psproxmoxve-dev-infra" +MODULE_PATH="/usr/local/share/powershell/Modules/PSProxmoxVE" +RUN_INTEGRATION="tests/infrastructure/scripts/run-integration.sh" + +ensure_dev() { + if ! docker inspect "$DEV_CONTAINER" --format '{{.State.Running}}' 2>/dev/null | grep -q true; then + echo "Starting dev container..." + $COMPOSE up -d dev + fi +} + +ensure_infra() { + if ! docker inspect "$INFRA_CONTAINER" --format '{{.State.Running}}' 2>/dev/null | grep -q true; then + echo "Starting infra container (x86 only)..." + $COMPOSE --profile infra up -d dev-infra + fi +} + +build_module() { + local container="$1" + docker exec "$container" bash -c " + dotnet publish src/PSProxmoxVE/PSProxmoxVE.csproj \ + -c Release -f netstandard2.0 -o /tmp/publish 2>&1 | tail -1 && \ + cp -r /tmp/publish/* $MODULE_PATH/ && \ + echo 'Module installed to $MODULE_PATH' + " +} + +case "${1:-shell}" in + shell) + ensure_dev + docker exec -it "$DEV_CONTAINER" pwsh -NoProfile + ;; + + build) + ensure_dev + build_module "$DEV_CONTAINER" + ;; + + test) + ensure_dev + build_module "$DEV_CONTAINER" + docker exec "$DEV_CONTAINER" pwsh -NoProfile -Command " + \$config = New-PesterConfiguration + \$config.Run.Path = 'tests/PSProxmoxVE.Tests' + \$config.Run.Exit = \$true + \$config.Filter.ExcludeTag = @('Integration') + \$config.Output.Verbosity = 'Detailed' + Invoke-Pester -Configuration \$config + " + ;; + + integration) + # Run integration tests against a pre-existing PVE (set via .env.test) + ensure_dev + build_module "$DEV_CONTAINER" + docker exec "$DEV_CONTAINER" bash -c " + SKIP_PROVISION=true bash $RUN_INTEGRATION test ${2:-all} + " + ;; + + provision) + # Full CI lifecycle: provision → test → cleanup (x86 infra container) + ensure_infra + build_module "$INFRA_CONTAINER" + docker exec "$INFRA_CONTAINER" bash "$RUN_INTEGRATION" all "${2:-all}" + ;; + + cleanup) + # Destroy provisioned VMs + ensure_infra + docker exec "$INFRA_CONTAINER" bash "$RUN_INTEGRATION" cleanup + ;; + + stop) + $COMPOSE --profile infra down + ;; + + rebuild) + $COMPOSE --profile infra down + $COMPOSE build --no-cache dev + $COMPOSE --profile infra build --no-cache dev-infra + $COMPOSE up -d dev + ;; + + *) + echo "Usage: $0 {shell|build|test|integration|provision|cleanup|stop|rebuild}" + echo "" + echo " shell Open pwsh in the dev container" + echo " build Build the module" + echo " test Run unit tests" + echo " integration Run integration tests against existing PVE (.env.test)" + echo " provision Full CI flow: provision → test → cleanup (x86 only)" + echo " cleanup Destroy provisioned VMs (x86 only)" + echo " stop Stop all containers" + echo " rebuild Rebuild container images" + exit 1 + ;; +esac diff --git a/tests/docker-compose.test.yml b/tests/docker-compose.test.yml new file mode 100644 index 0000000..e143dbc --- /dev/null +++ b/tests/docker-compose.test.yml @@ -0,0 +1,50 @@ +## PSProxmoxVE dev/test containers +## +## Default (works on ARM Macs + x86): +## docker compose -f tests/docker-compose.test.yml up -d +## docker exec -it psproxmoxve-dev pwsh +## +## Full CI infra (x86 only — adds Terraform + PVE provisioning tools): +## docker compose -f tests/docker-compose.test.yml --profile infra up -d +## docker exec -it psproxmoxve-dev-infra bash +## +## Stop: +## docker compose -f tests/docker-compose.test.yml down + +services: + dev: + build: + context: .. + dockerfile: tests/Dockerfile.test + target: dev + container_name: psproxmoxve-dev + volumes: + - ..:/repo + working_dir: /repo + stdin_open: true + tty: true + restart: unless-stopped + env_file: + - path: .env.test + required: false + + dev-infra: + build: + context: .. + dockerfile: tests/Dockerfile.test + target: dev-infra + container_name: psproxmoxve-dev-infra + profiles: [infra] + volumes: + - ..:/repo + - pve-isos:/opt/pve-isos + working_dir: /repo + stdin_open: true + tty: true + restart: unless-stopped + env_file: + - path: .env.test + required: false + +volumes: + pve-isos: