|
@@ -226,6 +226,54 @@ func TestClientGoneFromOneNode_KeepsSharedEmailRow(t *testing.T) {
|
|
|
assertUpDown(t, readTraffic(t, db, email), 140, 140, "node 2 keeps accruing")
|
|
assertUpDown(t, readTraffic(t, db, email), 140, 140, "node 2 keeps accruing")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// TestStatsUnderSiblingInbound_KeepsNodeBaseline reproduces the recurring
|
|
|
|
|
+// sweep bug behind #5202: the client is attached to two inbounds of the SAME
|
|
|
|
|
+// node, the node reports its stats under n1-a only, but the master-side row
|
|
|
|
|
+// is owned by n1-b's mirror. The per-email sweep for n1-b must not drop the
|
|
|
|
|
+// (node, email) baseline that n1-a's delta computation needs — doing so every
|
|
|
|
|
+// cycle froze the client's counter permanently.
|
|
|
|
|
+func TestStatsUnderSiblingInbound_KeepsNodeBaseline(t *testing.T) {
|
|
|
|
|
+ db := initTrafficTestDB(t)
|
|
|
|
|
+ createNodeInboundWithClient(t, db, 1, "n1-a", 41001, "fresh")
|
|
|
|
|
+ createNodeInbound(t, db, 1, "n1-b", 41002)
|
|
|
|
|
+ svc := &InboundService{}
|
|
|
|
|
+
|
|
|
|
|
+ const email = "fresh"
|
|
|
|
|
+ var ibB model.Inbound
|
|
|
|
|
+ if err := db.Where("tag = ?", "n1-b").First(&ibB).Error; err != nil {
|
|
|
|
|
+ t.Fatalf("load n1-b: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ // Master-side row created when the client was added on the panel, owned by
|
|
|
|
|
+ // n1-b's mirror (e.g. the client form targeted that inbound).
|
|
|
|
|
+ if err := db.Create(&xray.ClientTraffic{InboundId: ibB.Id, Email: email, Enable: true}).Error; err != nil {
|
|
|
|
|
+ t.Fatalf("seed master row: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ settings := fmt.Sprintf(`{"clients": [{"email": %q, "enable": true}]}`, email)
|
|
|
|
|
+ sync := func(up, down int64) {
|
|
|
|
|
+ t.Helper()
|
|
|
|
|
+ snap := &runtime.TrafficSnapshot{Inbounds: []*model.Inbound{
|
|
|
|
|
+ {Tag: "n1-a", Settings: settings, ClientStats: []xray.ClientTraffic{{Email: email, Up: up, Down: down, Enable: true}}},
|
|
|
|
|
+ {Tag: "n1-b", Settings: `{"clients": []}`},
|
|
|
|
|
+ }}
|
|
|
|
|
+ if _, err := svc.setRemoteTrafficLocked(1, snap, false); err != nil {
|
|
|
|
|
+ t.Fatalf("sync: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sync(630, 630)
|
|
|
|
|
+ var baselines int64
|
|
|
|
|
+ if err := db.Model(&model.NodeClientTraffic{}).Where("node_id = ? AND email = ?", 1, email).Count(&baselines).Error; err != nil {
|
|
|
|
|
+ t.Fatalf("count baselines: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if baselines != 1 {
|
|
|
|
|
+ t.Fatalf("baseline must survive the sibling-inbound sweep, found %d rows", baselines)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sync(700, 700)
|
|
|
|
|
+ assertUpDown(t, readTraffic(t, db, email), 70, 70, "delta accrues once baseline survives")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func TestDelClientStat_CleansNodeBaselines(t *testing.T) {
|
|
func TestDelClientStat_CleansNodeBaselines(t *testing.T) {
|
|
|
db := initTrafficTestDB(t)
|
|
db := initTrafficTestDB(t)
|
|
|
svc := &InboundService{}
|
|
svc := &InboundService{}
|