| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- 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"
- )
- func setupAccountMixedChannelRouter(adminSvc *stubAdminService) *gin.Engine {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- accountHandler := NewAccountHandler(adminSvc, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
- router.POST("/api/v1/admin/accounts/check-mixed-channel", accountHandler.CheckMixedChannel)
- router.POST("/api/v1/admin/accounts", accountHandler.Create)
- router.PUT("/api/v1/admin/accounts/:id", accountHandler.Update)
- router.POST("/api/v1/admin/accounts/bulk-update", accountHandler.BulkUpdate)
- return router
- }
- func TestAccountHandlerCheckMixedChannelNoRisk(t *testing.T) {
- adminSvc := newStubAdminService()
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "platform": "antigravity",
- "group_ids": []int64{27},
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/check-mixed-channel", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, float64(0), resp["code"])
- data, ok := resp["data"].(map[string]any)
- require.True(t, ok)
- require.Equal(t, false, data["has_risk"])
- require.Equal(t, int64(0), adminSvc.lastMixedCheck.accountID)
- require.Equal(t, "antigravity", adminSvc.lastMixedCheck.platform)
- require.Equal(t, []int64{27}, adminSvc.lastMixedCheck.groupIDs)
- }
- func TestAccountHandlerCheckMixedChannelWithRisk(t *testing.T) {
- adminSvc := newStubAdminService()
- adminSvc.checkMixedErr = &service.MixedChannelError{
- GroupID: 27,
- GroupName: "claude-max",
- CurrentPlatform: "Antigravity",
- OtherPlatform: "Anthropic",
- }
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "platform": "antigravity",
- "group_ids": []int64{27},
- "account_id": 99,
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/check-mixed-channel", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, float64(0), resp["code"])
- data, ok := resp["data"].(map[string]any)
- require.True(t, ok)
- require.Equal(t, true, data["has_risk"])
- require.Equal(t, "mixed_channel_warning", data["error"])
- details, ok := data["details"].(map[string]any)
- require.True(t, ok)
- require.Equal(t, float64(27), details["group_id"])
- require.Equal(t, "claude-max", details["group_name"])
- require.Equal(t, "Antigravity", details["current_platform"])
- require.Equal(t, "Anthropic", details["other_platform"])
- require.Equal(t, int64(99), adminSvc.lastMixedCheck.accountID)
- }
- func TestAccountHandlerCreateMixedChannelConflictSimplifiedResponse(t *testing.T) {
- adminSvc := newStubAdminService()
- adminSvc.createAccountErr = &service.MixedChannelError{
- GroupID: 27,
- GroupName: "claude-max",
- CurrentPlatform: "Antigravity",
- OtherPlatform: "Anthropic",
- }
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "name": "ag-oauth-1",
- "platform": "antigravity",
- "type": "oauth",
- "credentials": map[string]any{"refresh_token": "rt"},
- "group_ids": []int64{27},
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusConflict, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, "mixed_channel_warning", resp["error"])
- require.Contains(t, resp["message"], "mixed_channel_warning")
- _, hasDetails := resp["details"]
- _, hasRequireConfirmation := resp["require_confirmation"]
- require.False(t, hasDetails)
- require.False(t, hasRequireConfirmation)
- }
- func TestAccountHandlerUpdateMixedChannelConflictSimplifiedResponse(t *testing.T) {
- adminSvc := newStubAdminService()
- adminSvc.updateAccountErr = &service.MixedChannelError{
- GroupID: 27,
- GroupName: "claude-max",
- CurrentPlatform: "Antigravity",
- OtherPlatform: "Anthropic",
- }
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "group_ids": []int64{27},
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPut, "/api/v1/admin/accounts/3", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusConflict, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, "mixed_channel_warning", resp["error"])
- require.Contains(t, resp["message"], "mixed_channel_warning")
- _, hasDetails := resp["details"]
- _, hasRequireConfirmation := resp["require_confirmation"]
- require.False(t, hasDetails)
- require.False(t, hasRequireConfirmation)
- }
- func TestAccountHandlerUpdateMapsUpstreamBillingRateSyncSettings(t *testing.T) {
- adminSvc := newStubAdminService()
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "name": "gemini-key",
- "upstream_billing_probe_enabled": true,
- "upstream_billing_rate_sync_enabled": true,
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPut, "/api/v1/admin/accounts/42", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- require.NotNil(t, adminSvc.lastUpdateAccountInput)
- require.NotNil(t, adminSvc.lastUpdateAccountInput.ProbeEnabled)
- require.True(t, *adminSvc.lastUpdateAccountInput.ProbeEnabled)
- require.NotNil(t, adminSvc.lastUpdateAccountInput.RateSyncEnabled)
- require.True(t, *adminSvc.lastUpdateAccountInput.RateSyncEnabled)
- }
- func TestAccountHandlerBulkUpdateMixedChannelConflict(t *testing.T) {
- adminSvc := newStubAdminService()
- adminSvc.bulkUpdateAccountErr = &service.MixedChannelError{
- GroupID: 27,
- GroupName: "claude-max",
- CurrentPlatform: "Antigravity",
- OtherPlatform: "Anthropic",
- }
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "account_ids": []int64{1, 2, 3},
- "group_ids": []int64{27},
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/bulk-update", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusConflict, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, "mixed_channel_warning", resp["error"])
- require.Contains(t, resp["message"], "claude-max")
- }
- func TestAccountHandlerBulkUpdateMixedChannelConfirmSkips(t *testing.T) {
- adminSvc := newStubAdminService()
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "account_ids": []int64{1, 2},
- "group_ids": []int64{27},
- "confirm_mixed_channel_risk": true,
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/bulk-update", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, float64(0), resp["code"])
- data, ok := resp["data"].(map[string]any)
- require.True(t, ok)
- require.Equal(t, float64(2), data["success"])
- require.Equal(t, float64(0), data["failed"])
- }
- func TestBulkUpdateAcceptsFilterTargetRequest(t *testing.T) {
- adminSvc := newStubAdminService()
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "filters": map[string]any{
- "platform": "openai",
- "type": "oauth",
- "status": "active",
- "group": "12",
- "privacy_mode": "blocked",
- "search": "bulk-target",
- },
- "schedulable": true,
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/bulk-update", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
- require.Equal(t, float64(0), resp["code"])
- }
- func TestBulkUpdateAcceptsDedicatedUpstreamBillingProbeSetting(t *testing.T) {
- adminSvc := newStubAdminService()
- router := setupAccountMixedChannelRouter(adminSvc)
- body, _ := json.Marshal(map[string]any{
- "account_ids": []int64{1, 2},
- "upstream_billing_probe_enabled": false,
- })
- rec := httptest.NewRecorder()
- req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/accounts/bulk-update", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
- router.ServeHTTP(rec, req)
- require.Equal(t, http.StatusOK, rec.Code)
- require.NotNil(t, adminSvc.lastBulkUpdateAccountInput)
- require.NotNil(t, adminSvc.lastBulkUpdateAccountInput.ProbeEnabled)
- require.False(t, *adminSvc.lastBulkUpdateAccountInput.ProbeEnabled)
- }
|