package handler import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/gin-gonic/gin" "github.com/perfect-panel/server/internal/config" logiccommon "github.com/perfect-panel/server/internal/logic/common" "github.com/perfect-panel/server/internal/model/client" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/xerr" "github.com/redis/go-redis/v9" "gorm.io/gorm" ) func TestSubscribeHandlerReturnsBusinessErrorForDisabledUser(t *testing.T) { gin.SetMode(gin.TestMode) redisServer, err := miniredis.Run() if err != nil { t.Fatalf("miniredis.Run() error = %v", err) } defer redisServer.Close() rdb := redis.NewClient(&redis.Options{Addr: redisServer.Addr()}) defer func() { _ = rdb.Close() }() if err := rdb.Set(context.Background(), logiccommon.UserEnableCacheKey(83696), "false", 0).Err(); err != nil { t.Fatalf("seed user enable cache: %v", err) } router := gin.New() router.GET("/api/subscribe", SubscribeHandler(&svc.ServiceContext{ Config: config.Config{ Subscribe: config.SubscribeConfig{ SubscribePath: "/api/subscribe", }, }, ClientModel: subscribeClientModelStub{ list: []*client.SubscribeApplication{ { Id: 1, UserAgent: "clashmeta", IsDefault: true, OutputFormat: "yaml", }, }, }, Redis: rdb, UserModel: subscribeUserModelStub{ subscribe: &user.Subscribe{Id: 35446, UserId: 83696, SubscribeId: 1, Token: "disabled-token"}, }, })) req := httptest.NewRequest(http.MethodGet, "/api/subscribe?token=disabled-token", nil) req.Header.Set("User-Agent", "ClashMetaForAndroid/2.11.7.Meta") rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected HTTP 200, got %d", rec.Code) } var resp struct { Code uint32 `json:"code"` Msg string `json:"msg"` } if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { t.Fatalf("unmarshal response: %v", err) } if resp.Code != xerr.UserDisabled { t.Fatalf("expected code %d, got %d (%s)", xerr.UserDisabled, resp.Code, resp.Msg) } } type subscribeClientModelStub struct { list []*client.SubscribeApplication err error } func (s subscribeClientModelStub) Insert(context.Context, *client.SubscribeApplication) error { return nil } func (s subscribeClientModelStub) FindOne(context.Context, int64) (*client.SubscribeApplication, error) { return nil, nil } func (s subscribeClientModelStub) Update(context.Context, *client.SubscribeApplication) error { return nil } func (s subscribeClientModelStub) Delete(context.Context, int64) error { return nil } func (s subscribeClientModelStub) List(context.Context) ([]*client.SubscribeApplication, error) { return s.list, s.err } func (s subscribeClientModelStub) Transaction(context.Context, func(*gorm.DB) error) error { return nil } type subscribeUserModelStub struct { subscribe *user.Subscribe subErr error findOne *user.User findOneErr error } func (s subscribeUserModelStub) Insert(context.Context, *user.User, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) FindOne(context.Context, int64) (*user.User, error) { if s.findOneErr != nil { return nil, s.findOneErr } return s.findOne, nil } func (s subscribeUserModelStub) Update(context.Context, *user.User, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) UpdateCommission(context.Context, int64, int64, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) Delete(context.Context, int64, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) Transaction(context.Context, func(*gorm.DB) error) error { return nil } func (s subscribeUserModelStub) QueryPageList(context.Context, int, int, *user.UserFilterParams) ([]*user.User, int64, error) { return nil, 0, nil } func (s subscribeUserModelStub) FindOneByReferCode(context.Context, string) (*user.User, error) { return nil, nil } func (s subscribeUserModelStub) BatchDeleteUser(context.Context, []int64, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) InsertSubscribe(context.Context, *user.Subscribe, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) FindOneSubscribeByToken(context.Context, string) (*user.Subscribe, error) { if s.subErr != nil { return nil, s.subErr } return s.subscribe, nil } func (s subscribeUserModelStub) FindSingleModeAnchorSubscribe(context.Context, int64) (*user.Subscribe, error) { return nil, nil } func (s subscribeUserModelStub) FindOneSubscribeByOrderId(context.Context, int64) (*user.Subscribe, error) { return nil, nil } func (s subscribeUserModelStub) FindOneSubscribe(context.Context, int64) (*user.Subscribe, error) { return nil, nil } func (s subscribeUserModelStub) UpdateSubscribe(context.Context, *user.Subscribe, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) DeleteSubscribe(context.Context, string, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) DeleteSubscribeById(context.Context, int64, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) QueryUserSubscribe(context.Context, int64, ...int64) ([]*user.SubscribeDetails, error) { return nil, nil } func (s subscribeUserModelStub) FindOneSubscribeDetailsById(context.Context, int64) (*user.SubscribeDetails, error) { return nil, nil } func (s subscribeUserModelStub) FindOneUserSubscribe(context.Context, int64) (*user.SubscribeDetails, error) { return nil, nil } func (s subscribeUserModelStub) FindUsersSubscribeBySubscribeId(context.Context, int64) ([]*user.Subscribe, error) { return nil, nil } func (s subscribeUserModelStub) UpdateUserSubscribeWithTraffic(context.Context, int64, int64, int64, bool, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) QueryResisterUserTotalByDate(context.Context, time.Time) (int64, error) { return 0, nil } func (s subscribeUserModelStub) QueryResisterUserTotalByMonthly(context.Context, time.Time) (int64, error) { return 0, nil } func (s subscribeUserModelStub) QueryResisterUserTotal(context.Context) (int64, error) { return 0, nil } func (s subscribeUserModelStub) QueryAdminUsers(context.Context) ([]*user.User, error) { return nil, nil } func (s subscribeUserModelStub) UpdateUserCache(context.Context, *user.User) error { return nil } func (s subscribeUserModelStub) UpdateUserSubscribeCache(context.Context, *user.Subscribe) error { return nil } func (s subscribeUserModelStub) QueryActiveSubscriptions(context.Context, ...int64) (map[int64]int64, error) { return nil, nil } func (s subscribeUserModelStub) FindUserAuthMethods(context.Context, int64) ([]*user.AuthMethods, error) { return nil, nil } func (s subscribeUserModelStub) InsertUserAuthMethods(context.Context, *user.AuthMethods, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) UpdateUserAuthMethods(context.Context, *user.AuthMethods, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) DeleteUserAuthMethods(context.Context, int64, string, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) FindUserAuthMethodByOpenID(context.Context, string, string) (*user.AuthMethods, error) { return nil, nil } func (s subscribeUserModelStub) FindUserAuthMethodByUserId(context.Context, string, int64) (*user.AuthMethods, error) { return nil, nil } func (s subscribeUserModelStub) FindUserAuthMethodByPlatform(context.Context, int64, string) (*user.AuthMethods, error) { return nil, nil } func (s subscribeUserModelStub) FindOneByEmail(context.Context, string) (*user.User, error) { return nil, nil } func (s subscribeUserModelStub) FindOneDevice(context.Context, int64) (*user.Device, error) { return nil, nil } func (s subscribeUserModelStub) QueryDeviceList(context.Context, int64) ([]*user.Device, int64, error) { return nil, 0, nil } func (s subscribeUserModelStub) QueryDeviceListByUserIds(context.Context, []int64) ([]*user.Device, int64, error) { return nil, 0, nil } func (s subscribeUserModelStub) QueryDevicePageList(context.Context, int64, int64, int, int) ([]*user.Device, int64, error) { return nil, 0, nil } func (s subscribeUserModelStub) UpdateDevice(context.Context, *user.Device, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) FindOneDeviceByIdentifier(context.Context, string) (*user.Device, error) { return nil, nil } func (s subscribeUserModelStub) DeleteDevice(context.Context, int64, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) InsertDevice(context.Context, *user.Device, ...*gorm.DB) error { return nil } func (s subscribeUserModelStub) ClearSubscribeCache(context.Context, ...*user.Subscribe) error { return nil } func (s subscribeUserModelStub) ClearUserCache(context.Context, ...*user.User) error { return nil } func (s subscribeUserModelStub) ClearDeviceCache(context.Context, ...*user.Device) error { return nil } func (s subscribeUserModelStub) QueryDailyUserStatisticsList(context.Context, time.Time) ([]user.UserStatisticsWithDate, error) { return nil, nil } func (s subscribeUserModelStub) QueryMonthlyUserStatisticsList(context.Context, time.Time) ([]user.UserStatisticsWithDate, error) { return nil, nil } func (s subscribeUserModelStub) FindActiveSubscribe(context.Context, int64) (*user.Subscribe, error) { return nil, nil } func (s subscribeUserModelStub) FindActiveSubscribesByUserIds(context.Context, []int64) (map[int64]*user.UserStatusInfo, error) { return nil, nil }