node_detached_client_stats_test.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. package service
  2. import (
  3. "errors"
  4. "testing"
  5. "gorm.io/gorm"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  8. )
  9. // A node detach keeps the client's stat row (keepTraffic), so the node goes on
  10. // reporting it under an inbound whose settings no longer list the client (#6724).
  11. func TestNodeSnapshotIgnoresStatsOfClientDetachedFromInbound(t *testing.T) {
  12. const gib = int64(1) << 30
  13. const emptyClients = `{"clients": []}`
  14. t.Run("stale node quota and disable never reach the master row", func(t *testing.T) {
  15. db := initTrafficTestDB(t)
  16. createNodeInbound(t, db, 1, "n1-in", 41001)
  17. local := &model.Inbound{UserId: 1, Tag: "local-in", Enable: true, Port: 41010, Protocol: model.VLESS}
  18. if err := db.Create(local).Error; err != nil {
  19. t.Fatalf("create local inbound: %v", err)
  20. }
  21. const email = "moved"
  22. if err := db.Create(&xray.ClientTraffic{InboundId: local.Id, Email: email, Enable: true, Total: 200 * gib, Up: 5, Down: 5}).Error; err != nil {
  23. t.Fatalf("seed client_traffics: %v", err)
  24. }
  25. if err := db.Create(&model.NodeClientTraffic{NodeId: 1, Email: email, Up: 40, Down: 40}).Error; err != nil {
  26. t.Fatalf("seed node baseline: %v", err)
  27. }
  28. svc := &InboundService{}
  29. // Two ticks: the first copies the quota, which makes the second's disable look genuine.
  30. for range 2 {
  31. syncNodeWithSettings(t, svc, 1, "n1-in", emptyClients,
  32. xray.ClientTraffic{Email: email, Up: 40, Down: 40, Total: 100 * gib, Enable: false})
  33. }
  34. got := readTraffic(t, db, email)
  35. if got.Total != 200*gib || !got.Enable {
  36. t.Fatalf("master row = total %d enable %v, want total %d enable true", got.Total, got.Enable, 200*gib)
  37. }
  38. })
  39. t.Run("a detached email gets no master traffic row", func(t *testing.T) {
  40. db := initTrafficTestDB(t)
  41. createNodeInbound(t, db, 1, "n1-in", 41001)
  42. svc := &InboundService{}
  43. syncNodeWithSettings(t, svc, 1, "n1-in", emptyClients,
  44. xray.ClientTraffic{Email: "gone", Up: 40, Down: 40, Total: 100 * gib, Enable: true})
  45. var ct xray.ClientTraffic
  46. err := db.Model(xray.ClientTraffic{}).Where("email = ?", "gone").First(&ct).Error
  47. if !errors.Is(err, gorm.ErrRecordNotFound) {
  48. t.Fatalf("client_traffics row for detached email: err = %v, row = %+v; want ErrRecordNotFound", err, ct)
  49. }
  50. })
  51. }