Files
egress-proxies-operator/internal/provider/proxmox/id_test.go
T
kacerrandClaude Opus 5.5 6e996ae2c7 proxmox: config block, IDs, marks, state mapping, classify, registration
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>
2026-09-23 09:42:46 +00:00

90 lines
2.6 KiB
Go

package proxmox
import (
"errors"
"testing"
"gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider"
)
func TestFormatProviderID_roundTrip(t *testing.T) {
t.Parallel()
for _, vmid := range []int{100, 150042, 999999999} {
id := formatProviderID(vmid, "proxy-abcdefghijklmnop")
gotVMID, gotName, err := parseProviderID(id)
if err != nil || gotVMID != vmid || gotName != "proxy-abcdefghijklmnop" {
t.Errorf("parseProviderID(%q) = %d, %q, %v; want %d, proxy-abcdefghijklmnop, nil", id, gotVMID, gotName, err, vmid)
}
}
if got := formatProviderID(150042, "proxy-x"); got != "vms/150042/proxy-x" {
t.Errorf("formatProviderID() = %q, want vms/150042/proxy-x", got)
}
}
func TestParseProviderID_malformed(t *testing.T) {
t.Parallel()
for _, id := range []string{
"",
"vms",
"vms/150042",
"vms/150042/",
"vms//proxy-x",
"vm/150042/proxy-x",
"hosts/beelink/domains/proxy-x",
"vms/150042/proxy-x/extra",
"vms/abc/proxy-x",
"vms/0150042/proxy-x",
"vms/+150042/proxy-x",
"vms/-5/proxy-x",
"vms/99/proxy-x",
"vms/1000000000/proxy-x",
} {
t.Run(id, func(t *testing.T) {
t.Parallel()
_, _, err := parseProviderID(id)
if !errors.Is(err, provider.ErrPermanent) {
t.Errorf("parseProviderID(%q) error = %v, want ErrPermanent", id, err)
}
})
}
}
func TestVMIDFor_deterministicAndInRange(t *testing.T) {
t.Parallel()
uids := []string{"3f1c2d7e-8a44-4f1b-9a6c-1d2e3f4a5b6c", "00000000-0000-0000-0000-000000000000", "x"}
for _, uid := range uids {
first := vmidFor(uid, 100000, 199999, 0)
if first < 100000 || first > 199999 {
t.Errorf("vmidFor(%q) = %d, want within 100000..199999", uid, first)
}
if again := vmidFor(uid, 100000, 199999, 0); again != first {
t.Errorf("vmidFor(%q) = %d then %d, want the same VMID every time", uid, first, again)
}
}
if vmidFor(uids[0], 100000, 199999, 0) == vmidFor(uids[1], 100000, 199999, 0) {
t.Error("two different UIDs got the same first VMID; the hash does not spread")
}
}
func TestVMIDFor_probesForwardAndWraps(t *testing.T) {
t.Parallel()
const min, max = 500, 509
uid := "3f1c2d7e-8a44-4f1b-9a6c-1d2e3f4a5b6c"
start := vmidFor(uid, min, max, 0)
seen := map[int]bool{}
for attempt := range 10 {
got := vmidFor(uid, min, max, attempt)
want := min + (start-min+attempt)%10
if got != want {
t.Errorf("attempt %d: vmidFor = %d, want %d (one step forward, wrapping at max)", attempt, got, want)
}
seen[got] = true
}
if len(seen) != 10 {
t.Errorf("10 attempts over a range of 10 visited %d VMIDs, want all 10", len(seen))
}
if got := vmidFor(uid, min, max, 10); got != start {
t.Errorf("attempt 10 = %d, want to wrap back to %d", got, start)
}
}