Files
xboard-node/internal/tracker/tracker_test.go
T
2026-03-15 09:34:02 +08:00

279 lines
6.6 KiB
Go

package tracker
import (
"testing"
"github.com/cedar2025/xboard-node/internal/kernel"
)
func TestProcess_InitialTraffic(t *testing.T) {
tr := New()
conns := []kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200, SourceIP: "1.1.1.1"},
{ID: "c2", UserID: 2, Upload: 50, Download: 80, SourceIP: "2.2.2.2"},
}
interval := tr.Process(conns)
if interval[1] != [2]int64{100, 200} {
t.Errorf("user 1 interval: got %v", interval[1])
}
if interval[2] != [2]int64{50, 80} {
t.Errorf("user 2 interval: got %v", interval[2])
}
}
func TestProcess_DeltaCalculation(t *testing.T) {
tr := New()
// First tick
conns := []kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
}
tr.Process(conns)
// Second tick — counters advanced
conns[0].Upload = 300
conns[0].Download = 500
interval := tr.Process(conns)
if interval[1] != [2]int64{200, 300} {
t.Errorf("delta: got %v, want [200,300]", interval[1])
}
}
func TestProcess_CounterReset(t *testing.T) {
tr := New()
// First tick with high counters
conns := []kernel.Connection{
{ID: "c1", UserID: 1, Upload: 1000, Download: 2000},
}
tr.Process(conns)
// Counter reset (new value < old value) — treat as fresh
conns[0].Upload = 50
conns[0].Download = 80
interval := tr.Process(conns)
if interval[1] != [2]int64{50, 80} {
t.Errorf("counter reset: got %v, want [50,80]", interval[1])
}
}
func TestProcess_ConnectionCleanup(t *testing.T) {
tr := New()
// Tick 1: connection exists
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
})
// Tick 2: connection gone — should clean up state
tr.Process([]kernel.Connection{})
// Tick 3: same conn ID reappears — should count full bytes (not delta)
interval := tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 50, Download: 30},
})
if interval[1] != [2]int64{50, 30} {
t.Errorf("after cleanup: got %v, want [50,30]", interval[1])
}
}
func TestProcess_ZeroUserID(t *testing.T) {
tr := New()
interval := tr.Process([]kernel.Connection{
{ID: "c1", UserID: 0, Upload: 100, Download: 200},
})
if len(interval) != 0 {
t.Errorf("expected no interval traffic for userID=0, got %v", interval)
}
}
func TestProcess_MultipleConnectionsSameUser(t *testing.T) {
tr := New()
conns := []kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
{ID: "c2", UserID: 1, Upload: 50, Download: 80},
}
interval := tr.Process(conns)
if interval[1] != [2]int64{150, 280} {
t.Errorf("aggregated: got %v, want [150,280]", interval[1])
}
}
func TestFlushTraffic(t *testing.T) {
tr := New()
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
{ID: "c2", UserID: 2, Upload: 50, Download: 80},
})
flushed := tr.FlushTraffic()
if flushed[1] != [2]int64{100, 200} {
t.Errorf("flushed user 1: got %v", flushed[1])
}
if flushed[2] != [2]int64{50, 80} {
t.Errorf("flushed user 2: got %v", flushed[2])
}
// After flush, should be empty
if tr.HasTraffic() {
t.Error("expected no traffic after flush")
}
flushed2 := tr.FlushTraffic()
if len(flushed2) != 0 {
t.Errorf("expected empty after second flush, got %v", flushed2)
}
}
func TestRestoreTraffic(t *testing.T) {
tr := New()
// Generate some traffic
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
})
flushed := tr.FlushTraffic()
// Restore (simulating failed push)
tr.RestoreTraffic(flushed)
if !tr.HasTraffic() {
t.Error("expected traffic after restore")
}
restored := tr.FlushTraffic()
if restored[1] != [2]int64{100, 200} {
t.Errorf("restored: got %v", restored[1])
}
}
func TestRestoreTraffic_Additive(t *testing.T) {
tr := New()
// Generate traffic
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
})
// Generate more traffic
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 200, Download: 400},
})
// Flush and restore
first := tr.FlushTraffic()
tr.RestoreTraffic(first)
// Generate additional traffic
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 250, Download: 500},
})
final := tr.FlushTraffic()
// Should be restored(200,400) + delta(50,100) = (250,500)
if final[1][0] != 250 || final[1][1] != 500 {
t.Errorf("additive restore: got %v", final[1])
}
}
func TestFlushAliveIPs(t *testing.T) {
tr := New()
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200, SourceIP: "1.1.1.1"},
{ID: "c2", UserID: 1, Upload: 50, Download: 80, SourceIP: "2.2.2.2"},
{ID: "c3", UserID: 2, Upload: 30, Download: 40, SourceIP: "3.3.3.3"},
})
aliveIPs := tr.FlushAliveIPs()
if len(aliveIPs[1]) != 2 {
t.Errorf("user 1 IPs: got %d, want 2", len(aliveIPs[1]))
}
if len(aliveIPs[2]) != 1 {
t.Errorf("user 2 IPs: got %d, want 1", len(aliveIPs[2]))
}
// After flush, should be empty
aliveIPs2 := tr.FlushAliveIPs()
if len(aliveIPs2) != 0 {
t.Errorf("expected empty after second flush, got %v", aliveIPs2)
}
}
func TestFlushAliveIPs_DedupSameIP(t *testing.T) {
tr := New()
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200, SourceIP: "1.1.1.1"},
{ID: "c2", UserID: 1, Upload: 50, Download: 80, SourceIP: "1.1.1.1"},
})
aliveIPs := tr.FlushAliveIPs()
if len(aliveIPs[1]) != 1 {
t.Errorf("expected dedup to 1 IP, got %d", len(aliveIPs[1]))
}
}
func TestHasTraffic(t *testing.T) {
tr := New()
if tr.HasTraffic() {
t.Error("new tracker should not have traffic")
}
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
})
if !tr.HasTraffic() {
t.Error("should have traffic after process")
}
tr.FlushTraffic()
if tr.HasTraffic() {
t.Error("should not have traffic after flush")
}
}
func TestProcess_NoTrafficDelta(t *testing.T) {
tr := New()
conns := []kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
}
tr.Process(conns)
// Same values — no delta
interval := tr.Process(conns)
if len(interval) != 0 {
t.Errorf("expected no interval traffic for zero delta, got %v", interval)
}
}
func TestTrafficAccumulation(t *testing.T) {
tr := New()
// Tick 1
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 100, Download: 200},
})
// Tick 2
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 300, Download: 500},
})
// Tick 3
tr.Process([]kernel.Connection{
{ID: "c1", UserID: 1, Upload: 350, Download: 600},
})
flushed := tr.FlushTraffic()
// Total: 100+200+50 = 350 upload, 200+300+100 = 600 download
if flushed[1] != [2]int64{350, 600} {
t.Errorf("accumulated: got %v, want [350,600]", flushed[1])
}
}