| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- //go:build unit
- package service
- import (
- "context"
- "errors"
- "testing"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
- "github.com/stretchr/testify/require"
- )
- type userRepoStubForListUsers struct {
- userRepoStub
- users []User
- err error
- listWithFiltersParams pagination.PaginationParams
- lastUsedByUserID map[int64]*time.Time
- lastUsedErr error
- }
- func (s *userRepoStubForListUsers) ListWithFilters(_ context.Context, params pagination.PaginationParams, _ UserListFilters) ([]User, *pagination.PaginationResult, error) {
- s.listWithFiltersParams = params
- if s.err != nil {
- return nil, nil, s.err
- }
- out := make([]User, len(s.users))
- copy(out, s.users)
- return out, &pagination.PaginationResult{
- Total: int64(len(out)),
- Page: params.Page,
- PageSize: params.PageSize,
- }, nil
- }
- func (s *userRepoStubForListUsers) GetLatestUsedAtByUserIDs(_ context.Context, userIDs []int64) (map[int64]*time.Time, error) {
- if s.lastUsedErr != nil {
- return nil, s.lastUsedErr
- }
- result := make(map[int64]*time.Time, len(userIDs))
- for _, userID := range userIDs {
- if ts, ok := s.lastUsedByUserID[userID]; ok {
- result[userID] = ts
- }
- }
- return result, nil
- }
- func (s *userRepoStubForListUsers) GetLatestUsedAtByUserID(_ context.Context, userID int64) (*time.Time, error) {
- if s.lastUsedErr != nil {
- return nil, s.lastUsedErr
- }
- return s.lastUsedByUserID[userID], nil
- }
- type userGroupRateRepoStubForListUsers struct {
- batchCalls int
- singleCall []int64
- batchErr error
- batchData map[int64]map[int64]float64
- singleErr map[int64]error
- singleData map[int64]map[int64]float64
- }
- func (s *userGroupRateRepoStubForListUsers) GetByUserIDs(_ context.Context, _ []int64) (map[int64]map[int64]float64, error) {
- s.batchCalls++
- if s.batchErr != nil {
- return nil, s.batchErr
- }
- return s.batchData, nil
- }
- func (s *userGroupRateRepoStubForListUsers) GetByUserID(_ context.Context, userID int64) (map[int64]float64, error) {
- s.singleCall = append(s.singleCall, userID)
- if err, ok := s.singleErr[userID]; ok {
- return nil, err
- }
- if rates, ok := s.singleData[userID]; ok {
- return rates, nil
- }
- return map[int64]float64{}, nil
- }
- func (s *userGroupRateRepoStubForListUsers) GetByUserAndGroup(_ context.Context, userID, groupID int64) (*float64, error) {
- panic("unexpected GetByUserAndGroup call")
- }
- func (s *userGroupRateRepoStubForListUsers) GetRPMOverrideByUserAndGroup(_ context.Context, _, _ int64) (*int, error) {
- panic("unexpected GetRPMOverrideByUserAndGroup call")
- }
- func (s *userGroupRateRepoStubForListUsers) SyncUserGroupRates(_ context.Context, userID int64, rates map[int64]*float64) error {
- panic("unexpected SyncUserGroupRates call")
- }
- func (s *userGroupRateRepoStubForListUsers) GetByGroupID(_ context.Context, _ int64) ([]UserGroupRateEntry, error) {
- panic("unexpected GetByGroupID call")
- }
- func (s *userGroupRateRepoStubForListUsers) SyncGroupRateMultipliers(_ context.Context, _ int64, _ []GroupRateMultiplierInput) error {
- panic("unexpected SyncGroupRateMultipliers call")
- }
- func (s *userGroupRateRepoStubForListUsers) SyncGroupRPMOverrides(_ context.Context, _ int64, _ []GroupRPMOverrideInput) error {
- panic("unexpected SyncGroupRPMOverrides call")
- }
- func (s *userGroupRateRepoStubForListUsers) ClearGroupRPMOverrides(_ context.Context, _ int64) error {
- panic("unexpected ClearGroupRPMOverrides call")
- }
- func (s *userGroupRateRepoStubForListUsers) DeleteByGroupID(_ context.Context, _ int64) error {
- panic("unexpected DeleteByGroupID call")
- }
- func (s *userGroupRateRepoStubForListUsers) DeleteByUserID(_ context.Context, userID int64) error {
- panic("unexpected DeleteByUserID call")
- }
- func TestAdminService_ListUsers_BatchRateFallbackToSingle(t *testing.T) {
- userRepo := &userRepoStubForListUsers{
- users: []User{
- {ID: 101, Username: "u1"},
- {ID: 202, Username: "u2"},
- },
- }
- rateRepo := &userGroupRateRepoStubForListUsers{
- batchErr: errors.New("batch unavailable"),
- singleData: map[int64]map[int64]float64{
- 101: {11: 1.1},
- 202: {22: 2.2},
- },
- }
- svc := &adminServiceImpl{
- userRepo: userRepo,
- userGroupRateRepo: rateRepo,
- }
- users, total, err := svc.ListUsers(context.Background(), 1, 20, UserListFilters{}, "", "")
- require.NoError(t, err)
- require.Equal(t, int64(2), total)
- require.Len(t, users, 2)
- require.Equal(t, 1, rateRepo.batchCalls)
- require.ElementsMatch(t, []int64{101, 202}, rateRepo.singleCall)
- require.Equal(t, 1.1, users[0].GroupRates[11])
- require.Equal(t, 2.2, users[1].GroupRates[22])
- }
- func TestAdminService_ListUsers_PassesSortParams(t *testing.T) {
- userRepo := &userRepoStubForListUsers{
- users: []User{{ID: 1, Email: "a@example.com"}},
- }
- svc := &adminServiceImpl{userRepo: userRepo}
- _, _, err := svc.ListUsers(context.Background(), 2, 50, UserListFilters{}, "email", "ASC")
- require.NoError(t, err)
- require.Equal(t, pagination.PaginationParams{
- Page: 2,
- PageSize: 50,
- SortBy: "email",
- SortOrder: "ASC",
- }, userRepo.listWithFiltersParams)
- }
- func TestAdminService_ListUsers_PopulatesLastUsedAt(t *testing.T) {
- lastUsed := time.Now().UTC().Add(-30 * time.Minute).Truncate(time.Second)
- userRepo := &userRepoStubForListUsers{
- users: []User{{ID: 101, Email: "u@example.com"}},
- lastUsedByUserID: map[int64]*time.Time{
- 101: &lastUsed,
- },
- }
- svc := &adminServiceImpl{userRepo: userRepo}
- users, total, err := svc.ListUsers(context.Background(), 1, 20, UserListFilters{}, "", "")
- require.NoError(t, err)
- require.Equal(t, int64(1), total)
- require.Len(t, users, 1)
- require.NotNil(t, users[0].LastUsedAt)
- require.WithinDuration(t, lastUsed, *users[0].LastUsedAt, time.Second)
- }
|