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>
65 lines
3.2 KiB
Go
65 lines
3.2 KiB
Go
package proxmox
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestFormatDescription_keys(t *testing.T) {
|
|
t.Parallel()
|
|
created := time.Date(2026, 9, 23, 8, 0, 0, 0, time.FixedZone("CEST", 2*3600))
|
|
got := formatDescription("3f1c2d7e-uid", created)
|
|
want := "proxy-operator-managed=true\nproxy-operator-uid=3f1c2d7e-uid\nproxy-operator-created-at=2026-09-23T06:00:00Z"
|
|
if got != want {
|
|
t.Errorf("formatDescription() =\n%s\nwant\n%s", got, want)
|
|
}
|
|
m := parseDescription(got)
|
|
if !m.Managed || m.UID != "3f1c2d7e-uid" || !m.CreatedAt.Equal(created) {
|
|
t.Errorf("parseDescription(formatDescription()) = %+v, want managed, the UID and the time back", m)
|
|
}
|
|
}
|
|
|
|
// TestParseDescription_fromClone reads the mark back from the config of the
|
|
// VM cloned in Phase 0, as Proxmox stored it.
|
|
func TestParseDescription_fromClone(t *testing.T) {
|
|
t.Parallel()
|
|
cfg := decodeData[map[string]any](t, "vm_config_clone")
|
|
desc, _ := cfg["description"].(string)
|
|
m := parseDescription(desc)
|
|
want := time.Date(2026, 9, 23, 7, 0, 0, 0, time.UTC)
|
|
if !m.Managed || m.UID != "00000000-0000-0000-0000-00000000recon" || !m.CreatedAt.Equal(want) {
|
|
t.Errorf("parseDescription(captured) = %+v, want the recon mark", m)
|
|
}
|
|
}
|
|
|
|
func TestParseDescription_table(t *testing.T) {
|
|
t.Parallel()
|
|
ts := time.Date(2026, 9, 23, 7, 0, 0, 0, time.UTC)
|
|
tests := []struct {
|
|
name string
|
|
desc string
|
|
want instanceMark
|
|
}{
|
|
{name: "empty", desc: "", want: instanceMark{}},
|
|
{name: "unmarked free text", desc: "Ubuntu VM for testing", want: instanceMark{}},
|
|
{name: "managed with UID", desc: "proxy-operator-managed=true\nproxy-operator-uid=u1\nproxy-operator-created-at=2026-09-23T07:00:00Z", want: instanceMark{Managed: true, UID: "u1", CreatedAt: ts}},
|
|
{name: "managed with empty UID", desc: "proxy-operator-managed=true\nproxy-operator-uid=", want: instanceMark{Managed: true}},
|
|
{name: "managed without UID line", desc: "proxy-operator-managed=true", want: instanceMark{Managed: true}},
|
|
{name: "managed value must be exactly true", desc: "proxy-operator-managed=yes\nproxy-operator-uid=u1", want: instanceMark{UID: "u1"}},
|
|
{name: "unparsable created-at gives zero time", desc: "proxy-operator-managed=true\nproxy-operator-created-at=yesterday", want: instanceMark{Managed: true}},
|
|
{name: "CRLF line endings", desc: "proxy-operator-managed=true\r\nproxy-operator-uid=u1\r\n", want: instanceMark{Managed: true, UID: "u1"}},
|
|
{name: "admin lines kept around the mark", desc: "owner: team-crawl\n\nproxy-operator-managed=true\nnote=do not touch\nproxy-operator-uid=u1", want: instanceMark{Managed: true, UID: "u1"}},
|
|
{name: "first occurrence of a key wins", desc: "proxy-operator-uid=u1\nproxy-operator-uid=u2\nproxy-operator-managed=true", want: instanceMark{Managed: true, UID: "u1"}},
|
|
{name: "surrounding spaces trimmed", desc: " proxy-operator-managed=true \n proxy-operator-uid=u1", want: instanceMark{Managed: true, UID: "u1"}},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := parseDescription(tc.desc)
|
|
if got.Managed != tc.want.Managed || got.UID != tc.want.UID || !got.CreatedAt.Equal(tc.want.CreatedAt) {
|
|
t.Errorf("parseDescription(%q) = %+v, want %+v", tc.desc, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|