- Refactor collector to support mocking - Fix ZFS detection to support 'fuse.zfs' and case-insensitivity - Add regression tests for ZFS dataset deduplication
133 lines
4.2 KiB
Go
133 lines
4.2 KiB
Go
package hostmetrics
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
godisk "github.com/shirou/gopsutil/v4/disk"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestCollectDisks_ZFS_Deduplication(t *testing.T) {
|
|
// Save original functions and restore after test
|
|
origDiskPartitions := diskPartitions
|
|
origDiskUsage := diskUsage
|
|
defer func() {
|
|
diskPartitions = origDiskPartitions
|
|
diskUsage = origDiskUsage
|
|
}()
|
|
|
|
// Mock partitions
|
|
diskPartitions = func(ctx context.Context, all bool) ([]godisk.PartitionStat, error) {
|
|
return []godisk.PartitionStat{
|
|
{Device: "tank/dataset1", Mountpoint: "/mnt/dataset1", Fstype: "zfs"},
|
|
{Device: "tank/dataset2", Mountpoint: "/mnt/dataset2", Fstype: "zfs"},
|
|
{Device: "tank/dataset3", Mountpoint: "/mnt/dataset3", Fstype: "zfs"},
|
|
}, nil
|
|
}
|
|
|
|
// Mock usage
|
|
diskUsage = func(ctx context.Context, path string) (*godisk.UsageStat, error) {
|
|
// All datasets on the same pool share the same free space
|
|
return &godisk.UsageStat{
|
|
Total: 1000,
|
|
Used: 500,
|
|
Free: 500,
|
|
UsedPercent: 50.0,
|
|
}, nil
|
|
}
|
|
|
|
// Mock zpool stats query to fail so we test the fallback logic (which uses the datasets)
|
|
// We can't easily mock queryZpoolStats because it's in zfs.go and not exported/variable-ized in the same way,
|
|
// but we can rely on the fact that `exec.LookPath("zpool")` will likely fail or `zpool list` will fail in the test environment.
|
|
// However, to be sure, we should probably mock it.
|
|
// But wait, `summarizeZFSPools` calls `queryZpoolStats`.
|
|
// `queryZpoolStats` is a variable in zfs.go! `var queryZpoolStats = fetchZpoolStats`
|
|
|
|
// Let's mock queryZpoolStats too.
|
|
origQueryZpoolStats := queryZpoolStats
|
|
defer func() { queryZpoolStats = origQueryZpoolStats }()
|
|
|
|
queryZpoolStats = func(ctx context.Context, pools []string) (map[string]zpoolStats, error) {
|
|
return nil, assert.AnError // Simulate failure
|
|
}
|
|
|
|
ctx := context.Background()
|
|
disks := collectDisks(ctx)
|
|
|
|
// Should return 1 disk (the pool), not 3
|
|
assert.Len(t, disks, 1)
|
|
assert.Equal(t, "tank", disks[0].Device)
|
|
assert.Equal(t, int64(1000), disks[0].TotalBytes)
|
|
}
|
|
|
|
func TestCollectDisks_ZFS_CaseInsensitive(t *testing.T) {
|
|
// Save original functions
|
|
origDiskPartitions := diskPartitions
|
|
origDiskUsage := diskUsage
|
|
defer func() {
|
|
diskPartitions = origDiskPartitions
|
|
diskUsage = origDiskUsage
|
|
}()
|
|
|
|
// Mock partitions with "ZFS" (uppercase)
|
|
diskPartitions = func(ctx context.Context, all bool) ([]godisk.PartitionStat, error) {
|
|
return []godisk.PartitionStat{
|
|
{Device: "tank/dataset1", Mountpoint: "/mnt/dataset1", Fstype: "ZFS"},
|
|
{Device: "tank/dataset2", Mountpoint: "/mnt/dataset2", Fstype: "ZFS"},
|
|
}, nil
|
|
}
|
|
|
|
diskUsage = func(ctx context.Context, path string) (*godisk.UsageStat, error) {
|
|
return &godisk.UsageStat{Total: 1000, Used: 500, Free: 500}, nil
|
|
}
|
|
|
|
// Mock queryZpoolStats to fail
|
|
origQueryZpoolStats := queryZpoolStats
|
|
defer func() { queryZpoolStats = origQueryZpoolStats }()
|
|
queryZpoolStats = func(ctx context.Context, pools []string) (map[string]zpoolStats, error) {
|
|
return nil, assert.AnError
|
|
}
|
|
|
|
ctx := context.Background()
|
|
disks := collectDisks(ctx)
|
|
|
|
// If case-sensitive check fails, it will return 2 disks
|
|
// If we fix it, it should return 1
|
|
assert.Len(t, disks, 1)
|
|
}
|
|
|
|
func TestCollectDisks_ZFS_Fuse(t *testing.T) {
|
|
// Save original functions
|
|
origDiskPartitions := diskPartitions
|
|
origDiskUsage := diskUsage
|
|
defer func() {
|
|
diskPartitions = origDiskPartitions
|
|
diskUsage = origDiskUsage
|
|
}()
|
|
|
|
// Mock partitions with "fuse.zfs"
|
|
diskPartitions = func(ctx context.Context, all bool) ([]godisk.PartitionStat, error) {
|
|
return []godisk.PartitionStat{
|
|
{Device: "tank/dataset1", Mountpoint: "/mnt/dataset1", Fstype: "fuse.zfs"},
|
|
{Device: "tank/dataset2", Mountpoint: "/mnt/dataset2", Fstype: "fuse.zfs"},
|
|
}, nil
|
|
}
|
|
|
|
diskUsage = func(ctx context.Context, path string) (*godisk.UsageStat, error) {
|
|
return &godisk.UsageStat{Total: 1000, Used: 500, Free: 500}, nil
|
|
}
|
|
|
|
// Mock queryZpoolStats to fail
|
|
origQueryZpoolStats := queryZpoolStats
|
|
defer func() { queryZpoolStats = origQueryZpoolStats }()
|
|
queryZpoolStats = func(ctx context.Context, pools []string) (map[string]zpoolStats, error) {
|
|
return nil, assert.AnError
|
|
}
|
|
|
|
ctx := context.Background()
|
|
disks := collectDisks(ctx)
|
|
|
|
// If check fails, it will return 2 disks
|
|
assert.Len(t, disks, 1)
|
|
}
|