admin_service_list_users_test.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "errors"
  6. "testing"
  7. "time"
  8. "github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
  9. "github.com/stretchr/testify/require"
  10. )
  11. type userRepoStubForListUsers struct {
  12. userRepoStub
  13. users []User
  14. err error
  15. listWithFiltersParams pagination.PaginationParams
  16. lastUsedByUserID map[int64]*time.Time
  17. lastUsedErr error
  18. }
  19. func (s *userRepoStubForListUsers) ListWithFilters(_ context.Context, params pagination.PaginationParams, _ UserListFilters) ([]User, *pagination.PaginationResult, error) {
  20. s.listWithFiltersParams = params
  21. if s.err != nil {
  22. return nil, nil, s.err
  23. }
  24. out := make([]User, len(s.users))
  25. copy(out, s.users)
  26. return out, &pagination.PaginationResult{
  27. Total: int64(len(out)),
  28. Page: params.Page,
  29. PageSize: params.PageSize,
  30. }, nil
  31. }
  32. func (s *userRepoStubForListUsers) GetLatestUsedAtByUserIDs(_ context.Context, userIDs []int64) (map[int64]*time.Time, error) {
  33. if s.lastUsedErr != nil {
  34. return nil, s.lastUsedErr
  35. }
  36. result := make(map[int64]*time.Time, len(userIDs))
  37. for _, userID := range userIDs {
  38. if ts, ok := s.lastUsedByUserID[userID]; ok {
  39. result[userID] = ts
  40. }
  41. }
  42. return result, nil
  43. }
  44. func (s *userRepoStubForListUsers) GetLatestUsedAtByUserID(_ context.Context, userID int64) (*time.Time, error) {
  45. if s.lastUsedErr != nil {
  46. return nil, s.lastUsedErr
  47. }
  48. return s.lastUsedByUserID[userID], nil
  49. }
  50. type userGroupRateRepoStubForListUsers struct {
  51. batchCalls int
  52. singleCall []int64
  53. batchErr error
  54. batchData map[int64]map[int64]float64
  55. singleErr map[int64]error
  56. singleData map[int64]map[int64]float64
  57. }
  58. func (s *userGroupRateRepoStubForListUsers) GetByUserIDs(_ context.Context, _ []int64) (map[int64]map[int64]float64, error) {
  59. s.batchCalls++
  60. if s.batchErr != nil {
  61. return nil, s.batchErr
  62. }
  63. return s.batchData, nil
  64. }
  65. func (s *userGroupRateRepoStubForListUsers) GetByUserID(_ context.Context, userID int64) (map[int64]float64, error) {
  66. s.singleCall = append(s.singleCall, userID)
  67. if err, ok := s.singleErr[userID]; ok {
  68. return nil, err
  69. }
  70. if rates, ok := s.singleData[userID]; ok {
  71. return rates, nil
  72. }
  73. return map[int64]float64{}, nil
  74. }
  75. func (s *userGroupRateRepoStubForListUsers) GetByUserAndGroup(_ context.Context, userID, groupID int64) (*float64, error) {
  76. panic("unexpected GetByUserAndGroup call")
  77. }
  78. func (s *userGroupRateRepoStubForListUsers) GetRPMOverrideByUserAndGroup(_ context.Context, _, _ int64) (*int, error) {
  79. panic("unexpected GetRPMOverrideByUserAndGroup call")
  80. }
  81. func (s *userGroupRateRepoStubForListUsers) SyncUserGroupRates(_ context.Context, userID int64, rates map[int64]*float64) error {
  82. panic("unexpected SyncUserGroupRates call")
  83. }
  84. func (s *userGroupRateRepoStubForListUsers) GetByGroupID(_ context.Context, _ int64) ([]UserGroupRateEntry, error) {
  85. panic("unexpected GetByGroupID call")
  86. }
  87. func (s *userGroupRateRepoStubForListUsers) SyncGroupRateMultipliers(_ context.Context, _ int64, _ []GroupRateMultiplierInput) error {
  88. panic("unexpected SyncGroupRateMultipliers call")
  89. }
  90. func (s *userGroupRateRepoStubForListUsers) SyncGroupRPMOverrides(_ context.Context, _ int64, _ []GroupRPMOverrideInput) error {
  91. panic("unexpected SyncGroupRPMOverrides call")
  92. }
  93. func (s *userGroupRateRepoStubForListUsers) ClearGroupRPMOverrides(_ context.Context, _ int64) error {
  94. panic("unexpected ClearGroupRPMOverrides call")
  95. }
  96. func (s *userGroupRateRepoStubForListUsers) DeleteByGroupID(_ context.Context, _ int64) error {
  97. panic("unexpected DeleteByGroupID call")
  98. }
  99. func (s *userGroupRateRepoStubForListUsers) DeleteByUserID(_ context.Context, userID int64) error {
  100. panic("unexpected DeleteByUserID call")
  101. }
  102. func TestAdminService_ListUsers_BatchRateFallbackToSingle(t *testing.T) {
  103. userRepo := &userRepoStubForListUsers{
  104. users: []User{
  105. {ID: 101, Username: "u1"},
  106. {ID: 202, Username: "u2"},
  107. },
  108. }
  109. rateRepo := &userGroupRateRepoStubForListUsers{
  110. batchErr: errors.New("batch unavailable"),
  111. singleData: map[int64]map[int64]float64{
  112. 101: {11: 1.1},
  113. 202: {22: 2.2},
  114. },
  115. }
  116. svc := &adminServiceImpl{
  117. userRepo: userRepo,
  118. userGroupRateRepo: rateRepo,
  119. }
  120. users, total, err := svc.ListUsers(context.Background(), 1, 20, UserListFilters{}, "", "")
  121. require.NoError(t, err)
  122. require.Equal(t, int64(2), total)
  123. require.Len(t, users, 2)
  124. require.Equal(t, 1, rateRepo.batchCalls)
  125. require.ElementsMatch(t, []int64{101, 202}, rateRepo.singleCall)
  126. require.Equal(t, 1.1, users[0].GroupRates[11])
  127. require.Equal(t, 2.2, users[1].GroupRates[22])
  128. }
  129. func TestAdminService_ListUsers_PassesSortParams(t *testing.T) {
  130. userRepo := &userRepoStubForListUsers{
  131. users: []User{{ID: 1, Email: "a@example.com"}},
  132. }
  133. svc := &adminServiceImpl{userRepo: userRepo}
  134. _, _, err := svc.ListUsers(context.Background(), 2, 50, UserListFilters{}, "email", "ASC")
  135. require.NoError(t, err)
  136. require.Equal(t, pagination.PaginationParams{
  137. Page: 2,
  138. PageSize: 50,
  139. SortBy: "email",
  140. SortOrder: "ASC",
  141. }, userRepo.listWithFiltersParams)
  142. }
  143. func TestAdminService_ListUsers_PopulatesLastUsedAt(t *testing.T) {
  144. lastUsed := time.Now().UTC().Add(-30 * time.Minute).Truncate(time.Second)
  145. userRepo := &userRepoStubForListUsers{
  146. users: []User{{ID: 101, Email: "u@example.com"}},
  147. lastUsedByUserID: map[int64]*time.Time{
  148. 101: &lastUsed,
  149. },
  150. }
  151. svc := &adminServiceImpl{userRepo: userRepo}
  152. users, total, err := svc.ListUsers(context.Background(), 1, 20, UserListFilters{}, "", "")
  153. require.NoError(t, err)
  154. require.Equal(t, int64(1), total)
  155. require.Len(t, users, 1)
  156. require.NotNil(t, users[0].LastUsedAt)
  157. require.WithinDuration(t, lastUsed, *users[0].LastUsedAt, time.Second)
  158. }