|
@@ -0,0 +1,94 @@
|
|
|
|
|
+package service
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "testing"
|
|
|
|
|
+ "time"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
|
|
|
+
|
|
|
|
|
+ "gorm.io/gorm"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+func readClientTraffic(t *testing.T, email string) xray.ClientTraffic {
|
|
|
|
|
+ t.Helper()
|
|
|
|
|
+ var row xray.ClientTraffic
|
|
|
|
|
+ if err := database.GetDB().Where("email = ?", email).First(&row).Error; err != nil {
|
|
|
|
|
+ t.Fatalf("read client_traffics %s: %v", email, err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return row
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// The inbound modal posts the clients it loaded on open; a renewal committed
|
|
|
|
|
+// while it was open must survive the save in settings, record and stats.
|
|
|
|
|
+func TestInboundFormSaveKeepsClientRenewedWhileOpen(t *testing.T) {
|
|
|
|
|
+ setupBulkDB(t)
|
|
|
|
|
+ ib := seedRenewableNeighbour(t, 23201, nil)
|
|
|
|
|
+ form := *ib
|
|
|
|
|
+
|
|
|
|
|
+ if err := database.GetDB().Transaction(autoRenewTick); err != nil {
|
|
|
|
|
+ t.Fatalf("autoRenew: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ form.Remark = "edited"
|
|
|
|
|
+ if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
|
|
|
|
|
+ t.Fatalf("UpdateInbound: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ requireNeighbourRenewed(t, ib.Id)
|
|
|
|
|
+ now := time.Now().UnixMilli()
|
|
|
|
|
+ if row := readClientTraffic(t, "y@stale"); !row.Enable || row.ExpiryTime <= now {
|
|
|
|
|
+ t.Fatalf("client_traffics rolled back: enable=%v expiryTime=%d", row.Enable, row.ExpiryTime)
|
|
|
|
|
+ }
|
|
|
|
|
+ if rec := lookupClientRecord(t, "y@stale"); !rec.Enable || rec.ExpiryTime <= now {
|
|
|
|
|
+ t.Fatalf("client record rolled back: enable=%v expiryTime=%d", rec.Enable, rec.ExpiryTime)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// On a node the master's push is authoritative, lifecycle fields included.
|
|
|
|
|
+func TestInboundUpdateFromMasterAppliesClientLifecycle(t *testing.T) {
|
|
|
|
|
+ setupBulkDB(t)
|
|
|
|
|
+ ib := seedRenewableNeighbour(t, 23202, nil)
|
|
|
|
|
+ clients, err := (&InboundService{}).GetClients(ib)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("GetClients: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ for i := range clients {
|
|
|
|
|
+ if clients[i].Email == "x@stale" {
|
|
|
|
|
+ clients[i].Enable = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ push := *ib
|
|
|
|
|
+ push.Settings = clientsSettings(t, clients)
|
|
|
|
|
+ if _, _, err := (&InboundService{FromNodeSync: true}).UpdateInbound(&push); err != nil {
|
|
|
|
|
+ t.Fatalf("UpdateInbound: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if x, _ := settingsClient(t, ib.Id, "x@stale"); x.Enable {
|
|
|
|
|
+ t.Fatal("master push disabling x@stale was ignored in settings")
|
|
|
|
|
+ }
|
|
|
|
|
+ if readClientTraffic(t, "x@stale").Enable {
|
|
|
|
|
+ t.Fatal("master push disabling x@stale was ignored in client_traffics")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Traffic the poll adds after UpdateInbound read the row must not be written
|
|
|
|
|
+// back over by the edit.
|
|
|
|
|
+func TestInboundUpdateKeepsTrafficAddedMidEdit(t *testing.T) {
|
|
|
|
|
+ setupBulkDB(t)
|
|
|
|
|
+ ib := seedRenewableNeighbour(t, 23203, nil)
|
|
|
|
|
+ form := *ib
|
|
|
|
|
+ form.Remark = "edited"
|
|
|
|
|
+ commitTickBetweenReadAndWrite(t, func(tx *gorm.DB) error {
|
|
|
|
|
+ return (&InboundService{}).addInboundTraffic(tx, []*xray.Traffic{{IsInbound: true, Tag: ib.Tag, Up: 100, Down: 50}})
|
|
|
|
|
+ }, func() {
|
|
|
|
|
+ if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
|
|
|
|
|
+ t.Errorf("UpdateInbound: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ saved, err := (&InboundService{}).GetInbound(ib.Id)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("GetInbound: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if saved.Up != 100 || saved.Down != 50 || saved.Remark != "edited" {
|
|
|
|
|
+ t.Fatalf("inbound after edit: up=%d down=%d remark=%q, want 100/50/edited", saved.Up, saved.Down, saved.Remark)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|