From 80c3fce85a46fe62c5a8627ad4c2930f0927ebf6 Mon Sep 17 00:00:00 2001 From: soup Date: Thu, 9 Apr 2026 20:34:25 +0200 Subject: [PATCH 1/4] fix: honor explicit disk includes Make disk_includes a hard override for small and normally filtered mounts. Deduplicate bind mounts by default while preserving explicitly included bind mounts, and document the behavior. --- README.md | 6 +- cmd/netronome/main.go | 4 +- internal/agent/disk_utils.go | 141 ++++++++++++++++++++++++++++-- internal/agent/disk_utils_test.go | 109 +++++++++++++++++++++++ internal/agent/hardware.go | 31 ++----- 5 files changed, 257 insertions(+), 34 deletions(-) create mode 100644 internal/agent/disk_utils_test.go diff --git a/README.md b/README.md index 67a564d..7b164fa 100644 --- a/README.md +++ b/README.md @@ -434,13 +434,15 @@ host = "0.0.0.0" port = 8200 interface = "" # Empty for all interfaces api_key = "your-secret-key" -disk_includes = ["/mnt/storage"] # Additional mounts to monitor +disk_includes = ["/mnt/storage"] # Hard override: include these mounts even if small, tmpfs, or bind mounts disk_excludes = ["/boot", "/tmp"] # Mounts to exclude [monitor] enabled = true ``` +`disk_includes` is a hard override. Explicitly included mounts are reported even if they would normally be skipped for being special filesystems or smaller than 1 GiB. Disk reporting also dedupes bind mounts by default; explicitly included bind mounts are kept. + ### Packet Loss Monitoring Continuous network monitoring with MTR integration and performance tracking. @@ -796,7 +798,7 @@ NETRONOME__AGENT_HOST=0.0.0.0 # Agent listen address NETRONOME__AGENT_PORT=8200 # Agent port NETRONOME__AGENT_INTERFACE= # Network interface to monitor (empty for all) NETRONOME__AGENT_API_KEY= # Agent API key for authentication -NETRONOME__AGENT_DISK_INCLUDES= # Comma-separated paths to include +NETRONOME__AGENT_DISK_INCLUDES= # Comma-separated hard override include paths NETRONOME__AGENT_DISK_EXCLUDES= # Comma-separated paths to exclude ``` diff --git a/cmd/netronome/main.go b/cmd/netronome/main.go index 2143910..ffc70f2 100644 --- a/cmd/netronome/main.go +++ b/cmd/netronome/main.go @@ -42,7 +42,7 @@ var ( rootCmd = &cobra.Command{ Use: "netronome", Short: "Netronome is a network performance testing and monitoring tool", - Long: `Netronome is a network performance testing and monitoring tool that helps you + Long: `Netronome is a network performance testing and monitoring tool that helps you track and analyze your network performance over time.`, CompletionOptions: cobra.CompletionOptions{ DisableDefaultCmd: true, @@ -118,7 +118,7 @@ func init() { agentCmd.Flags().StringP("interface", "i", "", "network interface to monitor (empty for all)") agentCmd.Flags().StringP("api-key", "k", "", "API key for authentication") agentCmd.Flags().StringP("log-level", "l", "", "log level (trace, debug, info, warn, error)") - agentCmd.Flags().StringSlice("disk-include", []string{}, "additional disk mount points to monitor (e.g., /mnt/storage)") + agentCmd.Flags().StringSlice("disk-include", []string{}, "disk mount points to force into monitoring, even if small or normally filtered (e.g., /mnt/storage)") agentCmd.Flags().StringSlice("disk-exclude", []string{}, "disk mount points to exclude from monitoring (e.g., /boot)") agentCmd.Flags().Bool("disable-system-metrics", false, "disable system metrics collection (CPU, memory, disk, temperature)") agentCmd.Flags().Bool("tailscale", false, "enable Tailscale for secure connectivity") diff --git a/internal/agent/disk_utils.go b/internal/agent/disk_utils.go index ef8321c..055bb70 100644 --- a/internal/agent/disk_utils.go +++ b/internal/agent/disk_utils.go @@ -4,14 +4,24 @@ package agent import ( + "fmt" "os" "path/filepath" "runtime" "strings" "github.com/rs/zerolog/log" + "github.com/shirou/gopsutil/v4/disk" ) +const minDiskReportSize = 1024 * 1024 * 1024 + +type diskReportEntry struct { + Partition disk.PartitionStat + Usage *disk.UsageStat + ExplicitInclude bool +} + // matchPath checks if a path matches a pattern, supporting both glob and prefix patterns func matchPath(pattern, path string) bool { // Check if pattern ends with * for prefix matching @@ -36,13 +46,12 @@ func matchPath(pattern, path string) bool { return pattern == path } -// shouldIncludeDisk determines if a disk should be included in monitoring based on filter rules -func (a *Agent) shouldIncludeDisk(mountpoint, device, fstype string) bool { +func (a *Agent) diskFilterDecision(mountpoint, device, fstype string) (include bool, explicit bool) { // First check if it's explicitly included - this takes precedence for _, pattern := range a.config.DiskIncludes { if matchPath(pattern, mountpoint) { log.Debug().Str("mount", mountpoint).Str("pattern", pattern).Msg("Disk included by pattern match") - return true + return true, true } } @@ -50,7 +59,7 @@ func (a *Agent) shouldIncludeDisk(mountpoint, device, fstype string) bool { for _, pattern := range a.config.DiskExcludes { if matchPath(pattern, mountpoint) { log.Debug().Str("mount", mountpoint).Str("pattern", pattern).Msg("Disk excluded by pattern match") - return false + return false, false } } @@ -73,11 +82,131 @@ func (a *Agent) shouldIncludeDisk(mountpoint, device, fstype string) bool { fstype == "sysfs" || fstype == "cgroup" || fstype == "cgroup2" { - return false + return false, false } // Include everything else by default - return true + return true, false +} + +// shouldIncludeDisk determines if a disk should be included in monitoring based on filter rules +func (a *Agent) shouldIncludeDisk(mountpoint, device, fstype string) bool { + include, _ := a.diskFilterDecision(mountpoint, device, fstype) + return include +} + +func (a *Agent) buildDiskReportEntries(partitions []disk.PartitionStat, usageFn func(string) (*disk.UsageStat, error)) []diskReportEntry { + entries := make([]diskReportEntry, 0, len(partitions)) + + for _, partition := range partitions { + include, explicit := a.diskFilterDecision(partition.Mountpoint, partition.Device, partition.Fstype) + if !include { + log.Debug(). + Str("mount", partition.Mountpoint). + Str("device", partition.Device). + Str("fstype", partition.Fstype). + Msg("Skipping disk based on filter rules") + continue + } + + usage, err := usageFn(partition.Mountpoint) + if err != nil { + log.Debug().Err(err).Str("mount", partition.Mountpoint).Msg("Failed to get disk usage") + continue + } + + if !explicit && usage.Total < minDiskReportSize { + log.Debug(). + Str("mount", partition.Mountpoint). + Uint64("total", usage.Total). + Msg("Skipping small disk") + continue + } + + entries = append(entries, diskReportEntry{ + Partition: partition, + Usage: usage, + ExplicitInclude: explicit, + }) + } + + return dedupeDiskReportEntries(entries) +} + +func dedupeDiskReportEntries(entries []diskReportEntry) []diskReportEntry { + if len(entries) < 2 { + return entries + } + + grouped := make(map[string][]int, len(entries)) + for i, entry := range entries { + grouped[diskReportSignature(entry)] = append(grouped[diskReportSignature(entry)], i) + } + + keep := make([]bool, len(entries)) + + for _, indexes := range grouped { + if len(indexes) == 1 { + keep[indexes[0]] = true + continue + } + + preferred := indexes[0] + for _, idx := range indexes[1:] { + if prefersDiskEntry(entries[idx], entries[preferred]) { + preferred = idx + } + } + + keep[preferred] = true + + for _, idx := range indexes { + if idx == preferred { + continue + } + if entries[idx].ExplicitInclude || !isBindMount(entries[idx].Partition.Opts) { + keep[idx] = true + } + } + } + + deduped := make([]diskReportEntry, 0, len(entries)) + for i, entry := range entries { + if keep[i] { + deduped = append(deduped, entry) + } + } + + return deduped +} + +func diskReportSignature(entry diskReportEntry) string { + return fmt.Sprintf("%s|%s|%d|%d|%d", entry.Partition.Device, entry.Partition.Fstype, entry.Usage.Total, entry.Usage.Used, entry.Usage.Free) +} + +func prefersDiskEntry(left, right diskReportEntry) bool { + leftBind := isBindMount(left.Partition.Opts) + rightBind := isBindMount(right.Partition.Opts) + + if leftBind != rightBind { + return !leftBind + } + + if len(left.Partition.Mountpoint) != len(right.Partition.Mountpoint) { + return len(left.Partition.Mountpoint) < len(right.Partition.Mountpoint) + } + + return left.Partition.Mountpoint < right.Partition.Mountpoint +} + +func isBindMount(opts []string) bool { + for _, opt := range opts { + if opt == "bind" { + return true + } + } + + return false } // getDevicePaths returns a list of device paths to check for SMART data diff --git a/internal/agent/disk_utils_test.go b/internal/agent/disk_utils_test.go new file mode 100644 index 0000000..76d2edb --- /dev/null +++ b/internal/agent/disk_utils_test.go @@ -0,0 +1,109 @@ +// Copyright (c) 2024-2026, s0up and the autobrr contributors. +// SPDX-License-Identifier: GPL-2.0-or-later + +package agent + +import ( + "testing" + + "github.com/shirou/gopsutil/v4/disk" + "github.com/stretchr/testify/assert" + + "github.com/autobrr/netronome/internal/config" +) + +func TestBuildDiskReportEntries_ExplicitIncludeBypassesSizeFilter(t *testing.T) { + agent := New(&config.AgentConfig{ + DiskIncludes: []string{"/var/log"}, + }) + + partitions := []disk.PartitionStat{ + { + Device: "tmpfs", + Mountpoint: "/var/log", + Fstype: "tmpfs", + }, + } + + entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 128 * 1024 * 1024, + Used: 64 * 1024 * 1024, + Free: 64 * 1024 * 1024, + UsedPercent: 50, + }, nil + }) + + assert.Len(t, entries, 1) + assert.Equal(t, "/var/log", entries[0].Partition.Mountpoint) + assert.True(t, entries[0].ExplicitInclude) +} + +func TestBuildDiskReportEntries_DedupesNonExplicitBindMounts(t *testing.T) { + agent := New(&config.AgentConfig{}) + + partitions := []disk.PartitionStat{ + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/", + Fstype: "ext4", + Opts: []string{"rw", "relatime"}, + }, + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/var/hdd.log", + Fstype: "ext4", + Opts: []string{"rw", "relatime", "bind"}, + }, + } + + entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 32 * 1024 * 1024 * 1024, + Used: 8 * 1024 * 1024 * 1024, + Free: 24 * 1024 * 1024 * 1024, + UsedPercent: 25, + }, nil + }) + + assert.Len(t, entries, 1) + assert.Equal(t, "/", entries[0].Partition.Mountpoint) +} + +func TestBuildDiskReportEntries_KeepsExplicitlyIncludedBindMounts(t *testing.T) { + agent := New(&config.AgentConfig{ + DiskIncludes: []string{"/var/hdd.log"}, + }) + + partitions := []disk.PartitionStat{ + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/", + Fstype: "ext4", + Opts: []string{"rw", "relatime"}, + }, + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/var/hdd.log", + Fstype: "ext4", + Opts: []string{"rw", "relatime", "bind"}, + }, + } + + entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 32 * 1024 * 1024 * 1024, + Used: 8 * 1024 * 1024 * 1024, + Free: 24 * 1024 * 1024 * 1024, + UsedPercent: 25, + }, nil + }) + + assert.Len(t, entries, 2) + assert.Equal(t, "/", entries[0].Partition.Mountpoint) + assert.Equal(t, "/var/hdd.log", entries[1].Partition.Mountpoint) + assert.True(t, entries[1].ExplicitInclude) +} diff --git a/internal/agent/hardware.go b/internal/agent/hardware.go index acdd663..3b85950 100644 --- a/internal/agent/hardware.go +++ b/internal/agent/hardware.go @@ -197,32 +197,15 @@ func (a *Agent) getHardwareStats() (*HardwareStats, error) { } } - for _, partition := range partitions { - // Check if this disk should be included based on filters - if !a.shouldIncludeDisk(partition.Mountpoint, partition.Device, partition.Fstype) { - log.Debug(). - Str("mount", partition.Mountpoint). - Str("device", partition.Device). - Str("fstype", partition.Fstype). - Msg("Skipping disk based on filter rules") - continue - } - - usage, err := disk.Usage(partition.Mountpoint) + for _, entry := range a.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { + usage, err := disk.Usage(mountpoint) if err != nil { - log.Debug().Err(err).Str("mount", partition.Mountpoint).Msg("Failed to get disk usage") - continue + return nil, err } - - // Skip if disk is too small (less than 1GB) - if usage.Total < 1024*1024*1024 { - log.Debug(). - Str("mount", partition.Mountpoint). - Uint64("total", usage.Total). - Msg("Skipping small disk") - continue - } - + return usage, nil + }) { + partition := entry.Partition + usage := entry.Usage diskStat := DiskStats{ Path: partition.Mountpoint, Device: partition.Device, From 75b2748bcacd638a81f5eabb96d2e83e0cd20b6d Mon Sep 17 00:00:00 2001 From: soup Date: Thu, 9 Apr 2026 21:05:35 +0200 Subject: [PATCH 2/4] fix: tighten disk dedupe keys Remove the unused disk include wrapper and use a stable dedupe signature based on device, filesystem, and total size. Refactor the disk selection tests into a table-driven form and add coverage for include-over-exclude precedence. --- internal/agent/disk_utils.go | 8 +- internal/agent/disk_utils_test.go | 206 ++++++++++++++++++------------ 2 files changed, 124 insertions(+), 90 deletions(-) diff --git a/internal/agent/disk_utils.go b/internal/agent/disk_utils.go index 055bb70..d5740d0 100644 --- a/internal/agent/disk_utils.go +++ b/internal/agent/disk_utils.go @@ -89,12 +89,6 @@ func (a *Agent) diskFilterDecision(mountpoint, device, fstype string) (include b return true, false } -// shouldIncludeDisk determines if a disk should be included in monitoring based on filter rules -func (a *Agent) shouldIncludeDisk(mountpoint, device, fstype string) bool { - include, _ := a.diskFilterDecision(mountpoint, device, fstype) - return include -} - func (a *Agent) buildDiskReportEntries(partitions []disk.PartitionStat, usageFn func(string) (*disk.UsageStat, error)) []diskReportEntry { entries := make([]diskReportEntry, 0, len(partitions)) @@ -181,7 +175,7 @@ func dedupeDiskReportEntries(entries []diskReportEntry) []diskReportEntry { } func diskReportSignature(entry diskReportEntry) string { - return fmt.Sprintf("%s|%s|%d|%d|%d", entry.Partition.Device, entry.Partition.Fstype, entry.Usage.Total, entry.Usage.Used, entry.Usage.Free) + return fmt.Sprintf("%s|%s|%d", entry.Partition.Device, entry.Partition.Fstype, entry.Usage.Total) } func prefersDiskEntry(left, right diskReportEntry) bool { diff --git a/internal/agent/disk_utils_test.go b/internal/agent/disk_utils_test.go index 76d2edb..002fe2a 100644 --- a/internal/agent/disk_utils_test.go +++ b/internal/agent/disk_utils_test.go @@ -12,98 +12,138 @@ import ( "github.com/autobrr/netronome/internal/config" ) -func TestBuildDiskReportEntries_ExplicitIncludeBypassesSizeFilter(t *testing.T) { - agent := New(&config.AgentConfig{ - DiskIncludes: []string{"/var/log"}, - }) - - partitions := []disk.PartitionStat{ +func TestBuildDiskReportEntries(t *testing.T) { + tests := []struct { + name string + cfg config.AgentConfig + partitions []disk.PartitionStat + usageFn func(string) (*disk.UsageStat, error) + expectedMountpoints []string + expectedExplicitIncludes []bool + }{ { - Device: "tmpfs", - Mountpoint: "/var/log", - Fstype: "tmpfs", + name: "explicit include bypasses size filter", + cfg: config.AgentConfig{ + DiskIncludes: []string{"/var/log"}, + }, + partitions: []disk.PartitionStat{ + { + Device: "tmpfs", + Mountpoint: "/var/log", + Fstype: "tmpfs", + }, + }, + usageFn: func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 128 * 1024 * 1024, + Used: 64 * 1024 * 1024, + Free: 64 * 1024 * 1024, + UsedPercent: 50, + }, nil + }, + expectedMountpoints: []string{"/var/log"}, + expectedExplicitIncludes: []bool{true}, }, - } - - entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { - return &disk.UsageStat{ - Path: mountpoint, - Total: 128 * 1024 * 1024, - Used: 64 * 1024 * 1024, - Free: 64 * 1024 * 1024, - UsedPercent: 50, - }, nil - }) - - assert.Len(t, entries, 1) - assert.Equal(t, "/var/log", entries[0].Partition.Mountpoint) - assert.True(t, entries[0].ExplicitInclude) -} - -func TestBuildDiskReportEntries_DedupesNonExplicitBindMounts(t *testing.T) { - agent := New(&config.AgentConfig{}) - - partitions := []disk.PartitionStat{ { - Device: "/dev/mmcblk0p2", - Mountpoint: "/", - Fstype: "ext4", - Opts: []string{"rw", "relatime"}, + name: "dedupes non explicit bind mounts", + cfg: config.AgentConfig{}, + partitions: []disk.PartitionStat{ + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/", + Fstype: "ext4", + Opts: []string{"rw", "relatime"}, + }, + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/var/hdd.log", + Fstype: "ext4", + Opts: []string{"rw", "relatime", "bind"}, + }, + }, + usageFn: func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 32 * 1024 * 1024 * 1024, + Used: 8 * 1024 * 1024 * 1024, + Free: 24 * 1024 * 1024 * 1024, + UsedPercent: 25, + }, nil + }, + expectedMountpoints: []string{"/"}, + expectedExplicitIncludes: []bool{false}, }, { - Device: "/dev/mmcblk0p2", - Mountpoint: "/var/hdd.log", - Fstype: "ext4", - Opts: []string{"rw", "relatime", "bind"}, + name: "keeps explicitly included bind mounts", + cfg: config.AgentConfig{ + DiskIncludes: []string{"/var/hdd.log"}, + }, + partitions: []disk.PartitionStat{ + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/", + Fstype: "ext4", + Opts: []string{"rw", "relatime"}, + }, + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/var/hdd.log", + Fstype: "ext4", + Opts: []string{"rw", "relatime", "bind"}, + }, + }, + usageFn: func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 32 * 1024 * 1024 * 1024, + Used: 8 * 1024 * 1024 * 1024, + Free: 24 * 1024 * 1024 * 1024, + UsedPercent: 25, + }, nil + }, + expectedMountpoints: []string{"/", "/var/hdd.log"}, + expectedExplicitIncludes: []bool{false, true}, }, - } - - entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { - return &disk.UsageStat{ - Path: mountpoint, - Total: 32 * 1024 * 1024 * 1024, - Used: 8 * 1024 * 1024 * 1024, - Free: 24 * 1024 * 1024 * 1024, - UsedPercent: 25, - }, nil - }) - - assert.Len(t, entries, 1) - assert.Equal(t, "/", entries[0].Partition.Mountpoint) -} - -func TestBuildDiskReportEntries_KeepsExplicitlyIncludedBindMounts(t *testing.T) { - agent := New(&config.AgentConfig{ - DiskIncludes: []string{"/var/hdd.log"}, - }) - - partitions := []disk.PartitionStat{ { - Device: "/dev/mmcblk0p2", - Mountpoint: "/", - Fstype: "ext4", - Opts: []string{"rw", "relatime"}, - }, - { - Device: "/dev/mmcblk0p2", - Mountpoint: "/var/hdd.log", - Fstype: "ext4", - Opts: []string{"rw", "relatime", "bind"}, + name: "include wins over exclude precedence", + cfg: config.AgentConfig{ + DiskIncludes: []string{"/var/hdd.log"}, + DiskExcludes: []string{"/var/hdd.log"}, + }, + partitions: []disk.PartitionStat{ + { + Device: "/dev/mmcblk0p2", + Mountpoint: "/var/hdd.log", + Fstype: "ext4", + Opts: []string{"rw", "relatime", "bind"}, + }, + }, + usageFn: func(mountpoint string) (*disk.UsageStat, error) { + return &disk.UsageStat{ + Path: mountpoint, + Total: 32 * 1024 * 1024 * 1024, + Used: 8 * 1024 * 1024 * 1024, + Free: 24 * 1024 * 1024 * 1024, + UsedPercent: 25, + }, nil + }, + expectedMountpoints: []string{"/var/hdd.log"}, + expectedExplicitIncludes: []bool{true}, }, } - entries := agent.buildDiskReportEntries(partitions, func(mountpoint string) (*disk.UsageStat, error) { - return &disk.UsageStat{ - Path: mountpoint, - Total: 32 * 1024 * 1024 * 1024, - Used: 8 * 1024 * 1024 * 1024, - Free: 24 * 1024 * 1024 * 1024, - UsedPercent: 25, - }, nil - }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + agent := New(&tt.cfg) - assert.Len(t, entries, 2) - assert.Equal(t, "/", entries[0].Partition.Mountpoint) - assert.Equal(t, "/var/hdd.log", entries[1].Partition.Mountpoint) - assert.True(t, entries[1].ExplicitInclude) + entries := agent.buildDiskReportEntries(tt.partitions, tt.usageFn) + + assert.Len(t, entries, len(tt.expectedMountpoints)) + for i, entry := range entries { + assert.Equal(t, tt.expectedMountpoints[i], entry.Partition.Mountpoint) + assert.Equal(t, tt.expectedExplicitIncludes[i], entry.ExplicitInclude) + } + }) + } } From c55f51dacd8d70682641a6a767859ffb8e594def Mon Sep 17 00:00:00 2001 From: soup Date: Sat, 11 Apr 2026 21:48:12 +0200 Subject: [PATCH 3/4] test: harden disk selection assertions Cache the dedupe signature within the grouping loop and use require.Len in the table-driven disk tests so mismatched lengths abort before index-based assertions run. --- internal/agent/disk_utils.go | 3 ++- internal/agent/disk_utils_test.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/agent/disk_utils.go b/internal/agent/disk_utils.go index d5740d0..1132d82 100644 --- a/internal/agent/disk_utils.go +++ b/internal/agent/disk_utils.go @@ -134,7 +134,8 @@ func dedupeDiskReportEntries(entries []diskReportEntry) []diskReportEntry { grouped := make(map[string][]int, len(entries)) for i, entry := range entries { - grouped[diskReportSignature(entry)] = append(grouped[diskReportSignature(entry)], i) + sig := diskReportSignature(entry) + grouped[sig] = append(grouped[sig], i) } keep := make([]bool, len(entries)) diff --git a/internal/agent/disk_utils_test.go b/internal/agent/disk_utils_test.go index 002fe2a..4afd2de 100644 --- a/internal/agent/disk_utils_test.go +++ b/internal/agent/disk_utils_test.go @@ -8,6 +8,7 @@ import ( "github.com/shirou/gopsutil/v4/disk" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/autobrr/netronome/internal/config" ) @@ -139,7 +140,7 @@ func TestBuildDiskReportEntries(t *testing.T) { entries := agent.buildDiskReportEntries(tt.partitions, tt.usageFn) - assert.Len(t, entries, len(tt.expectedMountpoints)) + require.Len(t, entries, len(tt.expectedMountpoints)) for i, entry := range entries { assert.Equal(t, tt.expectedMountpoints[i], entry.Partition.Mountpoint) assert.Equal(t, tt.expectedExplicitIncludes[i], entry.ExplicitInclude) From 5e571794500065fc45d12a78186149a152e831f0 Mon Sep 17 00:00:00 2001 From: soup Date: Sat, 11 Apr 2026 21:54:00 +0200 Subject: [PATCH 4/4] test: guard disk expectation lengths Abort the table-driven disk test when expectedExplicitIncludes does not match the built entries length before the index-based assertions run. --- internal/agent/disk_utils_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/agent/disk_utils_test.go b/internal/agent/disk_utils_test.go index 4afd2de..8bbec53 100644 --- a/internal/agent/disk_utils_test.go +++ b/internal/agent/disk_utils_test.go @@ -141,6 +141,7 @@ func TestBuildDiskReportEntries(t *testing.T) { entries := agent.buildDiskReportEntries(tt.partitions, tt.usageFn) require.Len(t, entries, len(tt.expectedMountpoints)) + require.Len(t, tt.expectedExplicitIncludes, len(entries)) for i, entry := range entries { assert.Equal(t, tt.expectedMountpoints[i], entry.Partition.Mountpoint) assert.Equal(t, tt.expectedExplicitIncludes[i], entry.ExplicitInclude)