Adopt repo standards: scaffold, policies, lint-clean (closes #1)
check / check (push) Failing after 0s

Standard scaffold: script/ entrypoints with the Makefile as thin shims, a Dockerfile whose lint and test phases gate the build, a Gitea CI workflow running script/cibuild, REPO_POLICIES.md, TODO.md, .editorconfig, .dockerignore, LICENSE, wider .gitignore. .golangci.yml is byte-identical to the canonical copy in the prompts repo.

211 lint findings fixed in code: package globals became functions/fields and a cobra command constructor, magic numbers became named constants, ctx is threaded into the probes, monitor loops split. Tests moved to external _test packages with export_test.go. Behavior unchanged.

Readers will trip over: make lint/test/check now need a Docker daemon; the personal rsync copy/run targets are gone and make run runs locally.
Disclosure: four //nolint:gosec remain on the fixed-argv ping/curl calls and the operator-chosen log file, as the reference repo annotates the same class.
Disclosure: module path left as-is.

Model: opus-4-8 (implementation); fable-5-1 (merge)
This commit was merged in pull request #5.
This commit is contained in:
2026-09-21 09:22:28 +02:00
parent cf9dc39053
commit 7dd4ac798d
34 changed files with 2172 additions and 884 deletions
+40 -55
View File
@@ -1,125 +1,110 @@
//go:build linux
// +build linux
package monitor
package monitor_test
import (
"testing"
"git.eeqj.de/sneak/rtnetmon/internal/monitor"
)
// TestNewMonitor tests the creation of a new Monitor
func TestNewMonitor(t *testing.T) {
// Test that we can create a monitor without errors
mon := NewMonitor("test0", "Test Interface A", "test1", "Test Interface B", "/tmp/test.log")
t.Parallel()
if mon == nil {
t.Fatal("NewMonitor returned nil")
}
mon := monitor.NewMonitor("test0", "Test Interface A", "test1",
"Test Interface B", "/tmp/test.log")
// Check interfaces
if mon.interfaceA == nil {
if mon.InterfaceA() == nil {
t.Fatal("interfaceA is nil")
}
if mon.interfaceB == nil {
if mon.InterfaceB() == nil {
t.Fatal("interfaceB is nil")
}
if mon.interfaceA.Name != "test0" {
t.Errorf("Expected interfaceA.Name to be 'test0', got '%s'", mon.interfaceA.Name)
if mon.InterfaceA().Name != "test0" {
t.Errorf("interfaceA.Name = %q, want %q", mon.InterfaceA().Name, "test0")
}
if mon.interfaceB.Name != "test1" {
t.Errorf("Expected interfaceB.Name to be 'test1', got '%s'", mon.interfaceB.Name)
if mon.InterfaceB().Name != "test1" {
t.Errorf("interfaceB.Name = %q, want %q", mon.InterfaceB().Name, "test1")
}
// Check default configuration
if mon.ICMPTimeout.Milliseconds() != 500 {
t.Errorf("Expected ICMPTimeout to be 500ms, got %dms", mon.ICMPTimeout.Milliseconds())
t.Errorf("ICMPTimeout = %dms, want 500ms", mon.ICMPTimeout.Milliseconds())
}
if mon.PacketLossPings != 20 {
t.Errorf("Expected PacketLossPings to be 20, got %d", mon.PacketLossPings)
t.Errorf("PacketLossPings = %d, want 20", mon.PacketLossPings)
}
// Check that host lists are initialized
if mon.reachabilityHosts == nil {
if mon.ReachabilityHosts() == nil {
t.Error("reachabilityHosts is nil")
}
if mon.packetLossHosts == nil {
if mon.PacketLossHosts() == nil {
t.Error("packetLossHosts is nil")
}
if mon.tcpHosts == nil {
if mon.TCPHosts() == nil {
t.Error("tcpHosts is nil")
}
}
// TestAddHosts tests adding hosts to the monitor
func TestAddHosts(t *testing.T) {
mon := NewMonitor("test0", "Test A", "test1", "Test B", "")
t.Parallel()
mon := monitor.NewMonitor("test0", "Test A", "test1", "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))
if len(mon.ReachabilityHosts()) != 2 {
t.Errorf("reachability hosts = %d, want 2", 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))
if len(mon.PacketLossHosts()) != 1 {
t.Errorf("packet loss hosts = %d, want 1", 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))
if len(mon.TCPHosts()) != 2 {
t.Errorf("tcp hosts = %d, want 2", len(mon.TCPHosts()))
}
}
// TestMinMaxAvgStd tests the statistics calculation function
func TestMinMaxAvgStd(t *testing.T) {
t.Parallel()
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,
},
{"empty slice", []float64{}, 0, 0, 0},
{"single value", []float64{5.0}, 5.0, 5.0, 5.0},
{"multiple values", []float64{1.0, 2.0, 3.0, 4.0, 5.0}, 1.0, 5.0, 3.0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
min, max, avg, _ := MinMaxAvgStd(tt.data)
t.Parallel()
if min != tt.wantMin {
t.Errorf("MinMaxAvgStd() min = %v, want %v", min, tt.wantMin)
mn, mx, avg, _ := monitor.MinMaxAvgStd(tt.data)
if mn != tt.wantMin {
t.Errorf("min = %v, want %v", mn, tt.wantMin)
}
if max != tt.wantMax {
t.Errorf("MinMaxAvgStd() max = %v, want %v", max, tt.wantMax)
if mx != tt.wantMax {
t.Errorf("max = %v, want %v", mx, tt.wantMax)
}
if avg != tt.wantAvg {
t.Errorf("MinMaxAvgStd() avg = %v, want %v", avg, tt.wantAvg)
t.Errorf("avg = %v, want %v", avg, tt.wantAvg)
}
})
}