admin_service_group_rate_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "errors"
  6. "net/http"
  7. "testing"
  8. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  9. "github.com/stretchr/testify/require"
  10. )
  11. // userGroupRateRepoStubForGroupRate implements UserGroupRateRepository for group rate tests.
  12. type userGroupRateRepoStubForGroupRate struct {
  13. getByGroupIDData map[int64][]UserGroupRateEntry
  14. getByGroupIDErr error
  15. deletedGroupIDs []int64
  16. deleteByGroupErr error
  17. syncedGroupID int64
  18. syncedEntries []GroupRateMultiplierInput
  19. syncGroupErr error
  20. rpmSyncedGroupID int64
  21. rpmSyncedEntries []GroupRPMOverrideInput
  22. rpmSyncErr error
  23. }
  24. func (s *userGroupRateRepoStubForGroupRate) GetByUserID(_ context.Context, _ int64) (map[int64]float64, error) {
  25. panic("unexpected GetByUserID call")
  26. }
  27. func (s *userGroupRateRepoStubForGroupRate) GetByUserAndGroup(_ context.Context, _, _ int64) (*float64, error) {
  28. panic("unexpected GetByUserAndGroup call")
  29. }
  30. func (s *userGroupRateRepoStubForGroupRate) GetRPMOverrideByUserAndGroup(_ context.Context, _, _ int64) (*int, error) {
  31. panic("unexpected GetRPMOverrideByUserAndGroup call")
  32. }
  33. func (s *userGroupRateRepoStubForGroupRate) GetByGroupID(_ context.Context, groupID int64) ([]UserGroupRateEntry, error) {
  34. if s.getByGroupIDErr != nil {
  35. return nil, s.getByGroupIDErr
  36. }
  37. return s.getByGroupIDData[groupID], nil
  38. }
  39. func (s *userGroupRateRepoStubForGroupRate) SyncUserGroupRates(_ context.Context, _ int64, _ map[int64]*float64) error {
  40. panic("unexpected SyncUserGroupRates call")
  41. }
  42. func (s *userGroupRateRepoStubForGroupRate) SyncGroupRateMultipliers(_ context.Context, groupID int64, entries []GroupRateMultiplierInput) error {
  43. s.syncedGroupID = groupID
  44. s.syncedEntries = entries
  45. return s.syncGroupErr
  46. }
  47. func (s *userGroupRateRepoStubForGroupRate) SyncGroupRPMOverrides(_ context.Context, groupID int64, entries []GroupRPMOverrideInput) error {
  48. s.rpmSyncedGroupID = groupID
  49. s.rpmSyncedEntries = entries
  50. return s.rpmSyncErr
  51. }
  52. func (s *userGroupRateRepoStubForGroupRate) ClearGroupRPMOverrides(_ context.Context, _ int64) error {
  53. panic("unexpected ClearGroupRPMOverrides call")
  54. }
  55. func (s *userGroupRateRepoStubForGroupRate) DeleteByGroupID(_ context.Context, groupID int64) error {
  56. s.deletedGroupIDs = append(s.deletedGroupIDs, groupID)
  57. return s.deleteByGroupErr
  58. }
  59. func (s *userGroupRateRepoStubForGroupRate) DeleteByUserID(_ context.Context, _ int64) error {
  60. panic("unexpected DeleteByUserID call")
  61. }
  62. func TestAdminService_GetGroupRateMultipliers(t *testing.T) {
  63. t.Run("returns entries for group", func(t *testing.T) {
  64. repo := &userGroupRateRepoStubForGroupRate{
  65. getByGroupIDData: map[int64][]UserGroupRateEntry{
  66. 10: {
  67. {UserID: 1, UserName: "alice", UserEmail: "alice@test.com", RateMultiplier: ptrFloat(1.5)},
  68. {UserID: 2, UserName: "bob", UserEmail: "bob@test.com", RateMultiplier: ptrFloat(0.8)},
  69. },
  70. },
  71. }
  72. svc := &adminServiceImpl{userGroupRateRepo: repo}
  73. entries, err := svc.GetGroupRateMultipliers(context.Background(), 10)
  74. require.NoError(t, err)
  75. require.Len(t, entries, 2)
  76. require.Equal(t, int64(1), entries[0].UserID)
  77. require.Equal(t, "alice", entries[0].UserName)
  78. require.NotNil(t, entries[0].RateMultiplier)
  79. require.Equal(t, 1.5, *entries[0].RateMultiplier)
  80. require.Equal(t, int64(2), entries[1].UserID)
  81. require.NotNil(t, entries[1].RateMultiplier)
  82. require.Equal(t, 0.8, *entries[1].RateMultiplier)
  83. })
  84. t.Run("returns nil when repo is nil", func(t *testing.T) {
  85. svc := &adminServiceImpl{userGroupRateRepo: nil}
  86. entries, err := svc.GetGroupRateMultipliers(context.Background(), 10)
  87. require.NoError(t, err)
  88. require.Nil(t, entries)
  89. })
  90. t.Run("returns empty slice for group with no entries", func(t *testing.T) {
  91. repo := &userGroupRateRepoStubForGroupRate{
  92. getByGroupIDData: map[int64][]UserGroupRateEntry{},
  93. }
  94. svc := &adminServiceImpl{userGroupRateRepo: repo}
  95. entries, err := svc.GetGroupRateMultipliers(context.Background(), 99)
  96. require.NoError(t, err)
  97. require.Nil(t, entries)
  98. })
  99. t.Run("propagates repo error", func(t *testing.T) {
  100. repo := &userGroupRateRepoStubForGroupRate{
  101. getByGroupIDErr: errors.New("db error"),
  102. }
  103. svc := &adminServiceImpl{userGroupRateRepo: repo}
  104. _, err := svc.GetGroupRateMultipliers(context.Background(), 10)
  105. require.Error(t, err)
  106. require.Contains(t, err.Error(), "db error")
  107. })
  108. }
  109. func TestAdminService_ClearGroupRateMultipliers(t *testing.T) {
  110. t.Run("deletes by group ID", func(t *testing.T) {
  111. repo := &userGroupRateRepoStubForGroupRate{}
  112. svc := &adminServiceImpl{userGroupRateRepo: repo}
  113. err := svc.ClearGroupRateMultipliers(context.Background(), 42)
  114. require.NoError(t, err)
  115. require.Equal(t, []int64{42}, repo.deletedGroupIDs)
  116. })
  117. t.Run("returns nil when repo is nil", func(t *testing.T) {
  118. svc := &adminServiceImpl{userGroupRateRepo: nil}
  119. err := svc.ClearGroupRateMultipliers(context.Background(), 42)
  120. require.NoError(t, err)
  121. })
  122. t.Run("propagates repo error", func(t *testing.T) {
  123. repo := &userGroupRateRepoStubForGroupRate{
  124. deleteByGroupErr: errors.New("delete failed"),
  125. }
  126. svc := &adminServiceImpl{userGroupRateRepo: repo}
  127. err := svc.ClearGroupRateMultipliers(context.Background(), 42)
  128. require.Error(t, err)
  129. require.Contains(t, err.Error(), "delete failed")
  130. })
  131. }
  132. func TestAdminService_BatchSetGroupRateMultipliers(t *testing.T) {
  133. t.Run("syncs entries to repo", func(t *testing.T) {
  134. repo := &userGroupRateRepoStubForGroupRate{}
  135. svc := &adminServiceImpl{userGroupRateRepo: repo}
  136. entries := []GroupRateMultiplierInput{
  137. {UserID: 1, RateMultiplier: 1.5},
  138. {UserID: 2, RateMultiplier: 0.8},
  139. }
  140. err := svc.BatchSetGroupRateMultipliers(context.Background(), 10, entries)
  141. require.NoError(t, err)
  142. require.Equal(t, int64(10), repo.syncedGroupID)
  143. require.Equal(t, entries, repo.syncedEntries)
  144. })
  145. t.Run("returns nil when repo is nil", func(t *testing.T) {
  146. svc := &adminServiceImpl{userGroupRateRepo: nil}
  147. err := svc.BatchSetGroupRateMultipliers(context.Background(), 10, nil)
  148. require.NoError(t, err)
  149. })
  150. t.Run("propagates repo error", func(t *testing.T) {
  151. repo := &userGroupRateRepoStubForGroupRate{
  152. syncGroupErr: errors.New("sync failed"),
  153. }
  154. svc := &adminServiceImpl{userGroupRateRepo: repo}
  155. err := svc.BatchSetGroupRateMultipliers(context.Background(), 10, []GroupRateMultiplierInput{
  156. {UserID: 1, RateMultiplier: 1.0},
  157. })
  158. require.Error(t, err)
  159. require.Contains(t, err.Error(), "sync failed")
  160. })
  161. }
  162. func TestAdminService_BatchSetGroupRPMOverrides(t *testing.T) {
  163. t.Run("syncs entries to repo", func(t *testing.T) {
  164. repo := &userGroupRateRepoStubForGroupRate{}
  165. svc := &adminServiceImpl{userGroupRateRepo: repo}
  166. override := 20
  167. entries := []GroupRPMOverrideInput{{UserID: 2, RPMOverride: &override}}
  168. err := svc.BatchSetGroupRPMOverrides(context.Background(), 10, entries)
  169. require.NoError(t, err)
  170. require.Equal(t, int64(10), repo.rpmSyncedGroupID)
  171. require.Equal(t, entries, repo.rpmSyncedEntries)
  172. })
  173. t.Run("rejects negative override as bad request", func(t *testing.T) {
  174. repo := &userGroupRateRepoStubForGroupRate{}
  175. svc := &adminServiceImpl{userGroupRateRepo: repo}
  176. negative := -1
  177. err := svc.BatchSetGroupRPMOverrides(context.Background(), 10, []GroupRPMOverrideInput{
  178. {UserID: 2, RPMOverride: &negative},
  179. })
  180. require.Error(t, err)
  181. require.Equal(t, http.StatusBadRequest, infraerrors.Code(err))
  182. require.Zero(t, repo.rpmSyncedGroupID)
  183. })
  184. }