Files
pulse/internal/monitoring/memory_reclaimable_test.go
T
rcourtman f62f35e24d memory: restore the v5 reclaimable-cache split end-to-end
v5 modeled memory as used | cache | free (Memory.Cache, 'reclaimable
buff/cache') and the memory bar's tooltip carried a 'Shown in Proxmox'
row explaining why Pulse's percentage reads lower than the Proxmox UI's
cache-inclusive number — a recurring support question. The v6 rebuild
deleted the field from the backend model, so the split and the
reconciliation vanished product-wide. Flagged by the Proxmox overview
parity audit.

Backend: re-add Memory.Cache; split it out via a shared
splitReclaimableMemory helper at the node resolver (node status reports
truly-free directly) and the VM builder (when guest meminfo reported
free pages); transport as proxmox.memoryCache on unified resources
alongside swap/balloon; mock mode populates plausible cache for nodes
and VMs.

Frontend: cache prop on StackedMemoryBar with the v5 muted-amber
segment between active and balloon, tooltip rows for reclaimable cache,
truly-free Free (balloon-capped), and the 'Shown in Proxmox'
reconciliation; guest and node memory adapters normalize free to
truly-free at the boundary; guest and node drawers grow a Reclaimable
cache row; the Proxmox nodes table passes cache and node swap through.
2026-06-11 19:42:40 +01:00

56 lines
1.7 KiB
Go

package monitoring
import (
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
)
func TestSplitReclaimableMemory(t *testing.T) {
t.Run("splits cache out of available-based free", func(t *testing.T) {
memory := models.Memory{Total: 16, Used: 6, Free: 10, Usage: 37.5}
splitReclaimableMemory(&memory, 4)
if memory.Cache != 6 {
t.Fatalf("Cache = %d, want 6", memory.Cache)
}
if memory.Free != 4 {
t.Fatalf("Free = %d, want 4", memory.Free)
}
if memory.Used != 6 || memory.Total != 16 {
t.Fatalf("Used/Total mutated: %+v", memory)
}
if got := memory.Used + memory.Cache + memory.Free; got != memory.Total {
t.Fatalf("used+cache+free = %d, want %d", got, memory.Total)
}
})
t.Run("no-op when truly free is unknown", func(t *testing.T) {
memory := models.Memory{Total: 16, Used: 6, Free: 10}
splitReclaimableMemory(&memory, 0)
if memory.Cache != 0 || memory.Free != 10 {
t.Fatalf("unexpected split: %+v", memory)
}
})
t.Run("no-op when truly free is not smaller than free", func(t *testing.T) {
memory := models.Memory{Total: 16, Used: 6, Free: 10}
splitReclaimableMemory(&memory, 10)
if memory.Cache != 0 || memory.Free != 10 {
t.Fatalf("unexpected split: %+v", memory)
}
splitReclaimableMemory(&memory, 12)
if memory.Cache != 0 || memory.Free != 10 {
t.Fatalf("unexpected split with oversized trulyFree: %+v", memory)
}
})
t.Run("no-op on nil or non-positive free", func(t *testing.T) {
splitReclaimableMemory(nil, 4)
memory := models.Memory{Total: 16, Used: 16, Free: 0}
splitReclaimableMemory(&memory, 4)
if memory.Cache != 0 {
t.Fatalf("unexpected split on zero free: %+v", memory)
}
})
}