sub_balancer_test.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. package controller
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "net/http/httptest"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "testing"
  10. "github.com/gin-gonic/gin"
  11. "github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
  12. )
  13. func setupSubBalancerRouter(t *testing.T) *gin.Engine {
  14. t.Helper()
  15. t.Setenv("XUI_DB_FOLDER", t.TempDir())
  16. dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
  17. gin.SetMode(gin.TestMode)
  18. router := gin.New()
  19. NewSubBalancerController(router.Group("/panel/api"))
  20. return router
  21. }
  22. func subBalancerPost(t *testing.T, router *gin.Engine, path, body string) *httptest.ResponseRecorder {
  23. t.Helper()
  24. req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body))
  25. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  26. resp := httptest.NewRecorder()
  27. router.ServeHTTP(resp, req)
  28. return resp
  29. }
  30. func responseObj(t *testing.T, body string) map[string]any {
  31. t.Helper()
  32. var m map[string]any
  33. if err := json.Unmarshal([]byte(body), &m); err != nil {
  34. t.Fatalf("unmarshal response %q: %v", body, err)
  35. }
  36. return m
  37. }
  38. // enabled absent on create defaults to true; "false" disables; a non-boolean
  39. // value is rejected so a malformed toggle can't silently flip the row.
  40. func TestSubBalancerController_EnabledParsing(t *testing.T) {
  41. router := setupSubBalancerRouter(t)
  42. base := "remark=auto&strategy=random&sortOrder=1&inboundIds=1"
  43. resp := subBalancerPost(t, router, "/panel/api/sub-balancers", base)
  44. if !strings.Contains(resp.Body.String(), `"success":true`) {
  45. t.Fatalf("create no enabled: %s", resp.Body.String())
  46. }
  47. bal := responseObj(t, resp.Body.String())["obj"].(map[string]any)
  48. if bal["enabled"] != true {
  49. t.Fatalf("absent enabled = %v, want true", bal["enabled"])
  50. }
  51. resp = subBalancerPost(t, router, "/panel/api/sub-balancers", base+"&enabled=false")
  52. bal = responseObj(t, resp.Body.String())["obj"].(map[string]any)
  53. if bal["enabled"] != false {
  54. t.Fatalf("enabled=false -> %v, want false", bal["enabled"])
  55. }
  56. resp = subBalancerPost(t, router, "/panel/api/sub-balancers", base+"&enabled=bogus")
  57. if !strings.Contains(resp.Body.String(), `"success":false`) {
  58. t.Fatalf("enabled=bogus should be rejected: %s", resp.Body.String())
  59. }
  60. }
  61. // An update omitting enabled preserves the stored value instead of resetting it
  62. // to the create default — a partial PATCH must not clobber the toggle.
  63. func TestSubBalancerController_UpdatePreservesEnabledWhenAbsent(t *testing.T) {
  64. router := setupSubBalancerRouter(t)
  65. base := "remark=auto&strategy=random&sortOrder=1&inboundIds=1"
  66. resp := subBalancerPost(t, router, "/panel/api/sub-balancers", base+"&enabled=false")
  67. bal := responseObj(t, resp.Body.String())["obj"].(map[string]any)
  68. id := strconv.Itoa(int(bal["id"].(float64)))
  69. if bal["enabled"] != false {
  70. t.Fatalf("setup: enabled = %v, want false", bal["enabled"])
  71. }
  72. resp = subBalancerPost(t, router, "/panel/api/sub-balancers/"+id, "remark=renamed&strategy=random&sortOrder=1&inboundIds=1")
  73. if !strings.Contains(resp.Body.String(), `"success":true`) {
  74. t.Fatalf("update: %s", resp.Body.String())
  75. }
  76. bal = responseObj(t, resp.Body.String())["obj"].(map[string]any)
  77. if bal["enabled"] != false {
  78. t.Fatalf("update without enabled = %v, want preserved false", bal["enabled"])
  79. }
  80. if bal["remark"] != "renamed" {
  81. t.Fatalf("remark = %v, want renamed", bal["remark"])
  82. }
  83. resp = subBalancerPost(t, router, "/panel/api/sub-balancers/"+id, "remark=renamed&strategy=random&sortOrder=1&inboundIds=1&enabled=true")
  84. bal = responseObj(t, resp.Body.String())["obj"].(map[string]any)
  85. if bal["enabled"] != true {
  86. t.Fatalf("enabled=true -> %v, want true", bal["enabled"])
  87. }
  88. }