package webapp import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // makeFakeMetricsProc writes a minimal /proc tree with stat, meminfo, // loadavg, uptime, and net/dev files so CollectSnapshot can parse // it without a real /proc. func makeFakeMetricsProc(t *testing.T) string { t.Helper() root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "stat"), []byte(statFixture), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(root, "meminfo"), []byte(meminfoFixture), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(root, "loadavg"), []byte(loadavgFixture), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(root, "uptime"), []byte(uptimeFixture), 0o644)) require.NoError(t, os.MkdirAll(filepath.Join(root, "net"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(root, "net", "dev"), []byte(netdevFixture), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(root, "mounts"), []byte(mountsFixture), 0o644)) return root } const statFixture = `cpu 100 0 50 800 20 0 0 0 0 0 cpu0 25 0 12 200 5 0 0 0 0 0 intr 12345 0 0 0 ctxt 78910 btime 1700000000 ` const meminfoFixture = `MemTotal: 16384000 kB MemFree: 4096000 kB MemAvailable: 8192000 kB Buffers: 512000 kB Cached: 2048000 kB SwapCached: 0 kB SwapTotal: 2048000 kB SwapFree: 2048000 kB ` const loadavgFixture = "0.42 0.85 1.23 1/123 4567\n" const uptimeFixture = "12345.67\n" const netdevFixture = `Inter-| Receive | Transmit face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed lo: 1000000 1000 0 0 0 0 0 0 1000000 1000 0 0 0 0 0 0 eth0: 9999999 5000 1 2 0 0 0 0 8888888 4500 0 0 0 0 0 0 ` const mountsFixture = `/dev/sda1 / ext4 rw,relatime 0 0 tmpfs /run tmpfs rw,nosuid 0 0 ` func TestReadProcStatPercentages(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "stat"), []byte(statFixture), 0o644)) cpu, err := readProcStat(filepath.Join(root, "stat")) require.NoError(t, err) // Fixture: user=100, nice=0, system=50, idle=800, iowait=20, steal=0 // Total = 970, idle+iowait = 820, busy = 150. assert.InDelta(t, 10.31, cpu.UserPct, 0.01) assert.InDelta(t, 5.15, cpu.SystemPct, 0.01) assert.InDelta(t, 82.47, cpu.IdlePct, 0.01) assert.InDelta(t, 15.46, cpu.TotalPct, 0.01) } func TestReadMemInfoBytes(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "meminfo"), []byte(meminfoFixture), 0o644)) mem, err := readMemInfo(filepath.Join(root, "meminfo")) require.NoError(t, err) assert.Equal(t, uint64(16384000*1024), mem.Total) assert.Equal(t, uint64(8192000*1024), mem.Available) assert.Equal(t, uint64(2048000*1024), mem.SwapTotal) // used = total - available = 8192 MiB // pct = 8192 / 16384 * 100 = 50.00 assert.InDelta(t, 50.0, mem.UsedPct, 0.01) } func TestReadLoadAvg(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "loadavg"), []byte(loadavgFixture), 0o644)) load, err := readLoadAvg(filepath.Join(root, "loadavg")) require.NoError(t, err) assert.InDelta(t, 0.42, load.One, 0.001) assert.InDelta(t, 0.85, load.Five, 0.001) assert.InDelta(t, 1.23, load.Fifteen, 0.001) } func TestReadUptime(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "uptime"), []byte(uptimeFixture), 0o644)) d, err := readUptime(filepath.Join(root, "uptime")) require.NoError(t, err) assert.InDelta(t, 12345.67, d.Seconds(), 0.01) } func TestReadNetDev(t *testing.T) { root := t.TempDir() require.NoError(t, os.MkdirAll(filepath.Join(root, "net"), 0o755)) require.NoError(t, os.WriteFile(filepath.Join(root, "net", "dev"), []byte(netdevFixture), 0o644)) ifaces, err := readNetDev(filepath.Join(root, "net", "dev")) require.NoError(t, err) require.Len(t, ifaces, 2) eth := ifaces[1] assert.Equal(t, "eth0", eth.Name) assert.Equal(t, uint64(9999999), eth.RxBytes) assert.Equal(t, uint64(8888888), eth.TxBytes) assert.Equal(t, uint64(5000), eth.RxPkt) assert.Equal(t, uint64(1), eth.RxErr) } func TestReadMounts(t *testing.T) { root := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(root, "mounts"), []byte(mountsFixture), 0o644)) mounts, err := readMounts(filepath.Join(root, "mounts")) require.NoError(t, err) require.Len(t, mounts, 2) assert.Equal(t, "/", mounts[0].Mount) assert.Equal(t, "ext4", mounts[0].FSType) } func TestCollectSnapshotEndToEnd(t *testing.T) { root := makeFakeMetricsProc(t) snap, err := CollectSnapshot(root) require.NoError(t, err) assert.InDelta(t, 15.46, snap.CPU.TotalPct, 0.5, "CPU busy should be in the expected range") assert.InDelta(t, 50.0, snap.Memory.UsedPct, 0.5, "memory used should be ~50% from the fixture") assert.InDelta(t, 12345.67, snap.Uptime.Seconds(), 0.5) require.Len(t, snap.Networks, 2) require.Len(t, snap.Disks, 2) assert.False(t, snap.BootAt.IsZero(), "boot time must be derivable") }