owpengram-server/internal/rpc/account_freeze_worker_test.go
2026-09-01 12:06:31 +03:00

136 lines
4.6 KiB
Go

package rpc
import (
"context"
"errors"
"testing"
"time"
"github.com/iamxvbaba/td/clock"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap/zaptest"
"telesrv/internal/domain"
)
func TestAccountFreezeNotificationPushesCurrentViewerProjection(t *testing.T) {
const (
viewerID = int64(1001)
frozenID = int64(1002)
)
sessions := &captureSessions{}
freezeSvc := &freezeWorkerService{}
users := &freezeWorkerUsers{user: domain.User{
ID: frozenID,
FirstName: "Frozen",
RestrictionReasons: domain.AccountFrozenRestrictionReasons(),
}}
r := New(Config{}, Deps{
AccountFreezeNotifications: freezeSvc,
Users: users,
Sessions: sessions,
}, zaptest.NewLogger(t), clock.System)
r.dispatchAccountFreezeNotification(context.Background(), freezeSvc, domain.AccountFreezeNotification{
ID: 7, TargetUserID: viewerID, FrozenUserID: frozenID, Version: 4, Frozen: true,
})
if len(freezeSvc.completed) != 1 || freezeSvc.completed[0] != [2]int64{7, 4} {
t.Fatalf("completed = %v, want [[7 4]]", freezeSvc.completed)
}
if got := sessions.pushedUserIDs(); len(got) != 1 || got[0] != viewerID {
t.Fatalf("pushed user IDs = %v, want [%d]", got, viewerID)
}
updates, ok := sessions.lastUserPush().(*tg.Updates)
if !ok || len(updates.Updates) != 1 || len(updates.Users) != 1 {
t.Fatalf("push = %#v, want updateUser plus projected user", sessions.lastUserPush())
}
if update, ok := updates.Updates[0].(*tg.UpdateUser); !ok || update.UserID != frozenID {
t.Fatalf("update = %#v, want updateUser(%d)", updates.Updates[0], frozenID)
}
projected, ok := updates.Users[0].(*tg.User)
if !ok || !projected.Restricted {
t.Fatalf("projected user = %#v, want restricted user", updates.Users[0])
}
reasons, ok := projected.GetRestrictionReason()
if !ok || len(reasons) != 1 || reasons[0].Reason != "frozen" {
t.Fatalf("projected restriction = %+v ok=%v", reasons, ok)
}
}
func TestAccountFreezeNotificationLoadsCurrentStateAndRetriesLoadFailure(t *testing.T) {
const (
viewerID = int64(2001)
frozenID = int64(2002)
)
sessions := &captureSessions{}
freezeSvc := &freezeWorkerService{}
users := &freezeWorkerUsers{err: errors.New("projection unavailable")}
r := New(Config{}, Deps{
AccountFreezeNotifications: freezeSvc,
Users: users,
Sessions: sessions,
}, zaptest.NewLogger(t), clock.System)
notification := domain.AccountFreezeNotification{
ID: 8, TargetUserID: viewerID, FrozenUserID: frozenID, Version: 5, Frozen: true,
}
r.dispatchAccountFreezeNotification(context.Background(), freezeSvc, notification)
if len(freezeSvc.completed) != 0 || len(sessions.pushedUserIDs()) != 0 {
t.Fatalf("failed load completed=%v pushes=%v, want retry without push", freezeSvc.completed, sessions.pushedUserIDs())
}
// The queued payload may say frozen, but delivery must hydrate the latest
// viewer projection so a newer unfreeze can never be overwritten by stale work.
users.err = nil
users.user = domain.User{ID: frozenID, FirstName: "Active"}
r.dispatchAccountFreezeNotification(context.Background(), freezeSvc, notification)
updates, ok := sessions.lastUserPush().(*tg.Updates)
if !ok || len(updates.Users) != 1 {
t.Fatalf("push = %#v", sessions.lastUserPush())
}
projected, ok := updates.Users[0].(*tg.User)
if !ok || projected.Restricted {
t.Fatalf("latest projected user = %#v, want unrestricted", updates.Users[0])
}
if len(freezeSvc.completed) != 1 || freezeSvc.completed[0] != [2]int64{8, 5} {
t.Fatalf("completed = %v, want [[8 5]]", freezeSvc.completed)
}
}
type freezeWorkerService struct {
completed [][2]int64
}
func (*freezeWorkerService) AccountFreeze(context.Context, int64) (domain.AccountFreeze, bool, error) {
return domain.AccountFreeze{}, false, nil
}
func (*freezeWorkerService) ClaimAccountFreezeNotifications(context.Context, time.Time, int, time.Duration) ([]domain.AccountFreezeNotification, error) {
return nil, nil
}
func (s *freezeWorkerService) CompleteAccountFreezeNotification(_ context.Context, id, version int64, _ time.Time) error {
s.completed = append(s.completed, [2]int64{id, version})
return nil
}
type freezeWorkerUsers struct {
user domain.User
err error
}
func (s *freezeWorkerUsers) Self(context.Context, int64) (domain.User, error) {
return s.user, s.err
}
func (s *freezeWorkerUsers) ByID(context.Context, int64, int64) (domain.User, bool, error) {
return s.user, s.err == nil, s.err
}
func (s *freezeWorkerUsers) ByIDs(context.Context, int64, []int64) ([]domain.User, error) {
if s.err != nil {
return nil, s.err
}
return []domain.User{s.user}, nil
}