From f1a04db49de60f16869a93ac9064756a28965db0 Mon Sep 17 00:00:00 2001 From: Free-Guy-IR Date: Wed, 4 Nov 2026 02:41:51 +0330 Subject: [PATCH 1/2] fix(stats): report bytes received from a WireGuard peer as uplink --- backend/wireguard/stats_outbound_test.go | 20 ++++++++-------- pkg/stats/direction_test.go | 29 ++++++++++++++++++++++++ pkg/stats/interface_counters.go | 8 +++---- pkg/stats/tracker_test.go | 16 ++++++------- 4 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 pkg/stats/direction_test.go diff --git a/backend/wireguard/stats_outbound_test.go b/backend/wireguard/stats_outbound_test.go index efcdc9e3..ce55b404 100644 --- a/backend/wireguard/stats_outbound_test.go +++ b/backend/wireguard/stats_outbound_test.go @@ -85,10 +85,10 @@ func TestGetStatsOutboundsUsesInterfaceDeltaAndLink(t *testing.T) { t.Fatalf("GetStats failed: %v", err) } - if got := statValueByType(t, resp, "downlink"); got != 50 { + if got := statValueByType(t, resp, "uplink"); got != 50 { t.Fatalf("unexpected downlink delta: got %d want 50", got) } - if got := statValueByType(t, resp, "uplink"); got != 60 { + if got := statValueByType(t, resp, "downlink"); got != 60 { t.Fatalf("unexpected uplink delta: got %d want 60", got) } @@ -126,10 +126,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { if err != nil { t.Fatalf("GetStats failed: %v", err) } - if got := statValueByType(t, resp, "downlink"); got != 40 { + if got := statValueByType(t, resp, "uplink"); got != 40 { t.Fatalf("unexpected downlink delta before reset: got %d want 40", got) } - if got := statValueByType(t, resp, "uplink"); got != 60 { + if got := statValueByType(t, resp, "downlink"); got != 60 { t.Fatalf("unexpected uplink delta before reset: got %d want 60", got) } @@ -140,10 +140,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { if err != nil { t.Fatalf("GetStats reset failed: %v", err) } - if got := statValueByType(t, resp, "downlink"); got != 60 { + if got := statValueByType(t, resp, "uplink"); got != 60 { t.Fatalf("unexpected downlink delta on reset: got %d want 60", got) } - if got := statValueByType(t, resp, "uplink"); got != 100 { + if got := statValueByType(t, resp, "downlink"); got != 100 { t.Fatalf("unexpected uplink delta on reset: got %d want 100", got) } @@ -151,10 +151,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { if err != nil { t.Fatalf("GetStats post-reset failed: %v", err) } - if got := statValueByType(t, resp, "downlink"); got != 10 { + if got := statValueByType(t, resp, "uplink"); got != 10 { t.Fatalf("unexpected downlink delta after reset: got %d want 10", got) } - if got := statValueByType(t, resp, "uplink"); got != 10 { + if got := statValueByType(t, resp, "downlink"); got != 10 { t.Fatalf("unexpected uplink delta after reset: got %d want 10", got) } } @@ -190,10 +190,10 @@ func TestGetStatsOutboundsRebasesOnCounterRollback(t *testing.T) { if err != nil { t.Fatalf("GetStats after rollback failed: %v", err) } - if got := statValueByType(t, resp, "downlink"); got != 20 { + if got := statValueByType(t, resp, "uplink"); got != 20 { t.Fatalf("unexpected downlink delta after rollback rebase: got %d want 20", got) } - if got := statValueByType(t, resp, "uplink"); got != 10 { + if got := statValueByType(t, resp, "downlink"); got != 10 { t.Fatalf("unexpected uplink delta after rollback rebase: got %d want 10", got) } } diff --git a/pkg/stats/direction_test.go b/pkg/stats/direction_test.go new file mode 100644 index 00000000..ddc3265d --- /dev/null +++ b/pkg/stats/direction_test.go @@ -0,0 +1,29 @@ +package stats + +import ( + "context" + "testing" +) + +func TestBytesReceivedFromAWireGuardPeerAreReportedAsUplink(t *testing.T) { + tracker := New() + tracker.UpdateStatsBatch([]Sample{{PublicKey: "peer", Email: "user", Rx: 1000, Tx: 7}}) + + got := map[string]int64{} + for _, s := range tracker.GetUsersStats(context.Background(), false).GetStats() { + got[s.Type] = s.Value + } + if got["uplink"] != 1000 || got["downlink"] != 7 { + t.Fatalf("peer Rx=1000 (client upload) Tx=7 (client download) reported as uplink=%d downlink=%d", got["uplink"], got["downlink"]) + } +} + +func TestInterfaceBytesReceivedAreReportedAsUplink(t *testing.T) { + got := map[string]int64{} + for _, s := range BuildInterfaceStats("wg0", "", 1000, 7) { + got[s.Type] = s.Value + } + if got["uplink"] != 1000 || got["downlink"] != 7 { + t.Fatalf("interface rx=1000 tx=7 reported as uplink=%d downlink=%d", got["uplink"], got["downlink"]) + } +} diff --git a/pkg/stats/interface_counters.go b/pkg/stats/interface_counters.go index 4bdadb8f..0b7bd50d 100644 --- a/pkg/stats/interface_counters.go +++ b/pkg/stats/interface_counters.go @@ -54,20 +54,20 @@ func buildDeltaStats(name, link string, rx, tx int64) []*common.Stat { } stats := make([]*common.Stat, 0, 2) - if tx > 0 { + if rx > 0 { stats = append(stats, &common.Stat{ Name: name, Type: "uplink", Link: link, - Value: tx, + Value: rx, }) } - if rx > 0 { + if tx > 0 { stats = append(stats, &common.Stat{ Name: name, Type: "downlink", Link: link, - Value: rx, + Value: tx, }) } diff --git a/pkg/stats/tracker_test.go b/pkg/stats/tracker_test.go index 1dc7697c..c46d6b27 100644 --- a/pkg/stats/tracker_test.go +++ b/pkg/stats/tracker_test.go @@ -30,10 +30,10 @@ func TestStatsTracker_GetStats(t *testing.T) { } for _, s := range resp.Stats { - if s.Type == "downlink" && s.Value != 1000 { + if s.Type == "uplink" && s.Value != 1000 { t.Errorf("Expected rx=1000, got %d", s.Value) } - if s.Type == "uplink" && s.Value != 2000 { + if s.Type == "downlink" && s.Value != 2000 { t.Errorf("Expected tx=2000, got %d", s.Value) } } @@ -48,10 +48,10 @@ func TestStatsTracker_GetStatsReset(t *testing.T) { resp := tracker.GetStats(ctx, []string{"key1"}, true) for _, s := range resp.Stats { - if s.Type == "downlink" && s.Value != 2000 { + if s.Type == "uplink" && s.Value != 2000 { t.Errorf("Expected rx=2000, got %d", s.Value) } - if s.Type == "uplink" && s.Value != 4000 { + if s.Type == "downlink" && s.Value != 4000 { t.Errorf("Expected tx=4000, got %d", s.Value) } } @@ -200,10 +200,10 @@ func TestStatsTracker_ReaddAfterDeletePreservesPendingDelta(t *testing.T) { resp := tracker.GetStats(ctx, []string{"key1"}, false) for _, s := range resp.Stats { - if s.Type == "downlink" && s.Value != 1100 { + if s.Type == "uplink" && s.Value != 1100 { t.Errorf("Expected pending plus new rx=1100, got %d", s.Value) } - if s.Type == "uplink" && s.Value != 2200 { + if s.Type == "downlink" && s.Value != 2200 { t.Errorf("Expected pending plus new tx=2200, got %d", s.Value) } } @@ -220,9 +220,9 @@ func TestStatsTracker_ReaddAfterDeletePreservesPendingDelta(t *testing.T) { func pullTotals(tracker *Tracker) (rx, tx int64) { for _, s := range tracker.GetUsersStats(context.Background(), true).Stats { switch s.Type { - case "downlink": - rx += s.Value case "uplink": + rx += s.Value + case "downlink": tx += s.Value } } From 626952c8e57ecdc0e9023950faca9029cc1b2d74 Mon Sep 17 00:00:00 2001 From: Free-Guy-IR Date: Wed, 4 Nov 2026 04:53:10 +0330 Subject: [PATCH 2/2] Name the checked direction in WireGuard stats test failure messages --- backend/wireguard/stats_outbound_test.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/wireguard/stats_outbound_test.go b/backend/wireguard/stats_outbound_test.go index ce55b404..1f9574a5 100644 --- a/backend/wireguard/stats_outbound_test.go +++ b/backend/wireguard/stats_outbound_test.go @@ -86,10 +86,10 @@ func TestGetStatsOutboundsUsesInterfaceDeltaAndLink(t *testing.T) { } if got := statValueByType(t, resp, "uplink"); got != 50 { - t.Fatalf("unexpected downlink delta: got %d want 50", got) + t.Fatalf("unexpected uplink delta: got %d want 50", got) } if got := statValueByType(t, resp, "downlink"); got != 60 { - t.Fatalf("unexpected uplink delta: got %d want 60", got) + t.Fatalf("unexpected downlink delta: got %d want 60", got) } for _, stat := range resp.GetStats() { @@ -127,10 +127,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { t.Fatalf("GetStats failed: %v", err) } if got := statValueByType(t, resp, "uplink"); got != 40 { - t.Fatalf("unexpected downlink delta before reset: got %d want 40", got) + t.Fatalf("unexpected uplink delta before reset: got %d want 40", got) } if got := statValueByType(t, resp, "downlink"); got != 60 { - t.Fatalf("unexpected uplink delta before reset: got %d want 60", got) + t.Fatalf("unexpected downlink delta before reset: got %d want 60", got) } resp, err = wg.GetStats(context.Background(), &common.StatRequest{ @@ -141,10 +141,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { t.Fatalf("GetStats reset failed: %v", err) } if got := statValueByType(t, resp, "uplink"); got != 60 { - t.Fatalf("unexpected downlink delta on reset: got %d want 60", got) + t.Fatalf("unexpected uplink delta on reset: got %d want 60", got) } if got := statValueByType(t, resp, "downlink"); got != 100 { - t.Fatalf("unexpected uplink delta on reset: got %d want 100", got) + t.Fatalf("unexpected downlink delta on reset: got %d want 100", got) } resp, err = wg.GetStats(context.Background(), &common.StatRequest{Type: common.StatType_Outbounds}) @@ -152,10 +152,10 @@ func TestGetStatsOutboundsResetBaseline(t *testing.T) { t.Fatalf("GetStats post-reset failed: %v", err) } if got := statValueByType(t, resp, "uplink"); got != 10 { - t.Fatalf("unexpected downlink delta after reset: got %d want 10", got) + t.Fatalf("unexpected uplink delta after reset: got %d want 10", got) } if got := statValueByType(t, resp, "downlink"); got != 10 { - t.Fatalf("unexpected uplink delta after reset: got %d want 10", got) + t.Fatalf("unexpected downlink delta after reset: got %d want 10", got) } } @@ -191,10 +191,10 @@ func TestGetStatsOutboundsRebasesOnCounterRollback(t *testing.T) { t.Fatalf("GetStats after rollback failed: %v", err) } if got := statValueByType(t, resp, "uplink"); got != 20 { - t.Fatalf("unexpected downlink delta after rollback rebase: got %d want 20", got) + t.Fatalf("unexpected uplink delta after rollback rebase: got %d want 20", got) } if got := statValueByType(t, resp, "downlink"); got != 10 { - t.Fatalf("unexpected uplink delta after rollback rebase: got %d want 10", got) + t.Fatalf("unexpected downlink delta after rollback rebase: got %d want 10", got) } }