Files
rtnetmon/internal/monitor/monitor_test.go
T
sneak ef3c373a12 Detect interfaces per platform; add macOS and single-interface support (closes #2)
Linux runs exactly as before when both bridge interfaces exist. When they do
not, the lone default-route interface is monitored, and a single interface now
draws a single UI pane instead of an empty second one.

macOS is newly supported: a running VPN tunnel (utun) is monitored as the
primary pane alongside the physical default-route interface, or the physical
interface alone when no VPN is up.

Detection lives in internal/netdetect: interface/route data types, pure
selection logic keyed on OS name, and route parsers, all unit-tested on Linux
for both platforms. Only the real route query and the per-platform TCP dial
binding (source address on Linux, IP_BOUND_IF on macOS) are build-tagged. Ping
argument construction is a pure, OS-keyed function. NewMonitor now takes a list
of interfaces.

Model: opus-4-8
2026-09-21 06:53:39 +00:00

139 lines
3.5 KiB
Go

package monitor
import (
"testing"
)
// TestNewMonitor tests the creation of a new Monitor
func TestNewMonitor(t *testing.T) {
// Test that we can create a monitor without errors
mon := NewMonitor([]IfaceSpec{
{Name: "test0", Label: "Test Interface A"},
{Name: "test1", Label: "Test Interface B"},
}, "/tmp/test.log")
if mon == nil {
t.Fatal("NewMonitor returned nil")
}
// Check interfaces
if len(mon.interfaces) != 2 {
t.Fatalf("Expected 2 interfaces, got %d", len(mon.interfaces))
}
if mon.interfaces[0].Name != "test0" {
t.Errorf("Expected interfaces[0].Name to be 'test0', got '%s'", mon.interfaces[0].Name)
}
if mon.interfaces[1].Name != "test1" {
t.Errorf("Expected interfaces[1].Name to be 'test1', got '%s'", mon.interfaces[1].Name)
}
// Check default configuration
if mon.ICMPTimeout.Milliseconds() != 500 {
t.Errorf("Expected ICMPTimeout to be 500ms, got %dms", mon.ICMPTimeout.Milliseconds())
}
if mon.PacketLossPings != 20 {
t.Errorf("Expected PacketLossPings to be 20, got %d", mon.PacketLossPings)
}
// Check that host lists are initialized
if mon.reachabilityHosts == nil {
t.Error("reachabilityHosts is nil")
}
if mon.packetLossHosts == nil {
t.Error("packetLossHosts is nil")
}
if mon.tcpHosts == nil {
t.Error("tcpHosts is nil")
}
}
// TestNewMonitorSingle verifies a monitor can be built with one interface.
func TestNewMonitorSingle(t *testing.T) {
mon := NewMonitor([]IfaceSpec{{Name: "eth0", Label: "default route"}}, "")
if len(mon.interfaces) != 1 {
t.Fatalf("Expected 1 interface, got %d", len(mon.interfaces))
}
if mon.interfaces[0].Name != "eth0" {
t.Errorf("Expected interfaces[0].Name to be 'eth0', got '%s'", mon.interfaces[0].Name)
}
}
// TestAddHosts tests adding hosts to the monitor
func TestAddHosts(t *testing.T) {
mon := NewMonitor([]IfaceSpec{
{Name: "test0", Label: "Test A"},
{Name: "test1", Label: "Test B"},
}, "")
// Test adding reachability hosts
mon.AddReachabilityHost("8.8.8.8")
mon.AddReachabilityHost("google.com")
if len(mon.reachabilityHosts) != 2 {
t.Errorf("Expected 2 reachability hosts, got %d", len(mon.reachabilityHosts))
}
// Test adding packet loss hosts
mon.AddPacketLossHost("github.com")
if len(mon.packetLossHosts) != 1 {
t.Errorf("Expected 1 packet loss host, got %d", len(mon.packetLossHosts))
}
// Test adding TCP hosts
mon.AddTCPHost("google.com:443")
mon.AddTCPHost("github.com:443")
if len(mon.tcpHosts) != 2 {
t.Errorf("Expected 2 TCP hosts, got %d", len(mon.tcpHosts))
}
}
// TestMinMaxAvgStd tests the statistics calculation function
func TestMinMaxAvgStd(t *testing.T) {
tests := []struct {
name string
data []float64
wantMin, wantMax, wantAvg float64
}{
{
name: "empty slice",
data: []float64{},
wantMin: 0, wantMax: 0, wantAvg: 0,
},
{
name: "single value",
data: []float64{5.0},
wantMin: 5.0, wantMax: 5.0, wantAvg: 5.0,
},
{
name: "multiple values",
data: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
wantMin: 1.0, wantMax: 5.0, wantAvg: 3.0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
min, max, avg, _ := MinMaxAvgStd(tt.data)
if min != tt.wantMin {
t.Errorf("MinMaxAvgStd() min = %v, want %v", min, tt.wantMin)
}
if max != tt.wantMax {
t.Errorf("MinMaxAvgStd() max = %v, want %v", max, tt.wantMax)
}
if avg != tt.wantAvg {
t.Errorf("MinMaxAvgStd() avg = %v, want %v", avg, tt.wantAvg)
}
})
}
}