| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- //go:build unit
- package service
- import (
- "context"
- "testing"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/config"
- "github.com/stretchr/testify/require"
- )
- type revokeCacheUserSubRepoStub struct {
- userSubRepoNoop
- sub *UserSubscription
- deleted bool
- getActiveCalls int
- }
- func (r *revokeCacheUserSubRepoStub) GetByID(_ context.Context, id int64) (*UserSubscription, error) {
- if r.sub == nil || r.sub.ID != id || r.deleted {
- return nil, ErrSubscriptionNotFound
- }
- cp := *r.sub
- return &cp, nil
- }
- func (r *revokeCacheUserSubRepoStub) Delete(_ context.Context, id int64) error {
- if r.sub == nil || r.sub.ID != id || r.deleted {
- return ErrSubscriptionNotFound
- }
- r.deleted = true
- return nil
- }
- func (r *revokeCacheUserSubRepoStub) GetActiveByUserIDAndGroupID(_ context.Context, userID, groupID int64) (*UserSubscription, error) {
- r.getActiveCalls++
- if r.deleted || r.sub == nil || r.sub.UserID != userID || r.sub.GroupID != groupID {
- return nil, ErrSubscriptionNotFound
- }
- cp := *r.sub
- return &cp, nil
- }
- func TestRevokeSubscription_InvalidatesL1CacheSynchronously(t *testing.T) {
- repo := &revokeCacheUserSubRepoStub{
- sub: &UserSubscription{
- ID: 1,
- UserID: 10,
- GroupID: 20,
- Status: SubscriptionStatusActive,
- ExpiresAt: time.Now().Add(time.Hour),
- },
- }
- svc := NewSubscriptionService(groupRepoNoop{}, repo, nil, nil, &config.Config{
- SubscriptionCache: config.SubscriptionCacheConfig{
- L1Size: 16,
- L1TTLSeconds: 60,
- },
- })
- t.Cleanup(svc.Stop)
- _, err := svc.GetActiveSubscription(context.Background(), 10, 20)
- require.NoError(t, err)
- svc.subCacheL1.Wait()
- require.Equal(t, 1, repo.getActiveCalls)
- err = svc.RevokeSubscription(context.Background(), 1)
- require.NoError(t, err)
- _, err = svc.GetActiveSubscription(context.Background(), 10, 20)
- require.ErrorIs(t, err, ErrSubscriptionNotFound)
- require.Equal(t, 2, repo.getActiveCalls, "撤销后应回源确认订阅已不存在,不能命中旧 L1")
- }
- type restoreUserSubRepoStub struct {
- userSubRepoNoop
- sub *UserSubscription
- existsActive bool
- restoreCalls int
- restoredStatus string
- }
- func (r *restoreUserSubRepoStub) GetByIDIncludeDeleted(_ context.Context, id int64) (*UserSubscription, error) {
- if r.sub == nil || r.sub.ID != id {
- return nil, ErrSubscriptionNotFound
- }
- cp := *r.sub
- return &cp, nil
- }
- func (r *restoreUserSubRepoStub) ExistsActiveByUserIDAndGroupID(context.Context, int64, int64) (bool, error) {
- return r.existsActive, nil
- }
- func (r *restoreUserSubRepoStub) Restore(_ context.Context, id int64, restoredStatus string) (*UserSubscription, error) {
- if r.sub == nil || r.sub.ID != id {
- return nil, ErrSubscriptionNotFound
- }
- r.restoreCalls++
- r.restoredStatus = restoredStatus
- cp := *r.sub
- cp.Status = restoredStatus
- cp.DeletedAt = nil
- r.sub = &cp
- return &cp, nil
- }
- func TestRestoreSubscription_ExpiredActiveRestoresAsExpired(t *testing.T) {
- deletedAt := time.Now().Add(-time.Hour)
- repo := &restoreUserSubRepoStub{
- sub: &UserSubscription{
- ID: 1,
- UserID: 10,
- GroupID: 20,
- Status: SubscriptionStatusActive,
- ExpiresAt: time.Now().Add(-time.Minute),
- DeletedAt: &deletedAt,
- },
- }
- svc := NewSubscriptionService(groupRepoNoop{}, repo, nil, nil, nil)
- t.Cleanup(svc.Stop)
- restored, err := svc.RestoreSubscription(context.Background(), 1)
- require.NoError(t, err)
- require.Equal(t, 1, repo.restoreCalls)
- require.Equal(t, SubscriptionStatusExpired, repo.restoredStatus)
- require.Equal(t, SubscriptionStatusExpired, restored.Status)
- require.Nil(t, restored.DeletedAt)
- }
- func TestRestoreSubscription_NotRevokedReturnsConflict(t *testing.T) {
- repo := &restoreUserSubRepoStub{
- sub: &UserSubscription{
- ID: 1,
- UserID: 10,
- GroupID: 20,
- Status: SubscriptionStatusActive,
- ExpiresAt: time.Now().Add(time.Hour),
- },
- }
- svc := NewSubscriptionService(groupRepoNoop{}, repo, nil, nil, nil)
- t.Cleanup(svc.Stop)
- _, err := svc.RestoreSubscription(context.Background(), 1)
- require.ErrorIs(t, err, ErrSubscriptionNotRevoked)
- require.Zero(t, repo.restoreCalls)
- }
- func TestRestoreSubscription_LiveSubscriptionConflict(t *testing.T) {
- deletedAt := time.Now().Add(-time.Hour)
- repo := &restoreUserSubRepoStub{
- existsActive: true,
- sub: &UserSubscription{
- ID: 1,
- UserID: 10,
- GroupID: 20,
- Status: SubscriptionStatusExpired,
- ExpiresAt: time.Now().Add(-time.Hour),
- DeletedAt: &deletedAt,
- },
- }
- svc := NewSubscriptionService(groupRepoNoop{}, repo, nil, nil, nil)
- t.Cleanup(svc.Stop)
- _, err := svc.RestoreSubscription(context.Background(), 1)
- require.ErrorIs(t, err, ErrSubscriptionRestoreConflict)
- require.Zero(t, repo.restoreCalls)
- }
|