sub_balancer_test.go 3.6 KB

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