Phase 2 of the proxmox provider plan. Adds the proxmox: config block with its validation, registers the type in the config allow-list and the constructor map, and starts internal/provider/proxmox: the api seam and its data types, providerIDs (vms/<vmid>/<name>) with the UID-derived VMID, the description mark, state mapping, and error classification. Tests read the responses captured from pv1-1 in Phase 0 rather than repeating Proxmox's wording, and every matcher is run against every captured error so none fires on the wrong message. The contract methods are Transient stubs until Phases 5 to 7. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
98 lines
3.5 KiB
Go
98 lines
3.5 KiB
Go
package proxmox
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestAPITypes_decodeCaptures decodes the Phase 0 responses into the seam's
|
|
// types, so a misspelled JSON tag fails here and not in a later phase.
|
|
func TestAPITypes_decodeCaptures(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("version", func(t *testing.T) {
|
|
t.Parallel()
|
|
if v := decodeData[versionInfo](t, "version"); v.Version != "9.2.20" || v.Release != "9.2" || v.RepoID == "" {
|
|
t.Errorf("version = %+v", v)
|
|
}
|
|
})
|
|
t.Run("node status", func(t *testing.T) {
|
|
t.Parallel()
|
|
n := decodeData[nodeStatus](t, "node_status")
|
|
if !strings.HasPrefix(n.PVEVersion, "pve-manager/9.2.20") || n.CurrentKernel.Release != "7.0.14-17-pve" {
|
|
t.Errorf("node status = %+v", n)
|
|
}
|
|
})
|
|
t.Run("storage status", func(t *testing.T) {
|
|
t.Parallel()
|
|
s := decodeData[storageStatus](t, "storage_status")
|
|
if s.Type != "dir" || !strings.Contains(s.Content, "iso") || s.Active != 1 || s.Avail <= 0 || s.Total < s.Avail {
|
|
t.Errorf("storage status = %+v", s)
|
|
}
|
|
})
|
|
t.Run("cluster resources", func(t *testing.T) {
|
|
t.Parallel()
|
|
rs := decodeData[[]vmResource](t, "cluster_resources")
|
|
byID := map[int]vmResource{}
|
|
for _, r := range rs {
|
|
byID[r.VMID] = r
|
|
}
|
|
if r := byID[9001]; r.Name != "debian-13-template" || r.Template != 1 || r.Node != "pv1-1" || r.Type != "qemu" || r.Status != "stopped" {
|
|
t.Errorf("resources[9001] = %+v", r)
|
|
}
|
|
if r := byID[100]; r.Template != 0 || r.Name != "ubuntu-vm-1" {
|
|
t.Errorf("resources[100] = %+v", r)
|
|
}
|
|
})
|
|
t.Run("cluster resources during clone", func(t *testing.T) {
|
|
t.Parallel()
|
|
for _, r := range decodeData[[]vmResource](t, "cluster_resources_during_clone") {
|
|
if r.VMID != 199999 {
|
|
continue
|
|
}
|
|
if r.Name != "" || r.Lock != "clone" || r.Status != "unknown" {
|
|
t.Errorf("mid-clone entry = %+v, want no name, lock clone, status unknown", r)
|
|
}
|
|
return
|
|
}
|
|
t.Error("no entry for VMID 199999 in the mid-clone capture")
|
|
})
|
|
t.Run("cluster resources tags", func(t *testing.T) {
|
|
t.Parallel()
|
|
for _, r := range decodeData[[]vmResource](t, "cluster_resources_during_destroy") {
|
|
if r.VMID == 199999 && r.Tags != "proxy_operator" {
|
|
t.Errorf("tags = %q, want proxy_operator", r.Tags)
|
|
}
|
|
}
|
|
})
|
|
t.Run("status current", func(t *testing.T) {
|
|
t.Parallel()
|
|
if s := decodeData[vmStatus](t, "status_current_paused"); s.Status != "running" || s.QMPStatus != "paused" {
|
|
t.Errorf("paused status = %+v", s)
|
|
}
|
|
if s := decodeData[vmStatus](t, "status_current_stopped"); s.Status != "stopped" || s.QMPStatus != "stopped" {
|
|
t.Errorf("stopped status = %+v", s)
|
|
}
|
|
})
|
|
t.Run("task status", func(t *testing.T) {
|
|
t.Parallel()
|
|
if s := decodeData[taskStatus](t, "task_status_stopped_error"); s.Status != "stopped" || s.ExitStatus != "shrinking disks is not supported" || s.Type != "resize" {
|
|
t.Errorf("failed task = %+v", s)
|
|
}
|
|
if s := decodeData[taskStatus](t, "task_status_running"); s.Status != "running" || s.ExitStatus != "" || s.Node != "pv1-1" {
|
|
t.Errorf("running task = %+v", s)
|
|
}
|
|
if s := decodeData[taskStatus](t, "task_status_volume_missing"); s.ExitStatus != "OK" {
|
|
t.Errorf("deleting a missing volume = %+v, want exitstatus OK", s)
|
|
}
|
|
})
|
|
t.Run("UPID responses", func(t *testing.T) {
|
|
t.Parallel()
|
|
for _, c := range []string{"clone_response", "start_response", "stop_response", "destroy_response", "resize_response", "upload_response", "content_delete_response"} {
|
|
if upid := decodeData[string](t, c); !strings.HasPrefix(upid, "UPID:pv1-1:") {
|
|
t.Errorf("%s = %q, want a UPID of node pv1-1", c, upid)
|
|
}
|
|
}
|
|
})
|
|
}
|