diff --git a/internal/monitoring/diagnostic_snapshots_test.go b/internal/monitoring/diagnostic_snapshots_test.go new file mode 100644 index 000000000..1ffbcff18 --- /dev/null +++ b/internal/monitoring/diagnostic_snapshots_test.go @@ -0,0 +1,332 @@ +package monitoring + +import ( + "testing" +) + +func TestMakeNodeSnapshotKey(t *testing.T) { + tests := []struct { + name string + instance string + node string + expected string + }{ + { + name: "simple instance and node", + instance: "pve1", + node: "node1", + expected: "pve1|node1", + }, + { + name: "instance with port", + instance: "pve.example.com:8006", + node: "pve-node", + expected: "pve.example.com:8006|pve-node", + }, + { + name: "IP address instance", + instance: "192.168.1.100", + node: "server01", + expected: "192.168.1.100|server01", + }, + { + name: "empty instance", + instance: "", + node: "node1", + expected: "|node1", + }, + { + name: "empty node", + instance: "pve1", + node: "", + expected: "pve1|", + }, + { + name: "both empty", + instance: "", + node: "", + expected: "|", + }, + { + name: "special characters in node name", + instance: "pve1", + node: "node-with-dashes_and_underscores", + expected: "pve1|node-with-dashes_and_underscores", + }, + { + name: "FQDN instance", + instance: "proxmox.datacenter.example.com", + node: "pve-cluster-node-01", + expected: "proxmox.datacenter.example.com|pve-cluster-node-01", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := makeNodeSnapshotKey(tc.instance, tc.node) + if got != tc.expected { + t.Errorf("makeNodeSnapshotKey(%q, %q) = %q, want %q", + tc.instance, tc.node, got, tc.expected) + } + }) + } +} + +func TestMakeGuestSnapshotKey(t *testing.T) { + tests := []struct { + name string + instance string + guestType string + node string + vmid int + expected string + }{ + { + name: "VM guest", + instance: "pve1", + guestType: "qemu", + node: "node1", + vmid: 100, + expected: "pve1|qemu|node1|100", + }, + { + name: "LXC container", + instance: "pve1", + guestType: "lxc", + node: "node1", + vmid: 200, + expected: "pve1|lxc|node1|200", + }, + { + name: "high VMID", + instance: "pve1", + guestType: "qemu", + node: "node1", + vmid: 999999, + expected: "pve1|qemu|node1|999999", + }, + { + name: "zero VMID", + instance: "pve1", + guestType: "qemu", + node: "node1", + vmid: 0, + expected: "pve1|qemu|node1|0", + }, + { + name: "negative VMID", + instance: "pve1", + guestType: "qemu", + node: "node1", + vmid: -1, + expected: "pve1|qemu|node1|-1", + }, + { + name: "instance with port", + instance: "pve.example.com:8006", + guestType: "lxc", + node: "pve-node", + vmid: 101, + expected: "pve.example.com:8006|lxc|pve-node|101", + }, + { + name: "empty instance", + instance: "", + guestType: "qemu", + node: "node1", + vmid: 100, + expected: "|qemu|node1|100", + }, + { + name: "empty guest type", + instance: "pve1", + guestType: "", + node: "node1", + vmid: 100, + expected: "pve1||node1|100", + }, + { + name: "empty node", + instance: "pve1", + guestType: "qemu", + node: "", + vmid: 100, + expected: "pve1|qemu||100", + }, + { + name: "all empty except VMID", + instance: "", + guestType: "", + node: "", + vmid: 42, + expected: "|||42", + }, + { + name: "FQDN with complex node name", + instance: "proxmox.datacenter.example.com", + guestType: "qemu", + node: "pve-cluster-node-01", + vmid: 12345, + expected: "proxmox.datacenter.example.com|qemu|pve-cluster-node-01|12345", + }, + { + name: "IP address instance", + instance: "192.168.1.100", + guestType: "lxc", + node: "server01", + vmid: 500, + expected: "192.168.1.100|lxc|server01|500", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := makeGuestSnapshotKey(tc.instance, tc.guestType, tc.node, tc.vmid) + if got != tc.expected { + t.Errorf("makeGuestSnapshotKey(%q, %q, %q, %d) = %q, want %q", + tc.instance, tc.guestType, tc.node, tc.vmid, got, tc.expected) + } + }) + } +} + +func TestMakeSnapshotKey_Uniqueness(t *testing.T) { + // Test that different inputs produce different keys + + t.Run("node keys with different instances are unique", func(t *testing.T) { + key1 := makeNodeSnapshotKey("pve1", "node1") + key2 := makeNodeSnapshotKey("pve2", "node1") + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) + + t.Run("node keys with different nodes are unique", func(t *testing.T) { + key1 := makeNodeSnapshotKey("pve1", "node1") + key2 := makeNodeSnapshotKey("pve1", "node2") + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) + + t.Run("guest keys with different VMIDs are unique", func(t *testing.T) { + key1 := makeGuestSnapshotKey("pve1", "qemu", "node1", 100) + key2 := makeGuestSnapshotKey("pve1", "qemu", "node1", 101) + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) + + t.Run("guest keys with different guest types are unique", func(t *testing.T) { + key1 := makeGuestSnapshotKey("pve1", "qemu", "node1", 100) + key2 := makeGuestSnapshotKey("pve1", "lxc", "node1", 100) + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) + + t.Run("guest keys with different nodes are unique", func(t *testing.T) { + key1 := makeGuestSnapshotKey("pve1", "qemu", "node1", 100) + key2 := makeGuestSnapshotKey("pve1", "qemu", "node2", 100) + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) + + t.Run("guest keys with different instances are unique", func(t *testing.T) { + key1 := makeGuestSnapshotKey("pve1", "qemu", "node1", 100) + key2 := makeGuestSnapshotKey("pve2", "qemu", "node1", 100) + if key1 == key2 { + t.Errorf("Keys should be unique: %q == %q", key1, key2) + } + }) +} + +func TestDiagnosticSnapshotSet_Fields(t *testing.T) { + // Test struct field initialization + set := DiagnosticSnapshotSet{ + Nodes: []NodeMemorySnapshot{}, + Guests: []GuestMemorySnapshot{}, + } + + if set.Nodes == nil { + t.Error("Nodes should not be nil") + } + if set.Guests == nil { + t.Error("Guests should not be nil") + } + if len(set.Nodes) != 0 { + t.Errorf("Nodes length = %d, want 0", len(set.Nodes)) + } + if len(set.Guests) != 0 { + t.Errorf("Guests length = %d, want 0", len(set.Guests)) + } +} + +func TestNodeMemoryRaw_Fields(t *testing.T) { + raw := NodeMemoryRaw{ + Total: 16000000000, + Used: 8000000000, + Free: 8000000000, + Available: 10000000000, + Buffers: 500000000, + Cached: 2000000000, + ProxmoxMemorySource: "rrd-available", + } + + if raw.Total != 16000000000 { + t.Errorf("Total = %d, want 16000000000", raw.Total) + } + if raw.Used != 8000000000 { + t.Errorf("Used = %d, want 8000000000", raw.Used) + } + if raw.Free != 8000000000 { + t.Errorf("Free = %d, want 8000000000", raw.Free) + } + if raw.Available != 10000000000 { + t.Errorf("Available = %d, want 10000000000", raw.Available) + } + if raw.Buffers != 500000000 { + t.Errorf("Buffers = %d, want 500000000", raw.Buffers) + } + if raw.Cached != 2000000000 { + t.Errorf("Cached = %d, want 2000000000", raw.Cached) + } + if raw.ProxmoxMemorySource != "rrd-available" { + t.Errorf("ProxmoxMemorySource = %q, want %q", raw.ProxmoxMemorySource, "rrd-available") + } +} + +func TestVMMemoryRaw_Fields(t *testing.T) { + raw := VMMemoryRaw{ + ListingMem: 2000000000, + ListingMaxMem: 4000000000, + StatusMem: 2100000000, + StatusMaxMem: 4000000000, + Balloon: 3000000000, + BalloonMin: 1000000000, + Agent: 1, + } + + if raw.ListingMem != 2000000000 { + t.Errorf("ListingMem = %d, want 2000000000", raw.ListingMem) + } + if raw.ListingMaxMem != 4000000000 { + t.Errorf("ListingMaxMem = %d, want 4000000000", raw.ListingMaxMem) + } + if raw.StatusMem != 2100000000 { + t.Errorf("StatusMem = %d, want 2100000000", raw.StatusMem) + } + if raw.StatusMaxMem != 4000000000 { + t.Errorf("StatusMaxMem = %d, want 4000000000", raw.StatusMaxMem) + } + if raw.Balloon != 3000000000 { + t.Errorf("Balloon = %d, want 3000000000", raw.Balloon) + } + if raw.BalloonMin != 1000000000 { + t.Errorf("BalloonMin = %d, want 1000000000", raw.BalloonMin) + } + if raw.Agent != 1 { + t.Errorf("Agent = %d, want 1", raw.Agent) + } +}