proxy_data_handler_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package admin
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "net/http"
  6. "net/http/httptest"
  7. "testing"
  8. "time"
  9. "github.com/Wei-Shaw/sub2api/internal/service"
  10. "github.com/gin-gonic/gin"
  11. "github.com/stretchr/testify/require"
  12. )
  13. type proxyDataResponse struct {
  14. Code int `json:"code"`
  15. Data DataPayload `json:"data"`
  16. }
  17. type proxyImportResponse struct {
  18. Code int `json:"code"`
  19. Data DataImportResult `json:"data"`
  20. }
  21. func setupProxyDataRouter() (*gin.Engine, *stubAdminService) {
  22. gin.SetMode(gin.TestMode)
  23. router := gin.New()
  24. adminSvc := newStubAdminService()
  25. h := NewProxyHandler(adminSvc)
  26. router.GET("/api/v1/admin/proxies/data", h.ExportData)
  27. router.POST("/api/v1/admin/proxies/data", h.ImportData)
  28. return router, adminSvc
  29. }
  30. func TestProxyExportDataRespectsFilters(t *testing.T) {
  31. router, adminSvc := setupProxyDataRouter()
  32. adminSvc.proxies = []service.Proxy{
  33. {
  34. ID: 1,
  35. Name: "proxy-a",
  36. Protocol: "http",
  37. Host: "127.0.0.1",
  38. Port: 8080,
  39. Username: "user",
  40. Password: "pass",
  41. Status: service.StatusActive,
  42. },
  43. {
  44. ID: 2,
  45. Name: "proxy-b",
  46. Protocol: "https",
  47. Host: "10.0.0.2",
  48. Port: 443,
  49. Username: "u",
  50. Password: "p",
  51. Status: service.StatusDisabled,
  52. },
  53. }
  54. rec := httptest.NewRecorder()
  55. req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/data?protocol=https", nil)
  56. router.ServeHTTP(rec, req)
  57. require.Equal(t, http.StatusOK, rec.Code)
  58. var resp proxyDataResponse
  59. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  60. require.Equal(t, 0, resp.Code)
  61. require.Empty(t, resp.Data.Type)
  62. require.Equal(t, 0, resp.Data.Version)
  63. require.Len(t, resp.Data.Proxies, 1)
  64. require.Len(t, resp.Data.Accounts, 0)
  65. require.Equal(t, "https", resp.Data.Proxies[0].Protocol)
  66. require.Equal(t, 1, adminSvc.lastListProxies.calls)
  67. require.Equal(t, "https", adminSvc.lastListProxies.protocol)
  68. require.Equal(t, "id", adminSvc.lastListProxies.sortBy)
  69. require.Equal(t, "desc", adminSvc.lastListProxies.sortOrder)
  70. }
  71. func TestProxyExportDataWithSelectedIDs(t *testing.T) {
  72. router, adminSvc := setupProxyDataRouter()
  73. adminSvc.proxies = []service.Proxy{
  74. {
  75. ID: 1,
  76. Name: "proxy-a",
  77. Protocol: "http",
  78. Host: "127.0.0.1",
  79. Port: 8080,
  80. Username: "user",
  81. Password: "pass",
  82. Status: service.StatusActive,
  83. },
  84. {
  85. ID: 2,
  86. Name: "proxy-b",
  87. Protocol: "https",
  88. Host: "10.0.0.2",
  89. Port: 443,
  90. Username: "u",
  91. Password: "p",
  92. Status: service.StatusDisabled,
  93. },
  94. }
  95. rec := httptest.NewRecorder()
  96. req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/data?ids=2", nil)
  97. router.ServeHTTP(rec, req)
  98. require.Equal(t, http.StatusOK, rec.Code)
  99. var resp proxyDataResponse
  100. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  101. require.Equal(t, 0, resp.Code)
  102. require.Len(t, resp.Data.Proxies, 1)
  103. require.Equal(t, "https", resp.Data.Proxies[0].Protocol)
  104. require.Equal(t, "10.0.0.2", resp.Data.Proxies[0].Host)
  105. require.Equal(t, 0, adminSvc.lastListProxies.calls)
  106. }
  107. func TestProxyExportDataPassesSortParams(t *testing.T) {
  108. router, adminSvc := setupProxyDataRouter()
  109. adminSvc.proxies = []service.Proxy{
  110. {
  111. ID: 1,
  112. Name: "proxy-a",
  113. Protocol: "http",
  114. Host: "127.0.0.1",
  115. Port: 8080,
  116. Username: "user",
  117. Password: "pass",
  118. Status: service.StatusActive,
  119. },
  120. }
  121. rec := httptest.NewRecorder()
  122. req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/data?protocol=http&status=active&search=proxy&sort_by=name&sort_order=asc", nil)
  123. router.ServeHTTP(rec, req)
  124. require.Equal(t, http.StatusOK, rec.Code)
  125. require.Equal(t, 1, adminSvc.lastListProxies.calls)
  126. require.Equal(t, "http", adminSvc.lastListProxies.protocol)
  127. require.Equal(t, "active", adminSvc.lastListProxies.status)
  128. require.Equal(t, "proxy", adminSvc.lastListProxies.search)
  129. require.Equal(t, "name", adminSvc.lastListProxies.sortBy)
  130. require.Equal(t, "asc", adminSvc.lastListProxies.sortOrder)
  131. }
  132. func TestProxyExportDataSortByAccountCountUsesAccountCountListing(t *testing.T) {
  133. router, adminSvc := setupProxyDataRouter()
  134. adminSvc.proxies = []service.Proxy{
  135. {
  136. ID: 1,
  137. Name: "proxy-id-1",
  138. Protocol: "http",
  139. Host: "127.0.0.1",
  140. Port: 8080,
  141. Status: service.StatusActive,
  142. },
  143. {
  144. ID: 2,
  145. Name: "proxy-id-2",
  146. Protocol: "http",
  147. Host: "127.0.0.2",
  148. Port: 8081,
  149. Status: service.StatusActive,
  150. },
  151. }
  152. adminSvc.proxyCounts = []service.ProxyWithAccountCount{
  153. {
  154. Proxy: service.Proxy{
  155. ID: 2,
  156. Name: "proxy-count-high",
  157. Protocol: "http",
  158. Host: "127.0.0.2",
  159. Port: 8081,
  160. Status: service.StatusActive,
  161. },
  162. AccountCount: 9,
  163. },
  164. {
  165. Proxy: service.Proxy{
  166. ID: 1,
  167. Name: "proxy-count-low",
  168. Protocol: "http",
  169. Host: "127.0.0.1",
  170. Port: 8080,
  171. Status: service.StatusActive,
  172. },
  173. AccountCount: 1,
  174. },
  175. }
  176. rec := httptest.NewRecorder()
  177. req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/data?sort_by=account_count&sort_order=desc", nil)
  178. router.ServeHTTP(rec, req)
  179. require.Equal(t, http.StatusOK, rec.Code)
  180. var resp proxyDataResponse
  181. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  182. require.Equal(t, 0, resp.Code)
  183. require.Len(t, resp.Data.Proxies, 2)
  184. require.Equal(t, "proxy-count-high", resp.Data.Proxies[0].Name)
  185. require.Equal(t, "proxy-count-low", resp.Data.Proxies[1].Name)
  186. require.Equal(t, 0, adminSvc.lastListProxies.calls)
  187. }
  188. func TestProxyImportDataReusesAndTriggersLatencyProbe(t *testing.T) {
  189. router, adminSvc := setupProxyDataRouter()
  190. adminSvc.proxies = []service.Proxy{
  191. {
  192. ID: 1,
  193. Name: "proxy-a",
  194. Protocol: "http",
  195. Host: "127.0.0.1",
  196. Port: 8080,
  197. Username: "user",
  198. Password: "pass",
  199. Status: service.StatusActive,
  200. },
  201. }
  202. payload := map[string]any{
  203. "data": map[string]any{
  204. "type": dataType,
  205. "version": dataVersion,
  206. "proxies": []map[string]any{
  207. {
  208. "proxy_key": "http|127.0.0.1|8080|user|pass",
  209. "name": "proxy-a",
  210. "protocol": "http",
  211. "host": "127.0.0.1",
  212. "port": 8080,
  213. "username": "user",
  214. "password": "pass",
  215. "status": "inactive",
  216. },
  217. {
  218. "proxy_key": "https|10.0.0.2|443|u|p",
  219. "name": "proxy-b",
  220. "protocol": "https",
  221. "host": "10.0.0.2",
  222. "port": 443,
  223. "username": "u",
  224. "password": "p",
  225. "status": "active",
  226. },
  227. },
  228. "accounts": []map[string]any{},
  229. },
  230. }
  231. body, _ := json.Marshal(payload)
  232. rec := httptest.NewRecorder()
  233. req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/proxies/data", bytes.NewReader(body))
  234. req.Header.Set("Content-Type", "application/json")
  235. router.ServeHTTP(rec, req)
  236. require.Equal(t, http.StatusOK, rec.Code)
  237. var resp proxyImportResponse
  238. require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
  239. require.Equal(t, 0, resp.Code)
  240. require.Equal(t, 1, resp.Data.ProxyCreated)
  241. require.Equal(t, 1, resp.Data.ProxyReused)
  242. require.Equal(t, 0, resp.Data.ProxyFailed)
  243. adminSvc.mu.Lock()
  244. updatedIDs := append([]int64(nil), adminSvc.updatedProxyIDs...)
  245. adminSvc.mu.Unlock()
  246. require.Contains(t, updatedIDs, int64(1))
  247. require.Eventually(t, func() bool {
  248. adminSvc.mu.Lock()
  249. defer adminSvc.mu.Unlock()
  250. return len(adminSvc.testedProxyIDs) == 1
  251. }, time.Second, 10*time.Millisecond)
  252. }