| package admin |
|
|
| import ( |
| "bytes" |
| "encoding/json" |
| "net/http" |
| "net/http/httptest" |
| "testing" |
|
|
| "github.com/Wei-Shaw/sub2api/internal/service" |
| "github.com/gin-gonic/gin" |
| "github.com/stretchr/testify/require" |
| ) |
|
|
| type dataResponse struct { |
| Code int `json:"code"` |
| Data dataPayload `json:"data"` |
| } |
|
|
| type dataPayload struct { |
| Type string `json:"type"` |
| Version int `json:"version"` |
| Proxies []dataProxy `json:"proxies"` |
| Accounts []dataAccount `json:"accounts"` |
| } |
|
|
| type dataProxy struct { |
| ProxyKey string `json:"proxy_key"` |
| Name string `json:"name"` |
| Protocol string `json:"protocol"` |
| Host string `json:"host"` |
| Port int `json:"port"` |
| Username string `json:"username"` |
| Password string `json:"password"` |
| Status string `json:"status"` |
| } |
|
|
| type dataAccount struct { |
| Name string `json:"name"` |
| Platform string `json:"platform"` |
| Type string `json:"type"` |
| Credentials map[string]any `json:"credentials"` |
| Extra map[string]any `json:"extra"` |
| ProxyKey *string `json:"proxy_key"` |
| Concurrency int `json:"concurrency"` |
| Priority int `json:"priority"` |
| } |
|
|
| func setupAccountDataRouter() (*gin.Engine, *stubAdminService) { |
| gin.SetMode(gin.TestMode) |
| router := gin.New() |
| adminSvc := newStubAdminService() |
|
|
| h := NewAccountHandler( |
| adminSvc, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| ) |
|
|
| router.GET("/api/v1/admin/accounts/data", h.ExportData) |
| router.POST("/api/v1/admin/accounts/data", h.ImportData) |
| return router, adminSvc |
| } |
|
|
| func TestExportDataIncludesSecrets(t *testing.T) { |
| router, adminSvc := setupAccountDataRouter() |
|
|
| proxyID := int64(11) |
| adminSvc.proxies = []service.Proxy{ |
| { |
| ID: proxyID, |
| Name: "proxy", |
| Protocol: "http", |
| Host: "127.0.0.1", |
| Port: 8080, |
| Username: "user", |
| Password: "pass", |
| Status: service.StatusActive, |
| }, |
| { |
| ID: 12, |
| Name: "orphan", |
| Protocol: "https", |
| Host: "10.0.0.1", |
| Port: 443, |
| Username: "o", |
| Password: "p", |
| Status: service.StatusActive, |
| }, |
| } |
| adminSvc.accounts = []service.Account{ |
| { |
| ID: 21, |
| Name: "account", |
| Platform: service.PlatformOpenAI, |
| Type: service.AccountTypeOAuth, |
| Credentials: map[string]any{"token": "secret"}, |
| Extra: map[string]any{"note": "x"}, |
| ProxyID: &proxyID, |
| Concurrency: 3, |
| Priority: 50, |
| Status: service.StatusDisabled, |
| }, |
| } |
|
|
| rec := httptest.NewRecorder() |
| req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/accounts/data", nil) |
| router.ServeHTTP(rec, req) |
| require.Equal(t, http.StatusOK, rec.Code) |
|
|
| var resp dataResponse |
| require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) |
| require.Equal(t, 0, resp.Code) |
| require.Empty(t, resp.Data.Type) |
| require.Equal(t, 0, resp.Data.Version) |
| require.Len(t, resp.Data.Proxies, 1) |
| require.Equal(t, "pass", resp.Data.Proxies[0].Password) |
| require.Len(t, resp.Data.Accounts, 1) |
| require.Equal(t, "secret", resp.Data.Accounts[0].Credentials["token"]) |
| } |
|
|
| func TestExportDataWithoutProxies(t *testing.T) { |
| router, adminSvc := setupAccountDataRouter() |
|
|
| proxyID := int64(11) |
| adminSvc.proxies = []service.Proxy{ |
| { |
| ID: proxyID, |
| Name: "proxy", |
| Protocol: "http", |
| Host: "127.0.0.1", |
| Port: 8080, |
| Username: "user", |
| Password: "pass", |
| Status: service.StatusActive, |
| }, |
| } |
| adminSvc.accounts = []service.Account{ |
| { |
| ID: 21, |
| Name: "account", |
| Platform: service.PlatformOpenAI, |
| Type: service.AccountTypeOAuth, |
| Credentials: map[string]any{"token": "secret"}, |
| ProxyID: &proxyID, |
| Concurrency: 3, |
| Priority: 50, |
| Status: service.StatusDisabled, |
| }, |
| } |
|
|
| rec := httptest.NewRecorder() |
| req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/accounts/data?include_proxies=false", nil) |
| router.ServeHTTP(rec, req) |
| require.Equal(t, http.StatusOK, rec.Code) |
|
|
| var resp dataResponse |
| require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) |
| require.Equal(t, 0, resp.Code) |
| require.Len(t, resp.Data.Proxies, 0) |
| require.Len(t, resp.Data.Accounts, 1) |
| require.Nil(t, resp.Data.Accounts[0].ProxyKey) |
| } |
|
|
| func TestImportDataReusesProxyAndSkipsDefaultGroup(t *testing.T) { |
| router, adminSvc := setupAccountDataRouter() |
|
|
| adminSvc.proxies = []service.Proxy{ |
| { |
| ID: 1, |
| Name: "proxy", |
| Protocol: "socks5", |
| Host: "1.2.3.4", |
| Port: 1080, |
| Username: "u", |
| Password: "p", |
| Status: service.StatusActive, |
| }, |
| } |
|
|
| dataPayload := map[string]any{ |
| "data": map[string]any{ |
| "type": dataType, |
| "version": dataVersion, |
| "proxies": []map[string]any{ |
| { |
| "proxy_key": "socks5|1.2.3.4|1080|u|p", |
| "name": "proxy", |
| "protocol": "socks5", |
| "host": "1.2.3.4", |
| "port": 1080, |
| "username": "u", |
| "password": "p", |
| "status": "active", |
| }, |
| }, |
| "accounts": []map[string]any{ |
| { |
| "name": "acc", |
| "platform": service.PlatformOpenAI, |
| "type": service.AccountTypeOAuth, |
| "credentials": map[string]any{"token": "x"}, |
| "proxy_key": "socks5|1.2.3.4|1080|u|p", |
| "concurrency": 3, |
| "priority": 50, |
| }, |
| }, |
| }, |
| "skip_default_group_bind": true, |
| } |
|
|
| body, _ := json.Marshal(dataPayload) |
| rec := httptest.NewRecorder() |
| req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/data", bytes.NewReader(body)) |
| req.Header.Set("Content-Type", "application/json") |
| router.ServeHTTP(rec, req) |
| require.Equal(t, http.StatusOK, rec.Code) |
|
|
| require.Len(t, adminSvc.createdProxies, 0) |
| require.Len(t, adminSvc.createdAccounts, 1) |
| require.True(t, adminSvc.createdAccounts[0].SkipDefaultGroupBind) |
| } |
|
|