api_key_group_visibility_test.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //go:build unit
  2. package service
  3. import (
  4. "context"
  5. "errors"
  6. "testing"
  7. "time"
  8. "github.com/stretchr/testify/require"
  9. )
  10. type visibilityUserRepo struct {
  11. UserRepository
  12. user *User
  13. err error
  14. }
  15. func (r *visibilityUserRepo) GetByID(context.Context, int64) (*User, error) { return r.user, r.err }
  16. type visibilitySubRepo struct {
  17. UserSubscriptionRepository
  18. subscriptions []UserSubscription
  19. err error
  20. calls int
  21. }
  22. func (r *visibilitySubRepo) ListActiveByUserID(_ context.Context, userID int64) ([]UserSubscription, error) {
  23. r.calls++
  24. // Match the repository's active-status and expiry predicates.
  25. active := make([]UserSubscription, 0)
  26. for _, sub := range r.subscriptions {
  27. if sub.UserID == userID && sub.IsActive() {
  28. active = append(active, sub)
  29. }
  30. }
  31. return active, r.err
  32. }
  33. type visibilityGroupRepo struct {
  34. GroupRepository
  35. groups []Group
  36. }
  37. func (r *visibilityGroupRepo) ListActive(context.Context) ([]Group, error) { return r.groups, nil }
  38. func TestGetUserGroupVisibilityIncludesActiveSubscriptions(t *testing.T) {
  39. for _, restricted := range []bool{false, true} {
  40. t.Run(map[bool]string{false: "unrestricted", true: "restricted"}[restricted], func(t *testing.T) {
  41. now := time.Now()
  42. subs := &visibilitySubRepo{subscriptions: []UserSubscription{
  43. {UserID: 1, GroupID: 42, Status: SubscriptionStatusActive, ExpiresAt: now.Add(time.Hour)},
  44. {UserID: 1, GroupID: 43, Status: SubscriptionStatusActive, ExpiresAt: now.Add(-time.Hour)},
  45. {UserID: 1, GroupID: 44, Status: "expired", ExpiresAt: now.Add(time.Hour)},
  46. {UserID: 2, GroupID: 45, Status: SubscriptionStatusActive, ExpiresAt: now.Add(time.Hour)},
  47. }}
  48. svc := &APIKeyService{
  49. userRepo: &visibilityUserRepo{user: &User{ID: 1, AllowedGroups: []int64{7}, RestrictPublicGroups: restricted}},
  50. userSubRepo: subs,
  51. groupRepo: &visibilityGroupRepo{groups: []Group{{ID: 42, IsExclusive: true, SubscriptionType: "subscription"}}},
  52. }
  53. available, err := svc.GetAvailableGroups(context.Background(), 1)
  54. require.NoError(t, err)
  55. require.Len(t, available, 1)
  56. visible, restrict, err := svc.GetUserGroupVisibility(context.Background(), 1)
  57. require.NoError(t, err)
  58. require.Equal(t, restricted, restrict)
  59. require.Equal(t, map[int64]struct{}{7: {}, 42: {}}, visible)
  60. require.Contains(t, visible, available[0].ID, "a subscribed group that can be bound must be visible")
  61. })
  62. }
  63. }
  64. func TestGetUserGroupVisibilityEmptyAndErrors(t *testing.T) {
  65. failure := errors.New("repository unavailable")
  66. for _, tc := range []struct {
  67. name string
  68. userErr, subErr error
  69. }{
  70. {name: "empty"}, {name: "user failure", userErr: failure}, {name: "subscription failure", subErr: failure},
  71. } {
  72. t.Run(tc.name, func(t *testing.T) {
  73. subs := &visibilitySubRepo{err: tc.subErr}
  74. svc := &APIKeyService{userRepo: &visibilityUserRepo{user: &User{ID: 1}, err: tc.userErr}, userSubRepo: subs}
  75. got, _, err := svc.GetUserGroupVisibility(context.Background(), 1)
  76. if tc.userErr != nil || tc.subErr != nil {
  77. require.ErrorIs(t, err, failure)
  78. require.Nil(t, got, "repository failures must not become anonymous visibility")
  79. } else {
  80. require.NoError(t, err)
  81. require.NotNil(t, got, "an empty logged-in user must not become anonymous")
  82. require.Empty(t, got)
  83. }
  84. if tc.userErr != nil {
  85. require.Zero(t, subs.calls)
  86. }
  87. })
  88. }
  89. }