inbound_update_stale_form_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. "gorm.io/gorm"
  9. )
  10. func readClientTraffic(t *testing.T, email string) xray.ClientTraffic {
  11. t.Helper()
  12. var row xray.ClientTraffic
  13. if err := database.GetDB().Where("email = ?", email).First(&row).Error; err != nil {
  14. t.Fatalf("read client_traffics %s: %v", email, err)
  15. }
  16. return row
  17. }
  18. // The inbound modal posts the clients it loaded on open; a renewal committed
  19. // while it was open must survive the save in settings, record and stats.
  20. func TestInboundFormSaveKeepsClientRenewedWhileOpen(t *testing.T) {
  21. setupBulkDB(t)
  22. ib := seedRenewableNeighbour(t, 23201, nil)
  23. form := *ib
  24. if err := database.GetDB().Transaction(autoRenewTick); err != nil {
  25. t.Fatalf("autoRenew: %v", err)
  26. }
  27. form.Remark = "edited"
  28. if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
  29. t.Fatalf("UpdateInbound: %v", err)
  30. }
  31. requireNeighbourRenewed(t, ib.Id)
  32. now := time.Now().UnixMilli()
  33. if row := readClientTraffic(t, "y@stale"); !row.Enable || row.ExpiryTime <= now {
  34. t.Fatalf("client_traffics rolled back: enable=%v expiryTime=%d", row.Enable, row.ExpiryTime)
  35. }
  36. if rec := lookupClientRecord(t, "y@stale"); !rec.Enable || rec.ExpiryTime <= now {
  37. t.Fatalf("client record rolled back: enable=%v expiryTime=%d", rec.Enable, rec.ExpiryTime)
  38. }
  39. }
  40. // On a node the master's push is authoritative, lifecycle fields included.
  41. func TestInboundUpdateFromMasterAppliesClientLifecycle(t *testing.T) {
  42. setupBulkDB(t)
  43. ib := seedRenewableNeighbour(t, 23202, nil)
  44. clients, err := (&InboundService{}).GetClients(ib)
  45. if err != nil {
  46. t.Fatalf("GetClients: %v", err)
  47. }
  48. for i := range clients {
  49. if clients[i].Email == "x@stale" {
  50. clients[i].Enable = false
  51. }
  52. }
  53. push := *ib
  54. push.Settings = clientsSettings(t, clients)
  55. if _, _, err := (&InboundService{FromNodeSync: true}).UpdateInbound(&push); err != nil {
  56. t.Fatalf("UpdateInbound: %v", err)
  57. }
  58. if x, _ := settingsClient(t, ib.Id, "x@stale"); x.Enable {
  59. t.Fatal("master push disabling x@stale was ignored in settings")
  60. }
  61. if readClientTraffic(t, "x@stale").Enable {
  62. t.Fatal("master push disabling x@stale was ignored in client_traffics")
  63. }
  64. }
  65. // Traffic the poll adds after UpdateInbound read the row must not be written
  66. // back over by the edit.
  67. func TestInboundUpdateKeepsTrafficAddedMidEdit(t *testing.T) {
  68. setupBulkDB(t)
  69. ib := seedRenewableNeighbour(t, 23203, nil)
  70. form := *ib
  71. form.Remark = "edited"
  72. commitTickBetweenReadAndWrite(t, func(tx *gorm.DB) error {
  73. return (&InboundService{}).addInboundTraffic(tx, []*xray.Traffic{{IsInbound: true, Tag: ib.Tag, Up: 100, Down: 50}})
  74. }, func() {
  75. if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
  76. t.Errorf("UpdateInbound: %v", err)
  77. }
  78. })
  79. saved, err := (&InboundService{}).GetInbound(ib.Id)
  80. if err != nil {
  81. t.Fatalf("GetInbound: %v", err)
  82. }
  83. if saved.Up != 100 || saved.Down != 50 || saved.Remark != "edited" {
  84. t.Fatalf("inbound after edit: up=%d down=%d remark=%q, want 100/50/edited", saved.Up, saved.Down, saved.Remark)
  85. }
  86. }
  87. func inboundLinksEmail(t *testing.T, inboundId int, email string) bool {
  88. t.Helper()
  89. var n int64
  90. if err := database.GetDB().Table("client_inbounds").
  91. Joins("JOIN clients ON clients.id = client_inbounds.client_id").
  92. Where("client_inbounds.inbound_id = ? AND clients.email = ?", inboundId, email).
  93. Count(&n).Error; err != nil {
  94. t.Fatalf("count links: %v", err)
  95. }
  96. return n > 0
  97. }
  98. // A client added while the modal was open is not in the list it posts back;
  99. // saving the inbound must not detach it.
  100. func TestInboundFormSaveKeepsClientAddedWhileOpen(t *testing.T) {
  101. setupBulkDB(t)
  102. ib := seedRenewableNeighbour(t, 23204, nil)
  103. form := *ib
  104. if _, err := (&ClientService{}).AddInboundClient(&InboundService{}, &model.Inbound{
  105. Id: ib.Id, Settings: clientsSettings(t, []model.Client{{Email: "z@stale", ID: "aaaaaaaa-0000-0000-0000-00000000000c", Enable: true}}),
  106. }); err != nil {
  107. t.Fatalf("AddInboundClient: %v", err)
  108. }
  109. form.Remark = "edited"
  110. if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
  111. t.Fatalf("UpdateInbound: %v", err)
  112. }
  113. if _, ok := settingsClient(t, ib.Id, "z@stale"); !ok || !inboundLinksEmail(t, ib.Id, "z@stale") {
  114. t.Fatalf("client added while the form was open was dropped: in settings=%v linked=%v", ok, inboundLinksEmail(t, ib.Id, "z@stale"))
  115. }
  116. }
  117. // A client deleted while the modal was open is still in the list it posts
  118. // back; saving must not restore its access.
  119. func TestInboundFormSaveDoesNotResurrectDeletedClient(t *testing.T) {
  120. setupBulkDB(t)
  121. ib := seedRenewableNeighbour(t, 23205, nil)
  122. form := *ib
  123. if _, err := (&ClientService{}).DelInboundClientByEmail(&InboundService{}, ib.Id, "x@stale", false, true); err != nil {
  124. t.Fatalf("DelInboundClientByEmail: %v", err)
  125. }
  126. form.Remark = "edited"
  127. if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
  128. t.Fatalf("UpdateInbound: %v", err)
  129. }
  130. if _, ok := settingsClient(t, ib.Id, "x@stale"); ok || inboundLinksEmail(t, ib.Id, "x@stale") {
  131. t.Fatalf("deleted client came back: in settings=%v linked=%v", ok, inboundLinksEmail(t, ib.Id, "x@stale"))
  132. }
  133. }
  134. // An inbound switched off while the modal was open stays off when the form,
  135. // which still holds enable=true, is saved.
  136. func TestInboundFormSaveKeepsEnableToggledWhileOpen(t *testing.T) {
  137. setupBulkDB(t)
  138. ib := seedRenewableNeighbour(t, 23206, nil)
  139. form := *ib
  140. if _, err := (&InboundService{}).SetInboundEnable(ib.Id, false); err != nil {
  141. t.Fatalf("SetInboundEnable: %v", err)
  142. }
  143. form.Remark = "edited"
  144. if _, _, err := (&InboundService{}).UpdateInbound(&form); err != nil {
  145. t.Fatalf("UpdateInbound: %v", err)
  146. }
  147. saved, err := (&InboundService{}).GetInbound(ib.Id)
  148. if err != nil {
  149. t.Fatalf("GetInbound: %v", err)
  150. }
  151. if saved.Enable || saved.Remark != "edited" {
  152. t.Fatalf("after save: enable=%v remark=%q, want disabled and edited", saved.Enable, saved.Remark)
  153. }
  154. }