inbound_autorenew_globaltraffic_test.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package service
  2. import (
  3. "testing"
  4. "time"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  8. )
  9. func seedGlobalTraffic(t *testing.T, email string, up, down int64) {
  10. t.Helper()
  11. if err := database.GetDB().Create(&model.ClientGlobalTraffic{
  12. MasterGuid: "peer-" + email, Email: email, Up: up, Down: down,
  13. }).Error; err != nil {
  14. t.Fatalf("seed client_global_traffics: %v", err)
  15. }
  16. }
  17. func countGlobalTraffic(t *testing.T, email string) int64 {
  18. t.Helper()
  19. var n int64
  20. if err := database.GetDB().Model(&model.ClientGlobalTraffic{}).
  21. Where("email = ?", email).Count(&n).Error; err != nil {
  22. t.Fatalf("count client_global_traffics: %v", err)
  23. }
  24. return n
  25. }
  26. // A capped catch-up keeps its counters and stays expired, so its cross-panel
  27. // rows still describe a window this client has not spent.
  28. func TestAutoRenewClients_TruncatedCatchUpKeepsCrossPanelTraffic(t *testing.T) {
  29. setupBulkDB(t)
  30. svc := &InboundService{}
  31. db := database.GetDB()
  32. past := time.Now().Add(-150 * 24 * time.Hour).UnixMilli()
  33. clients := []model.Client{
  34. {Email: "capped@x", ID: "66666666-6666-6666-6666-666666666666", Enable: false, Reset: 30, ResetMax: 3, ExpiryTime: past},
  35. }
  36. ib := mkInbound(t, 30126, model.VLESS, clientsSettings(t, clients))
  37. if err := svc.clientService.SyncInbound(nil, ib.Id, clients); err != nil {
  38. t.Fatalf("SyncInbound: %v", err)
  39. }
  40. if err := db.Create(&xray.ClientTraffic{
  41. InboundId: ib.Id, Email: "capped@x", Enable: false, Reset: 30, ResetMax: 3, ResetCount: 2,
  42. Up: 111, Down: 222, ExpiryTime: past,
  43. }).Error; err != nil {
  44. t.Fatalf("seed client_traffics: %v", err)
  45. }
  46. seedGlobalTraffic(t, "capped@x", 111, 222)
  47. if _, _, err := svc.autoRenewClients(db, newTrafficMutationBatch()); err != nil {
  48. t.Fatalf("autoRenewClients: %v", err)
  49. }
  50. var row xray.ClientTraffic
  51. if err := db.Where("email = ?", "capped@x").First(&row).Error; err != nil {
  52. t.Fatal(err)
  53. }
  54. if row.Up != 111 || row.Down != 222 {
  55. t.Fatalf("local counters were reset, so this case no longer exercises the cap: up=%d down=%d", row.Up, row.Down)
  56. }
  57. if got := countGlobalTraffic(t, "capped@x"); got != 1 {
  58. t.Fatalf("cross-panel rows=%d, want 1: the window was dropped for a client that never got it", got)
  59. }
  60. }
  61. // The counterpart: a real renewal must drop the rows, or the stale pushed
  62. // totals re-deplete the fresh window at once.
  63. func TestAutoRenewClients_RenewedClientLosesCrossPanelTraffic(t *testing.T) {
  64. setupBulkDB(t)
  65. svc := &InboundService{}
  66. db := database.GetDB()
  67. past := time.Now().Add(-40 * 24 * time.Hour).UnixMilli()
  68. clients := []model.Client{
  69. {Email: "rolled@x", ID: "77777777-7777-7777-7777-777777777777", Enable: false, Reset: 30, ExpiryTime: past},
  70. }
  71. ib := mkInbound(t, 30127, model.VLESS, clientsSettings(t, clients))
  72. if err := svc.clientService.SyncInbound(nil, ib.Id, clients); err != nil {
  73. t.Fatalf("SyncInbound: %v", err)
  74. }
  75. if err := db.Create(&xray.ClientTraffic{
  76. InboundId: ib.Id, Email: "rolled@x", Enable: false, Reset: 30,
  77. Up: 333, Down: 444, ExpiryTime: past,
  78. }).Error; err != nil {
  79. t.Fatalf("seed client_traffics: %v", err)
  80. }
  81. seedGlobalTraffic(t, "rolled@x", 333, 444)
  82. if _, _, err := svc.autoRenewClients(db, newTrafficMutationBatch()); err != nil {
  83. t.Fatalf("autoRenewClients: %v", err)
  84. }
  85. var row xray.ClientTraffic
  86. if err := db.Where("email = ?", "rolled@x").First(&row).Error; err != nil {
  87. t.Fatal(err)
  88. }
  89. if row.Up != 0 || row.Down != 0 {
  90. t.Fatalf("counters survived a renewal: up=%d down=%d", row.Up, row.Down)
  91. }
  92. if got := countGlobalTraffic(t, "rolled@x"); got != 0 {
  93. t.Fatalf("cross-panel rows=%d, want 0: stale pushed totals would re-deplete the fresh window", got)
  94. }
  95. }