package tracker import ( "testing" ) func TestProcess_InitialTraffic(t *testing.T) { tr := New() cumTraffic := map[int][2]int64{ 1: {100, 200}, 2: {50, 80}, } aliveIPs := map[int]map[string]bool{ 1: {"1.1.1.1": true}, 2: {"2.2.2.2": true}, } tr.Process(cumTraffic, aliveIPs, 2) flushed := tr.FlushTraffic() if flushed[1] != [2]int64{100, 200} { t.Errorf("user 1 traffic: got %v", flushed[1]) } if flushed[2] != [2]int64{50, 80} { t.Errorf("user 2 traffic: got %v", flushed[2]) } } func TestProcess_DeltaCalculation(t *testing.T) { tr := New() // First tick tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) // Second tick — counters advanced tr.Process(map[int][2]int64{1: {300, 500}}, nil, 1) flushed := tr.FlushTraffic() // Total: first tick 100/200 + delta 200/300 = 300/500 if flushed[1] != [2]int64{300, 500} { t.Errorf("accumulated: got %v, want [300,500]", flushed[1]) } } func TestProcess_CounterReset(t *testing.T) { tr := New() // First tick with high counters tr.Process(map[int][2]int64{1: {1000, 2000}}, nil, 1) tr.FlushTraffic() // clear accumulator // Counter reset (new value < old value) — treat as fresh tr.Process(map[int][2]int64{1: {50, 80}}, nil, 1) flushed := tr.FlushTraffic() if flushed[1] != [2]int64{50, 80} { t.Errorf("counter reset: got %v, want [50,80]", flushed[1]) } } func TestProcess_UserDisappears(t *testing.T) { tr := New() // Tick 1: user present tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) // Tick 2: user gone tr.Process(map[int][2]int64{}, nil, 0) // Tick 3: user reappears — should count full bytes (treated as new) tr.Process(map[int][2]int64{1: {50, 30}}, nil, 1) flushed := tr.FlushTraffic() // First tick: 100/200, tick 2: 0, tick 3: 50/30 = total 150/230 if flushed[1] != [2]int64{150, 230} { t.Errorf("after disappear: got %v, want [150,230]", flushed[1]) } } func TestProcess_ZeroTraffic(t *testing.T) { tr := New() tr.Process(map[int][2]int64{}, nil, 0) if tr.HasTraffic() { t.Error("expected no traffic for empty input") } } func TestFlushTraffic(t *testing.T) { tr := New() tr.Process(map[int][2]int64{ 1: {100, 200}, 2: {50, 80}, }, nil, 2) 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() tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) flushed := tr.FlushTraffic() 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() // Two ticks accumulate tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) tr.Process(map[int][2]int64{1: {200, 400}}, nil, 1) // Flush and restore first := tr.FlushTraffic() tr.RestoreTraffic(first) // Generate additional traffic tr.Process(map[int][2]int64{1: {250, 500}}, nil, 1) final := tr.FlushTraffic() // Restored: 200/400 (initial 100/200 + delta 100/200) + new 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() aliveIPs := map[int]map[string]bool{ 1: {"1.1.1.1": true, "2.2.2.2": true}, 2: {"3.3.3.3": true}, } tr.Process(map[int][2]int64{1: {100, 200}, 2: {30, 40}}, aliveIPs, 3) flushed := tr.FlushAliveIPs() if len(flushed[1]) != 2 { t.Errorf("user 1 IPs: got %d, want 2", len(flushed[1])) } if len(flushed[2]) != 1 { t.Errorf("user 2 IPs: got %d, want 1", len(flushed[2])) } // Without another Process call, hash is same → returns nil (skip duplicate) flushed2 := tr.FlushAliveIPs() if flushed2 != nil { t.Errorf("expected nil on duplicate flush, got %v", flushed2) } } func TestFlushAliveIPs_DedupSameIP(t *testing.T) { tr := New() aliveIPs := map[int]map[string]bool{ 1: {"1.1.1.1": true}, } tr.Process(map[int][2]int64{1: {100, 200}}, aliveIPs, 2) flushed := tr.FlushAliveIPs() if len(flushed[1]) != 1 { t.Errorf("expected 1 IP, got %d", len(flushed[1])) } } func TestHasTraffic(t *testing.T) { tr := New() if tr.HasTraffic() { t.Error("new tracker should not have traffic") } tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) 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() tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) tr.FlushTraffic() // clear // Same values — no delta tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) if tr.HasTraffic() { t.Error("expected no traffic for zero delta") } } func TestTrafficAccumulation(t *testing.T) { tr := New() // Tick 1 tr.Process(map[int][2]int64{1: {100, 200}}, nil, 1) // Tick 2 (cumulative 300, 500 → delta 200, 300) tr.Process(map[int][2]int64{1: {300, 500}}, nil, 1) // Tick 3 (cumulative 350, 600 → delta 50, 100) tr.Process(map[int][2]int64{1: {350, 600}}, nil, 1) 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]) } }