| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- package service
- import (
- "testing"
- "time"
- "github.com/mhsanaei/3x-ui/v3/internal/database"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "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)
- }
- }
- func inboundLinksEmail(t *testing.T, inboundId int, email string) bool {
- t.Helper()
- var n int64
- if err := database.GetDB().Table("client_inbounds").
- Joins("JOIN clients ON clients.id = client_inbounds.client_id").
- Where("client_inbounds.inbound_id = ? AND clients.email = ?", inboundId, email).
- Count(&n).Error; err != nil {
- t.Fatalf("count links: %v", err)
- }
- return n > 0
- }
- // A client added while the modal was open is not in the list it posts back;
- // saving the inbound must not detach it.
- func TestInboundFormSaveKeepsClientAddedWhileOpen(t *testing.T) {
- setupBulkDB(t)
- ib := seedRenewableNeighbour(t, 23204, nil)
- form := *ib
- if _, err := (&ClientService{}).AddInboundClient(&InboundService{}, &model.Inbound{
- Id: ib.Id, Settings: clientsSettings(t, []model.Client{{Email: "z@stale", ID: "aaaaaaaa-0000-0000-0000-00000000000c", Enable: true}}),
- }); err != nil {
- t.Fatalf("AddInboundClient: %v", err)
- }
- form.Remark = "edited"
- if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
- t.Fatalf("UpdateInbound: %v", err)
- }
- if _, ok := settingsClient(t, ib.Id, "z@stale"); !ok || !inboundLinksEmail(t, ib.Id, "z@stale") {
- t.Fatalf("client added while the form was open was dropped: in settings=%v linked=%v", ok, inboundLinksEmail(t, ib.Id, "z@stale"))
- }
- }
- // A client deleted while the modal was open is still in the list it posts
- // back; saving must not restore its access.
- func TestInboundFormSaveDoesNotResurrectDeletedClient(t *testing.T) {
- setupBulkDB(t)
- ib := seedRenewableNeighbour(t, 23205, nil)
- form := *ib
- if _, err := (&ClientService{}).DelInboundClientByEmail(&InboundService{}, ib.Id, "x@stale", false, true); err != nil {
- t.Fatalf("DelInboundClientByEmail: %v", err)
- }
- form.Remark = "edited"
- if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
- t.Fatalf("UpdateInbound: %v", err)
- }
- if _, ok := settingsClient(t, ib.Id, "x@stale"); ok || inboundLinksEmail(t, ib.Id, "x@stale") {
- t.Fatalf("deleted client came back: in settings=%v linked=%v", ok, inboundLinksEmail(t, ib.Id, "x@stale"))
- }
- }
- // An inbound switched off while the modal was open stays off when the form,
- // which still holds enable=true, is saved.
- func TestInboundFormSaveKeepsEnableToggledWhileOpen(t *testing.T) {
- setupBulkDB(t)
- ib := seedRenewableNeighbour(t, 23206, nil)
- form := *ib
- if _, err := (&InboundService{}).SetInboundEnable(ib.Id, false); err != nil {
- t.Fatalf("SetInboundEnable: %v", err)
- }
- form.Remark = "edited"
- if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
- t.Fatalf("UpdateInbound: %v", err)
- }
- saved, err := (&InboundService{}).GetInbound(ib.Id)
- if err != nil {
- t.Fatalf("GetInbound: %v", err)
- }
- if saved.Enable || saved.Remark != "edited" {
- t.Fatalf("after save: enable=%v remark=%q, want disabled and edited", saved.Enable, saved.Remark)
- }
- }
|