fix: keep participant changes out of channel pts

(cherry picked from commit 07b2497664bd108dec84f6cfe43715540faf2688)
This commit is contained in:
A 2026-06-07 20:57:31 +08:00
parent 23a2b2aff7
commit 6fd690a06e
10 changed files with 184 additions and 115 deletions

View file

@ -784,6 +784,7 @@ func TestChannelAdminTitlePinAndInvite(t *testing.T) {
if err != nil {
t.Fatalf("CreateMegagroupFromCreateChat: %v", err)
}
ptsBeforeAdmin := created.Channel.Pts
admin, err := service.EditAdmin(ctx, 1001, domain.EditChannelAdminRequest{
ChannelID: created.Channel.ID,
@ -802,15 +803,18 @@ func TestChannelAdminTitlePinAndInvite(t *testing.T) {
if admin.Participant.Role != domain.ChannelRoleAdmin || !admin.Participant.AdminRights.PinMessages || admin.Channel.AdminsCount != 2 {
t.Fatalf("admin result = %+v, want promoted admin with counts", admin)
}
if admin.Event.Type != domain.ChannelUpdateParticipant || admin.Event.PtsCount != 1 || admin.Event.Participant.UserID != 1002 || admin.Event.Previous.UserID != 1002 {
t.Fatalf("admin participant event = %+v, want durable participant transition", admin.Event)
if admin.Channel.Pts != ptsBeforeAdmin {
t.Fatalf("admin channel pts = %d, want unchanged %d", admin.Channel.Pts, ptsBeforeAdmin)
}
diffAfterAdmin, err := service.GetDifference(ctx, 1002, domain.ChannelDifferenceRequest{ChannelID: created.Channel.ID, Pts: 1, Limit: 10})
if admin.Event.Type != domain.ChannelUpdateParticipant || admin.Event.Pts != 0 || admin.Event.PtsCount != 0 || admin.Event.Participant.UserID != 1002 || admin.Event.Previous.UserID != 1002 {
t.Fatalf("admin participant event = %+v, want transient participant transition", admin.Event)
}
diffAfterAdmin, err := service.GetDifference(ctx, 1002, domain.ChannelDifferenceRequest{ChannelID: created.Channel.ID, Pts: ptsBeforeAdmin, Limit: 10})
if err != nil {
t.Fatalf("GetDifference after admin: %v", err)
}
if len(diffAfterAdmin.OtherUpdates) != 1 || diffAfterAdmin.OtherUpdates[0].Type != domain.ChannelUpdateParticipant {
t.Fatalf("diff after admin = %+v, want participant update in channel difference", diffAfterAdmin)
if len(diffAfterAdmin.OtherUpdates) != 0 || diffAfterAdmin.Pts != ptsBeforeAdmin {
t.Fatalf("diff after admin = %+v, want no durable participant update", diffAfterAdmin)
}
admins, err := service.GetParticipants(ctx, 1001, created.Channel.ID, domain.ChannelParticipantsFilter{Kind: domain.ChannelParticipantsAdmins}, 0, 10)
if err != nil {
@ -995,6 +999,7 @@ func TestChannelBanAndDeletePermissions(t *testing.T) {
if err != nil {
t.Fatalf("CreateMegagroupFromCreateChat: %v", err)
}
ptsBeforeBan := created.Channel.Pts
if _, err := service.DeleteChannel(ctx, 1002, domain.DeleteChannelRequest{ChannelID: created.Channel.ID, Date: 11}); !errors.Is(err, domain.ErrChannelAdminRequired) {
t.Fatalf("member DeleteChannel err = %v, want ErrChannelAdminRequired", err)
}
@ -1013,8 +1018,11 @@ func TestChannelBanAndDeletePermissions(t *testing.T) {
if banned.Participant.Status != domain.ChannelMemberKicked || banned.Channel.ParticipantsCount != 1 || banned.Channel.KickedCount != 1 {
t.Fatalf("banned = %+v, want kicked participant and counts", banned)
}
if banned.Event.Type != domain.ChannelUpdateParticipant || banned.Event.Participant.Status != domain.ChannelMemberKicked || banned.Event.PtsCount != 1 {
t.Fatalf("ban participant event = %+v, want durable kicked transition", banned.Event)
if banned.Channel.Pts != ptsBeforeBan {
t.Fatalf("banned channel pts = %d, want unchanged %d", banned.Channel.Pts, ptsBeforeBan)
}
if banned.Event.Type != domain.ChannelUpdateParticipant || banned.Event.Participant.Status != domain.ChannelMemberKicked || banned.Event.Pts != 0 || banned.Event.PtsCount != 0 {
t.Fatalf("ban participant event = %+v, want transient kicked transition", banned.Event)
}
kicked, err := service.GetParticipants(ctx, 1001, created.Channel.ID, domain.ChannelParticipantsFilter{Kind: domain.ChannelParticipantsKicked}, 0, 10)
if err != nil {
@ -1348,6 +1356,7 @@ func TestChannelDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
if err != nil {
t.Fatalf("CreateMegagroupFromCreateChat: %v", err)
}
ptsFloor := created.Channel.Pts
promoted, err := service.EditAdmin(ctx, 1001, domain.EditChannelAdminRequest{
ChannelID: created.Channel.ID,
MemberID: 1002,
@ -1359,12 +1368,15 @@ func TestChannelDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
if err != nil {
t.Fatalf("EditAdmin: %v", err)
}
if promoted.Event.Pts != 0 || promoted.Channel.Pts != ptsFloor {
t.Fatalf("promoted = %+v, want transient admin event and unchanged pts %d", promoted, ptsFloor)
}
joined, err := service.JoinChannel(ctx, 1003, created.Channel.ID, 12)
if err != nil {
t.Fatalf("JoinChannel: %v", err)
}
if joined.Members[0].AvailableMinPts != promoted.Event.Pts {
t.Fatalf("joined available_min_pts = %d, want pre-join channel pts %d", joined.Members[0].AvailableMinPts, promoted.Event.Pts)
if joined.Members[0].AvailableMinPts != ptsFloor {
t.Fatalf("joined available_min_pts = %d, want pre-join channel pts %d", joined.Members[0].AvailableMinPts, ptsFloor)
}
diff, err := service.GetDifference(ctx, 1003, domain.ChannelDifferenceRequest{ChannelID: created.Channel.ID, Pts: 0, Limit: 100})
if err != nil {
@ -1374,13 +1386,13 @@ func TestChannelDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
t.Fatalf("diff pts = %d, want current channel pts %d", diff.Pts, joined.Channel.Pts)
}
for _, msg := range diff.NewMessages {
if msg.Pts <= promoted.Event.Pts {
t.Fatalf("diff leaks pre-join message %+v at or before available_min_pts %d", msg, promoted.Event.Pts)
if msg.Pts <= ptsFloor {
t.Fatalf("diff leaks pre-join message %+v at or before available_min_pts %d", msg, ptsFloor)
}
}
for _, event := range diff.OtherUpdates {
if event.Pts <= promoted.Event.Pts {
t.Fatalf("diff leaks pre-join event %+v at or before available_min_pts %d", event, promoted.Event.Pts)
if event.Pts <= ptsFloor {
t.Fatalf("diff leaks pre-join event %+v at or before available_min_pts %d", event, ptsFloor)
}
}
}

View file

@ -1861,9 +1861,9 @@ func (r *Router) onChannelsEditAdmin(ctx context.Context, req *tg.ChannelsEditAd
} else {
r.removeOnlineChannelMemberships(res.Channel.ID, res.Participant.UserID)
}
updates := r.channelParticipantUpdates(ctx, userID, userID, res.Channel, res.Previous, res.Participant, res.Event, res.Date)
updates := r.channelParticipantUpdates(ctx, userID, userID, res.Channel, res.Previous, res.Participant, res.Date)
r.pushChannelUpdates(ctx, userID, res.Channel.ID, res.Recipients, func(viewerUserID int64) *tg.Updates {
return r.channelParticipantUpdates(ctx, viewerUserID, userID, res.Channel, res.Previous, res.Participant, res.Event, res.Date)
return r.channelParticipantUpdates(ctx, viewerUserID, userID, res.Channel, res.Previous, res.Participant, res.Date)
})
return updates, nil
}
@ -1894,9 +1894,9 @@ func (r *Router) onChannelsEditBanned(ctx context.Context, req *tg.ChannelsEditB
if err != nil {
return nil, channelAdminErr(err)
}
updates := r.channelParticipantUpdates(ctx, userID, userID, res.Channel, res.Previous, res.Participant, res.Event, res.Date)
updates := r.channelParticipantUpdates(ctx, userID, userID, res.Channel, res.Previous, res.Participant, res.Date)
r.pushChannelUpdates(ctx, userID, res.Channel.ID, res.Recipients, func(viewerUserID int64) *tg.Updates {
return r.channelParticipantUpdates(ctx, viewerUserID, userID, res.Channel, res.Previous, res.Participant, res.Event, res.Date)
return r.channelParticipantUpdates(ctx, viewerUserID, userID, res.Channel, res.Previous, res.Participant, res.Date)
})
return updates, nil
}
@ -2904,7 +2904,7 @@ func (r *Router) channelTitleUpdates(ctx context.Context, viewerUserID int64, re
}
}
func (r *Router) channelParticipantUpdates(ctx context.Context, viewerUserID, actorUserID int64, channel domain.Channel, previous, participant domain.ChannelMember, event domain.ChannelUpdateEvent, date int) *tg.Updates {
func (r *Router) channelParticipantUpdates(ctx context.Context, viewerUserID, actorUserID int64, channel domain.Channel, previous, participant domain.ChannelMember, date int) *tg.Updates {
update := &tg.UpdateChannelParticipant{
ChannelID: channel.ID,
Date: date,
@ -2920,14 +2920,8 @@ func (r *Router) channelParticipantUpdates(ctx context.Context, viewerUserID, ac
if participant.UserID != 0 {
update.SetNewParticipant(tgChannelParticipantForUpdate(viewerUserID, participant))
}
updates := []tg.UpdateClass{update, &tg.UpdateChannel{ChannelID: channel.ID}}
if event.Pts > 0 {
tooLong := &tg.UpdateChannelTooLong{ChannelID: channel.ID}
tooLong.SetPts(event.Pts)
updates = append(updates, tooLong)
}
return &tg.Updates{
Updates: updates,
Updates: []tg.UpdateClass{update, &tg.UpdateChannel{ChannelID: channel.ID}},
Users: r.tgUsersForIDs(ctx, viewerUserID, []int64{participant.UserID, participant.InviterUserID, previous.UserID, previous.InviterUserID, update.ActorID}),
Chats: []tg.ChatClass{tgChannelChat(viewerUserID, channel, nil)},
Date: int(r.clock.Now().Unix()),

View file

@ -4186,6 +4186,11 @@ func TestChannelAdminPinInviteRPC(t *testing.T) {
t.Fatalf("create chat: %v", err)
}
channel := created.Updates.(*tg.Updates).Chats[0].(*tg.Channel)
createdChannel, err := channelStore.GetChannelByID(ctx, channel.ID)
if err != nil {
t.Fatalf("get created channel: %v", err)
}
initialChannelPts := createdChannel.Pts
selfParticipant, err := r.onChannelsGetParticipant(WithUserID(ctx, friend.ID), &tg.ChannelsGetParticipantRequest{
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
@ -4229,29 +4234,25 @@ func TestChannelAdminPinInviteRPC(t *testing.T) {
if err != nil {
t.Fatalf("edit admin: %v", err)
}
if updates := adminUpdates.(*tg.Updates); len(updates.Updates) < 3 {
t.Fatalf("admin updates empty, want participant update")
if updates := adminUpdates.(*tg.Updates); len(updates.Updates) != 2 {
t.Fatalf("admin updates = %+v, want participant update and channel refresh", updates.Updates)
} else if _, ok := updates.Updates[0].(*tg.UpdateChannelParticipant); !ok {
t.Fatalf("admin update[0] = %T, want updateChannelParticipant", updates.Updates[0])
} else if tooLong, ok := updates.Updates[2].(*tg.UpdateChannelTooLong); !ok {
t.Fatalf("admin update[2] = %T, want updateChannelTooLong", updates.Updates[2])
} else if pts, ok := tooLong.GetPts(); !ok || pts == 0 {
t.Fatalf("admin updateChannelTooLong pts = %d ok=%v, want set pts", pts, ok)
} else if _, ok := updates.Updates[1].(*tg.UpdateChannel); !ok {
t.Fatalf("admin update[1] = %T, want updateChannel", updates.Updates[1])
}
adminDiff, err := r.onUpdatesGetChannelDifference(WithUserID(ctx, friend.ID), &tg.UpdatesGetChannelDifferenceRequest{
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
Filter: &tg.ChannelMessagesFilterEmpty{},
Pts: 1,
Pts: initialChannelPts,
Limit: 10,
})
if err != nil {
t.Fatalf("channel difference after admin: %v", err)
}
adminFullDiff, ok := adminDiff.(*tg.UpdatesChannelDifference)
if !ok || len(adminFullDiff.OtherUpdates) == 0 {
t.Fatalf("admin diff = %T %+v, want participant other update", adminDiff, adminDiff)
} else if _, ok := adminFullDiff.OtherUpdates[0].(*tg.UpdateChannelParticipant); !ok {
t.Fatalf("admin diff update[0] = %T, want updateChannelParticipant", adminFullDiff.OtherUpdates[0])
adminEmptyDiff, ok := adminDiff.(*tg.UpdatesChannelDifferenceEmpty)
if !ok || !adminEmptyDiff.Final || adminEmptyDiff.Pts != initialChannelPts {
t.Fatalf("admin diff = %T %+v, want empty difference at unchanged pts %d", adminDiff, adminDiff, initialChannelPts)
}
admins, err := r.onChannelsGetParticipants(WithUserID(ctx, owner.ID), &tg.ChannelsGetParticipantsRequest{
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},

View file

@ -627,8 +627,7 @@ func (s *ChannelStore) EditChannelAdmin(_ context.Context, req domain.EditChanne
})
s.refreshChannelCountsLocked(req.ChannelID)
channel = s.channels[req.ChannelID]
event := s.appendParticipantEventLocked(channel, req.UserID, previous, member, req.Date)
channel = s.channels[req.ChannelID]
event := transientChannelParticipantEvent(channel.ID, req.UserID, previous, member, req.Date)
if msg, ok := s.findMessageLocked(req.ChannelID, channel.TopMessageID); ok {
s.upsertChannelDialogLocked(member.UserID, channel, msg, false)
}
@ -702,8 +701,7 @@ func (s *ChannelStore) EditChannelBanned(_ context.Context, req domain.EditChann
})
s.refreshChannelCountsLocked(req.ChannelID)
channel = s.channels[req.ChannelID]
event := s.appendParticipantEventLocked(channel, req.UserID, previous, member, req.Date)
channel = s.channels[req.ChannelID]
event := transientChannelParticipantEvent(channel.ID, req.UserID, previous, member, req.Date)
if member.Status == domain.ChannelMemberActive {
if msg, ok := s.findMessageLocked(req.ChannelID, channel.TopMessageID); ok {
s.upsertChannelDialogLocked(member.UserID, channel, msg, false)
@ -5142,25 +5140,6 @@ func (s *ChannelStore) nextChannelPtsNLocked(channelID int64, count int) int {
return s.ptsSeq[channelID]
}
func (s *ChannelStore) appendParticipantEventLocked(channel domain.Channel, actorUserID int64, previous, participant domain.ChannelMember, date int) domain.ChannelUpdateEvent {
pts := s.nextChannelPtsLocked(channel.ID)
channel.Pts = pts
s.channels[channel.ID] = channel
event := domain.ChannelUpdateEvent{
ChannelID: channel.ID,
Type: domain.ChannelUpdateParticipant,
Pts: pts,
PtsCount: 1,
Date: date,
SenderUserID: actorUserID,
UserIDs: uniqueNonZeroInt64s(actorUserID, previous.UserID, previous.InviterUserID, participant.UserID, participant.InviterUserID),
Previous: previous,
Participant: participant,
}
s.events[channel.ID] = append(s.events[channel.ID], event)
return cloneChannelEvent(event)
}
func (s *ChannelStore) appendChannelServiceMessageLocked(channelID, senderUserID int64, date int, action domain.ChannelMessageAction) (domain.ChannelMessage, domain.ChannelUpdateEvent) {
channel := s.channels[channelID]
pts := s.nextChannelPtsLocked(channelID)
@ -5189,6 +5168,18 @@ func (s *ChannelStore) appendChannelServiceMessageLocked(channelID, senderUserID
return msg, event
}
func transientChannelParticipantEvent(channelID, actorUserID int64, previous, participant domain.ChannelMember, date int) domain.ChannelUpdateEvent {
return domain.ChannelUpdateEvent{
ChannelID: channelID,
Type: domain.ChannelUpdateParticipant,
Date: date,
SenderUserID: actorUserID,
UserIDs: uniqueNonZeroInt64s(actorUserID, previous.UserID, previous.InviterUserID, participant.UserID, participant.InviterUserID),
Previous: previous,
Participant: participant,
}
}
func (s *ChannelStore) channelForMemberLocked(userID, channelID int64) (domain.Channel, error) {
channel, _, err := s.channelAndMemberLocked(userID, channelID)
return channel, err

View file

@ -39,6 +39,69 @@ func TestChannelRealtimeRecipientsAreCapped(t *testing.T) {
}
}
func TestChannelAdminAndBanDoNotAdvanceChannelPts(t *testing.T) {
ctx := context.Background()
store := NewChannelStore()
created, err := store.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: 1,
Title: "participant state no pts",
Megagroup: true,
MemberUserIDs: []int64{2},
Date: 1_700_000_120,
})
if err != nil {
t.Fatalf("create channel: %v", err)
}
channelID := created.Channel.ID
ptsFloor := created.Channel.Pts
promoted, err := store.EditChannelAdmin(ctx, domain.EditChannelAdminRequest{
UserID: 1,
ChannelID: channelID,
MemberID: 2,
AdminRights: domain.ChannelAdminRights{
InviteUsers: true,
},
Date: 1_700_000_121,
})
if err != nil {
t.Fatalf("edit admin: %v", err)
}
if promoted.Event.Pts != 0 || promoted.Event.PtsCount != 0 || promoted.Channel.Pts != ptsFloor {
t.Fatalf("edit admin pts = event(%d,%d) channel %d, want unchanged %d", promoted.Event.Pts, promoted.Event.PtsCount, promoted.Channel.Pts, ptsFloor)
}
banned, err := store.EditChannelBanned(ctx, domain.EditChannelBannedRequest{
UserID: 1,
ChannelID: channelID,
Participant: domain.Peer{Type: domain.PeerTypeUser, ID: 2},
BannedRights: domain.ChannelBannedRights{
ViewMessages: true,
UntilDate: 1_700_001_121,
},
Date: 1_700_000_122,
})
if err != nil {
t.Fatalf("edit banned: %v", err)
}
if banned.Event.Pts != 0 || banned.Event.PtsCount != 0 || banned.Channel.Pts != ptsFloor {
t.Fatalf("edit banned pts = event(%d,%d) channel %d, want unchanged %d", banned.Event.Pts, banned.Event.PtsCount, banned.Channel.Pts, ptsFloor)
}
diff, err := store.ListChannelDifference(ctx, domain.ChannelDifferenceRequest{
UserID: 1,
ChannelID: channelID,
Pts: ptsFloor,
Limit: 10,
})
if err != nil {
t.Fatalf("list difference: %v", err)
}
if len(diff.Events) != 0 || diff.Pts != ptsFloor {
t.Fatalf("difference after participant state change = %+v, want no durable events at pts %d", diff, ptsFloor)
}
}
func TestPendingJoinRequestsSummaryAndInviteAdmins(t *testing.T) {
ctx := context.Background()
store := NewChannelStore()

View file

@ -798,11 +798,9 @@ func (s *ChannelStore) EditChannelAdmin(ctx context.Context, req domain.EditChan
return domain.EditChannelAdminResult{}, fmt.Errorf("begin edit channel admin: %w", err)
}
committed := false
var reserved []reservedChannelPts
defer func() {
if !committed {
_ = tx.Rollback(ctx)
s.recordChannelPtsGaps(ctx, reserved, req.Date)
}
}()
channel, actor, err := s.getChannelForMember(ctx, tx, req.UserID, req.ChannelID)
@ -873,10 +871,7 @@ func (s *ChannelStore) EditChannelAdmin(ctx context.Context, req domain.EditChan
if err != nil {
return domain.EditChannelAdminResult{}, err
}
event, channel, err := s.insertParticipantEventTx(ctx, tx, channel, req.UserID, previous, member, req.Date, &reserved)
if err != nil {
return domain.EditChannelAdminResult{}, err
}
event := transientChannelParticipantEvent(channel.ID, req.UserID, previous, member, req.Date)
msg, _ := s.getChannelMessage(ctx, tx, req.ChannelID, channel.TopMessageID)
if err := upsertChannelDialogTx(ctx, tx, member.UserID, channel, msg, member.ReadInboxMaxID, member.ReadOutboxMaxID); err != nil {
return domain.EditChannelAdminResult{}, err
@ -906,11 +901,9 @@ func (s *ChannelStore) EditChannelBanned(ctx context.Context, req domain.EditCha
return domain.EditChannelBannedResult{}, fmt.Errorf("begin edit channel banned: %w", err)
}
committed := false
var reserved []reservedChannelPts
defer func() {
if !committed {
_ = tx.Rollback(ctx)
s.recordChannelPtsGaps(ctx, reserved, req.Date)
}
}()
channel, actor, err := s.getChannelForMember(ctx, tx, req.UserID, req.ChannelID)
@ -979,10 +972,7 @@ func (s *ChannelStore) EditChannelBanned(ctx context.Context, req domain.EditCha
if err != nil {
return domain.EditChannelBannedResult{}, err
}
event, channel, err := s.insertParticipantEventTx(ctx, tx, channel, req.UserID, previous, member, req.Date, &reserved)
if err != nil {
return domain.EditChannelBannedResult{}, err
}
event := transientChannelParticipantEvent(channel.ID, req.UserID, previous, member, req.Date)
if member.Status == domain.ChannelMemberActive {
msg, _ := s.getChannelMessage(ctx, tx, req.ChannelID, channel.TopMessageID)
if err := upsertChannelDialogTx(ctx, tx, member.UserID, channel, msg, member.ReadInboxMaxID, member.ReadOutboxMaxID); err != nil {
@ -8025,31 +8015,16 @@ func (s *ChannelStore) insertServiceMessage(ctx context.Context, tx pgx.Tx, chan
return msg, event, nil
}
func (s *ChannelStore) insertParticipantEventTx(ctx context.Context, tx pgx.Tx, channel domain.Channel, actorUserID int64, previous, participant domain.ChannelMember, date int, reserved *[]reservedChannelPts) (domain.ChannelUpdateEvent, domain.Channel, error) {
pts, err := s.pts.NextChannelPts(ctx, channel.ID)
if err != nil {
return domain.ChannelUpdateEvent{}, channel, fmt.Errorf("allocate channel participant pts: %w", err)
}
reserveChannelPts(reserved, channel.ID, pts, 1)
event := domain.ChannelUpdateEvent{
ChannelID: channel.ID,
func transientChannelParticipantEvent(channelID, actorUserID int64, previous, participant domain.ChannelMember, date int) domain.ChannelUpdateEvent {
return domain.ChannelUpdateEvent{
ChannelID: channelID,
Type: domain.ChannelUpdateParticipant,
Pts: pts,
PtsCount: 1,
Date: date,
SenderUserID: actorUserID,
UserIDs: uniqueNonZeroInt64s(actorUserID, previous.UserID, previous.InviterUserID, participant.UserID, participant.InviterUserID),
Previous: previous,
Participant: participant,
}
if err := insertChannelEventTx(ctx, tx, event); err != nil {
return domain.ChannelUpdateEvent{}, channel, err
}
if _, err := tx.Exec(ctx, `UPDATE channels SET pts = $2, updated_at = now() WHERE id = $1`, channel.ID, pts); err != nil {
return domain.ChannelUpdateEvent{}, channel, fmt.Errorf("update channel participant pts: %w", err)
}
channel.Pts = pts
return event, channel, nil
}
func (s *ChannelStore) deleteChannelMessagesTx(ctx context.Context, tx pgx.Tx, channel domain.Channel, member domain.ChannelMember, ids []int, actorUserID int64, date int, reserved *[]reservedChannelPts) ([]int, domain.ChannelUpdateEvent, domain.Channel, error) {

View file

@ -689,7 +689,8 @@ func TestChannelStoreJoinRejectsKickedMember(t *testing.T) {
t.Fatalf("create channel: %v", err)
}
channelID = created.Channel.ID
if _, err := channels.EditChannelBanned(ctx, domain.EditChannelBannedRequest{
ptsFloor := created.Channel.Pts
banned, err := channels.EditChannelBanned(ctx, domain.EditChannelBannedRequest{
UserID: owner.ID,
ChannelID: channelID,
Participant: domain.Peer{Type: domain.PeerTypeUser, ID: member.ID},
@ -698,9 +699,25 @@ func TestChannelStoreJoinRejectsKickedMember(t *testing.T) {
UntilDate: 1700001300,
},
Date: 1700000306,
}); err != nil {
})
if err != nil {
t.Fatalf("kick member: %v", err)
}
if banned.Event.Pts != 0 || banned.Event.PtsCount != 0 || banned.Channel.Pts != ptsFloor {
t.Fatalf("kick affected channel pts = event(%d,%d) channel %d, want no pts advance from %d", banned.Event.Pts, banned.Event.PtsCount, banned.Channel.Pts, ptsFloor)
}
banDiff, err := channels.ListChannelDifference(ctx, domain.ChannelDifferenceRequest{
UserID: owner.ID,
ChannelID: channelID,
Pts: ptsFloor,
Limit: 10,
})
if err != nil {
t.Fatalf("difference after kick: %v", err)
}
if len(banDiff.Events) != 0 || banDiff.Pts != ptsFloor {
t.Fatalf("difference after kick = %+v, want no durable participant event at pts %d", banDiff, ptsFloor)
}
if _, err := channels.JoinChannel(ctx, channelID, member.ID, 1700000307); !errors.Is(err, domain.ErrChannelUserBanned) {
t.Fatalf("kicked JoinChannel err = %v, want ErrChannelUserBanned", err)
}
@ -1292,6 +1309,7 @@ func TestChannelStoreDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
t.Fatalf("create channel: %v", err)
}
channelID = created.Channel.ID
ptsFloor := created.Channel.Pts
promoted, err := channels.EditChannelAdmin(ctx, domain.EditChannelAdminRequest{
UserID: owner.ID,
ChannelID: channelID,
@ -1304,12 +1322,27 @@ func TestChannelStoreDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
if err != nil {
t.Fatalf("edit admin: %v", err)
}
if promoted.Event.Pts != 0 || promoted.Event.PtsCount != 0 || promoted.Channel.Pts != ptsFloor {
t.Fatalf("promote affected channel pts = event(%d,%d) channel %d, want no pts advance from %d", promoted.Event.Pts, promoted.Event.PtsCount, promoted.Channel.Pts, ptsFloor)
}
adminDiff, err := channels.ListChannelDifference(ctx, domain.ChannelDifferenceRequest{
UserID: member.ID,
ChannelID: channelID,
Pts: ptsFloor,
Limit: 10,
})
if err != nil {
t.Fatalf("difference after promote: %v", err)
}
if len(adminDiff.Events) != 0 || adminDiff.Pts != ptsFloor {
t.Fatalf("difference after promote = %+v, want no durable participant event at pts %d", adminDiff, ptsFloor)
}
joined, err := channels.JoinChannel(ctx, channelID, joiner.ID, 1700000352)
if err != nil {
t.Fatalf("join channel: %v", err)
}
if len(joined.Members) != 1 || joined.Members[0].AvailableMinPts != promoted.Event.Pts {
t.Fatalf("joined members = %+v, want available_min_pts %d", joined.Members, promoted.Event.Pts)
if len(joined.Members) != 1 || joined.Members[0].AvailableMinPts != ptsFloor {
t.Fatalf("joined members = %+v, want available_min_pts %d", joined.Members, ptsFloor)
}
diff, err := channels.ListChannelDifference(ctx, domain.ChannelDifferenceRequest{
UserID: joiner.ID,
@ -1324,13 +1357,13 @@ func TestChannelStoreDifferenceStartsAtMemberAvailableMinPts(t *testing.T) {
t.Fatalf("diff pts = %d, want current channel pts %d", diff.Pts, joined.Channel.Pts)
}
for _, msg := range diff.NewMessages {
if msg.Pts <= promoted.Event.Pts {
t.Fatalf("diff leaks pre-join message %+v at or before available_min_pts %d", msg, promoted.Event.Pts)
if msg.Pts <= ptsFloor {
t.Fatalf("diff leaks pre-join message %+v at or before available_min_pts %d", msg, ptsFloor)
}
}
for _, event := range diff.OtherUpdates {
if event.Pts <= promoted.Event.Pts {
t.Fatalf("diff leaks pre-join event %+v at or before available_min_pts %d", event, promoted.Event.Pts)
if event.Pts <= ptsFloor {
t.Fatalf("diff leaks pre-join event %+v at or before available_min_pts %d", event, ptsFloor)
}
}
}