From fe4db29941eff9a45d6bf56897cd26b79b045f23 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Sun, 30 Nov 2025 22:34:09 +0000 Subject: [PATCH] Add unit tests for diagnostic snapshot key generation functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 32 test cases covering makeNodeSnapshotKey and makeGuestSnapshotKey utility functions used for diagnostic memory snapshot storage. Tests cover simple inputs, edge cases (empty strings, negative VMIDs), special characters, and key uniqueness verification. Also includes struct field tests for NodeMemoryRaw and VMMemoryRaw. First test file for diagnostic_snapshots.go. Coverage 36.7% → 36.8%. --- .../monitoring/diagnostic_snapshots_test.go | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 internal/monitoring/diagnostic_snapshots_test.go 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) + } +}