host_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package service
  2. import (
  3. "testing"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. func mkHost(t *testing.T, svc *HostService, inboundId int, remark string, order int) *model.Host {
  8. t.Helper()
  9. h, err := svc.AddHost(&model.Host{
  10. InboundId: inboundId,
  11. Remark: remark,
  12. SortOrder: order,
  13. Address: remark + ".example.com",
  14. Port: 8443,
  15. })
  16. if err != nil {
  17. t.Fatalf("AddHost %s: %v", remark, err)
  18. }
  19. return h
  20. }
  21. // TestAddHost_GetHostsByInbound: create persists; query returns by inbound,
  22. // ordered by sort_order then id.
  23. func TestAddHost_GetHostsByInbound(t *testing.T) {
  24. setupBulkDB(t)
  25. svc := &HostService{}
  26. ib := mkInbound(t, 443, model.VLESS, `{"clients":[]}`)
  27. h1 := mkHost(t, svc, ib.Id, "b", 2)
  28. h2 := mkHost(t, svc, ib.Id, "a", 1)
  29. got, err := svc.GetHostsByInbound(ib.Id)
  30. if err != nil {
  31. t.Fatalf("GetHostsByInbound: %v", err)
  32. }
  33. if len(got) != 2 {
  34. t.Fatalf("len = %d, want 2", len(got))
  35. }
  36. if got[0].Id != h2.Id || got[1].Id != h1.Id {
  37. t.Fatalf("order = [%d,%d], want [%d,%d] (sort_order asc)", got[0].Id, got[1].Id, h2.Id, h1.Id)
  38. }
  39. if got[0].Address != "a.example.com" {
  40. t.Fatalf("address not persisted: %q", got[0].Address)
  41. }
  42. }
  43. // TestAddHost_RejectsUnknownInbound: a host whose inbound does not exist is refused.
  44. func TestAddHost_RejectsUnknownInbound(t *testing.T) {
  45. setupBulkDB(t)
  46. svc := &HostService{}
  47. if _, err := svc.AddHost(&model.Host{InboundId: 99999, Remark: "x"}); err == nil {
  48. t.Fatalf("expected error adding host to unknown inbound")
  49. }
  50. }
  51. // TestReorderHosts: reorder updates sort_order and re-query reflects new order.
  52. func TestReorderHosts(t *testing.T) {
  53. setupBulkDB(t)
  54. svc := &HostService{}
  55. ib := mkInbound(t, 443, model.VLESS, `{"clients":[]}`)
  56. h1 := mkHost(t, svc, ib.Id, "h1", 0)
  57. h2 := mkHost(t, svc, ib.Id, "h2", 0)
  58. h3 := mkHost(t, svc, ib.Id, "h3", 0)
  59. want := []int{h3.Id, h1.Id, h2.Id}
  60. if err := svc.ReorderHosts(want); err != nil {
  61. t.Fatalf("ReorderHosts: %v", err)
  62. }
  63. got, _ := svc.GetHostsByInbound(ib.Id)
  64. for i, h := range got {
  65. if h.Id != want[i] {
  66. t.Fatalf("position %d = %d, want %d", i, h.Id, want[i])
  67. }
  68. if h.SortOrder != i {
  69. t.Fatalf("host %d sort_order = %d, want %d", h.Id, h.SortOrder, i)
  70. }
  71. }
  72. }
  73. // TestSetHostEnableAndBulk: per-row and bulk enable/disable toggles persist.
  74. func TestSetHostEnableAndBulk(t *testing.T) {
  75. setupBulkDB(t)
  76. svc := &HostService{}
  77. ib := mkInbound(t, 443, model.VLESS, `{"clients":[]}`)
  78. h1 := mkHost(t, svc, ib.Id, "h1", 0)
  79. h2 := mkHost(t, svc, ib.Id, "h2", 1)
  80. if err := svc.SetHostEnable(h1.Id, false); err != nil {
  81. t.Fatalf("SetHostEnable: %v", err)
  82. }
  83. if g, _ := svc.GetHost(h1.Id); g == nil || !g.IsDisabled {
  84. t.Fatalf("h1 should be disabled after SetHostEnable(false)")
  85. }
  86. if err := svc.SetHostsEnable([]int{h1.Id, h2.Id}, true); err != nil {
  87. t.Fatalf("SetHostsEnable(true): %v", err)
  88. }
  89. for _, id := range []int{h1.Id, h2.Id} {
  90. if g, _ := svc.GetHost(id); g == nil || g.IsDisabled {
  91. t.Fatalf("host %d should be enabled", id)
  92. }
  93. }
  94. if err := svc.SetHostsEnable([]int{h1.Id, h2.Id}, false); err != nil {
  95. t.Fatalf("SetHostsEnable(false): %v", err)
  96. }
  97. for _, id := range []int{h1.Id, h2.Id} {
  98. if g, _ := svc.GetHost(id); g == nil || !g.IsDisabled {
  99. t.Fatalf("host %d should be disabled", id)
  100. }
  101. }
  102. }
  103. // TestDeleteHosts: bulk delete removes exactly the named rows.
  104. func TestDeleteHosts(t *testing.T) {
  105. setupBulkDB(t)
  106. svc := &HostService{}
  107. ib := mkInbound(t, 443, model.VLESS, `{"clients":[]}`)
  108. h1 := mkHost(t, svc, ib.Id, "h1", 0)
  109. h2 := mkHost(t, svc, ib.Id, "h2", 1)
  110. h3 := mkHost(t, svc, ib.Id, "h3", 2)
  111. if err := svc.DeleteHosts([]int{h1.Id, h3.Id}); err != nil {
  112. t.Fatalf("DeleteHosts: %v", err)
  113. }
  114. got, _ := svc.GetHostsByInbound(ib.Id)
  115. if len(got) != 1 || got[0].Id != h2.Id {
  116. t.Fatalf("remaining = %v, want only h2 (%d)", got, h2.Id)
  117. }
  118. }
  119. // TestDeleteInboundCascadesHosts: deleting an inbound deletes its hosts.
  120. func TestDeleteInboundCascadesHosts(t *testing.T) {
  121. setupBulkDB(t)
  122. svc := &HostService{}
  123. inboundSvc := &InboundService{}
  124. // Disabled local inbound so DelInbound skips the runtime push.
  125. ib := &model.Inbound{Tag: "casc", Enable: false, Port: 4443, Protocol: model.VLESS, Settings: `{"clients":[]}`}
  126. if err := database.GetDB().Create(ib).Error; err != nil {
  127. t.Fatalf("create inbound: %v", err)
  128. }
  129. mkHost(t, svc, ib.Id, "h1", 0)
  130. mkHost(t, svc, ib.Id, "h2", 1)
  131. if _, err := inboundSvc.DelInbound(ib.Id); err != nil {
  132. t.Fatalf("DelInbound: %v", err)
  133. }
  134. got, _ := svc.GetHostsByInbound(ib.Id)
  135. if len(got) != 0 {
  136. t.Fatalf("hosts not cascaded on inbound delete, len = %d", len(got))
  137. }
  138. }
  139. // TestGetAllTags: distinct, sorted tags across all hosts.
  140. func TestGetAllTags(t *testing.T) {
  141. setupBulkDB(t)
  142. svc := &HostService{}
  143. ib := mkInbound(t, 443, model.VLESS, `{"clients":[]}`)
  144. if _, err := svc.AddHost(&model.Host{InboundId: ib.Id, Remark: "h1", Tags: []string{"EU", "CDN"}}); err != nil {
  145. t.Fatalf("AddHost: %v", err)
  146. }
  147. if _, err := svc.AddHost(&model.Host{InboundId: ib.Id, Remark: "h2", Tags: []string{"CDN", "FAST"}}); err != nil {
  148. t.Fatalf("AddHost: %v", err)
  149. }
  150. tags, err := svc.GetAllTags()
  151. if err != nil {
  152. t.Fatalf("GetAllTags: %v", err)
  153. }
  154. want := []string{"CDN", "EU", "FAST"}
  155. if len(tags) != len(want) {
  156. t.Fatalf("tags = %v, want %v", tags, want)
  157. }
  158. for i := range want {
  159. if tags[i] != want[i] {
  160. t.Fatalf("tags = %v, want %v", tags, want)
  161. }
  162. }
  163. }