Files
team/server/channels/store/storetest/mocks/SharedChannelStore.go
Doug LauderandGitHub 2e82c160f4 MM-67616: Refactor shared channel membership sync to use ChannelMemberHistory (#35619)
* Refactor shared channel membership sync to use ChannelMemberHistory (MM-67616)

Replace the trigger-time membership sync mechanism with a cursor-based
approach using ChannelMemberHistory, aligning membership sync with the
established pattern used by posts and reactions.

Previously, membership changes were built into SyncMsg at trigger time
and sent via a separate TopicChannelMembership code path. This meant
removals were lost if a remote was offline, since ChannelMembers
hard-deletes rows.

Now, membership changes are fetched from ChannelMemberHistory at sync
time using the LastMembersSyncAt cursor, detecting both joins and leaves
reliably. The data flows through the normal syncForRemote pipeline
alongside posts, reactions, and other sync data.

Key changes:
  - Add GetMembershipChanges store method for ChannelMemberHistory
  - Add fetchMembershipsForSync and sendMembershipSyncData to sync pipeline
  - Replace HandleMembershipChange with NotifyMembershipChanged (trigger-only)
  - Remove conflict detection (idempotent add/remove resolves naturally)
  - Remove per-user membership tracking (GetUserChanges, UpdateUserLastMembershipSyncAt)
  - Add MembershipErrors to SyncResponse
  - Keep TopicChannelMembership receiver for one release cycle (backward compat)
2026-03-23 10:12:17 -04:00

780 lines
20 KiB
Go

// Code generated by mockery v2.53.4. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import (
model "github.com/mattermost/mattermost/server/public/model"
mock "github.com/stretchr/testify/mock"
)
// SharedChannelStore is an autogenerated mock type for the SharedChannelStore type
type SharedChannelStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: channelID
func (_m *SharedChannelStore) Delete(channelID string) (bool, error) {
ret := _m.Called(channelID)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string) (bool, error)); ok {
return rf(channelID)
}
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(channelID)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(channelID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeleteRemote provides a mock function with given fields: remoteID
func (_m *SharedChannelStore) DeleteRemote(remoteID string) (bool, error) {
ret := _m.Called(remoteID)
if len(ret) == 0 {
panic("no return value specified for DeleteRemote")
}
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string) (bool, error)); ok {
return rf(remoteID)
}
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(remoteID)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Get provides a mock function with given fields: channelID
func (_m *SharedChannelStore) Get(channelID string) (*model.SharedChannel, error) {
ret := _m.Called(channelID)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 *model.SharedChannel
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.SharedChannel, error)); ok {
return rf(channelID)
}
if rf, ok := ret.Get(0).(func(string) *model.SharedChannel); ok {
r0 = rf(channelID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannel)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(channelID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetAll provides a mock function with given fields: offset, limit, opts
func (_m *SharedChannelStore) GetAll(offset int, limit int, opts model.SharedChannelFilterOpts) ([]*model.SharedChannel, error) {
ret := _m.Called(offset, limit, opts)
if len(ret) == 0 {
panic("no return value specified for GetAll")
}
var r0 []*model.SharedChannel
var r1 error
if rf, ok := ret.Get(0).(func(int, int, model.SharedChannelFilterOpts) ([]*model.SharedChannel, error)); ok {
return rf(offset, limit, opts)
}
if rf, ok := ret.Get(0).(func(int, int, model.SharedChannelFilterOpts) []*model.SharedChannel); ok {
r0 = rf(offset, limit, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.SharedChannel)
}
}
if rf, ok := ret.Get(1).(func(int, int, model.SharedChannelFilterOpts) error); ok {
r1 = rf(offset, limit, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetAllCount provides a mock function with given fields: opts
func (_m *SharedChannelStore) GetAllCount(opts model.SharedChannelFilterOpts) (int64, error) {
ret := _m.Called(opts)
if len(ret) == 0 {
panic("no return value specified for GetAllCount")
}
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(model.SharedChannelFilterOpts) (int64, error)); ok {
return rf(opts)
}
if rf, ok := ret.Get(0).(func(model.SharedChannelFilterOpts) int64); ok {
r0 = rf(opts)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(model.SharedChannelFilterOpts) error); ok {
r1 = rf(opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetAttachment provides a mock function with given fields: fileID, remoteID
func (_m *SharedChannelStore) GetAttachment(fileID string, remoteID string) (*model.SharedChannelAttachment, error) {
ret := _m.Called(fileID, remoteID)
if len(ret) == 0 {
panic("no return value specified for GetAttachment")
}
var r0 *model.SharedChannelAttachment
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (*model.SharedChannelAttachment, error)); ok {
return rf(fileID, remoteID)
}
if rf, ok := ret.Get(0).(func(string, string) *model.SharedChannelAttachment); ok {
r0 = rf(fileID, remoteID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelAttachment)
}
}
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(fileID, remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetRemote provides a mock function with given fields: id
func (_m *SharedChannelStore) GetRemote(id string) (*model.SharedChannelRemote, error) {
ret := _m.Called(id)
if len(ret) == 0 {
panic("no return value specified for GetRemote")
}
var r0 *model.SharedChannelRemote
var r1 error
if rf, ok := ret.Get(0).(func(string) (*model.SharedChannelRemote, error)); ok {
return rf(id)
}
if rf, ok := ret.Get(0).(func(string) *model.SharedChannelRemote); ok {
r0 = rf(id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelRemote)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetRemoteByIds provides a mock function with given fields: channelID, remoteID
func (_m *SharedChannelStore) GetRemoteByIds(channelID string, remoteID string) (*model.SharedChannelRemote, error) {
ret := _m.Called(channelID, remoteID)
if len(ret) == 0 {
panic("no return value specified for GetRemoteByIds")
}
var r0 *model.SharedChannelRemote
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (*model.SharedChannelRemote, error)); ok {
return rf(channelID, remoteID)
}
if rf, ok := ret.Get(0).(func(string, string) *model.SharedChannelRemote); ok {
r0 = rf(channelID, remoteID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelRemote)
}
}
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(channelID, remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetRemoteForUser provides a mock function with given fields: remoteID, userID, includeDeleted
func (_m *SharedChannelStore) GetRemoteForUser(remoteID string, userID string, includeDeleted bool) (*model.RemoteCluster, error) {
ret := _m.Called(remoteID, userID, includeDeleted)
if len(ret) == 0 {
panic("no return value specified for GetRemoteForUser")
}
var r0 *model.RemoteCluster
var r1 error
if rf, ok := ret.Get(0).(func(string, string, bool) (*model.RemoteCluster, error)); ok {
return rf(remoteID, userID, includeDeleted)
}
if rf, ok := ret.Get(0).(func(string, string, bool) *model.RemoteCluster); ok {
r0 = rf(remoteID, userID, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.RemoteCluster)
}
}
if rf, ok := ret.Get(1).(func(string, string, bool) error); ok {
r1 = rf(remoteID, userID, includeDeleted)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetRemotes provides a mock function with given fields: offset, limit, opts
func (_m *SharedChannelStore) GetRemotes(offset int, limit int, opts model.SharedChannelRemoteFilterOpts) ([]*model.SharedChannelRemote, error) {
ret := _m.Called(offset, limit, opts)
if len(ret) == 0 {
panic("no return value specified for GetRemotes")
}
var r0 []*model.SharedChannelRemote
var r1 error
if rf, ok := ret.Get(0).(func(int, int, model.SharedChannelRemoteFilterOpts) ([]*model.SharedChannelRemote, error)); ok {
return rf(offset, limit, opts)
}
if rf, ok := ret.Get(0).(func(int, int, model.SharedChannelRemoteFilterOpts) []*model.SharedChannelRemote); ok {
r0 = rf(offset, limit, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.SharedChannelRemote)
}
}
if rf, ok := ret.Get(1).(func(int, int, model.SharedChannelRemoteFilterOpts) error); ok {
r1 = rf(offset, limit, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetRemotesStatus provides a mock function with given fields: channelID
func (_m *SharedChannelStore) GetRemotesStatus(channelID string) ([]*model.SharedChannelRemoteStatus, error) {
ret := _m.Called(channelID)
if len(ret) == 0 {
panic("no return value specified for GetRemotesStatus")
}
var r0 []*model.SharedChannelRemoteStatus
var r1 error
if rf, ok := ret.Get(0).(func(string) ([]*model.SharedChannelRemoteStatus, error)); ok {
return rf(channelID)
}
if rf, ok := ret.Get(0).(func(string) []*model.SharedChannelRemoteStatus); ok {
r0 = rf(channelID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.SharedChannelRemoteStatus)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(channelID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetSingleUser provides a mock function with given fields: userID, channelID, remoteID
func (_m *SharedChannelStore) GetSingleUser(userID string, channelID string, remoteID string) (*model.SharedChannelUser, error) {
ret := _m.Called(userID, channelID, remoteID)
if len(ret) == 0 {
panic("no return value specified for GetSingleUser")
}
var r0 *model.SharedChannelUser
var r1 error
if rf, ok := ret.Get(0).(func(string, string, string) (*model.SharedChannelUser, error)); ok {
return rf(userID, channelID, remoteID)
}
if rf, ok := ret.Get(0).(func(string, string, string) *model.SharedChannelUser); ok {
r0 = rf(userID, channelID, remoteID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelUser)
}
}
if rf, ok := ret.Get(1).(func(string, string, string) error); ok {
r1 = rf(userID, channelID, remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetUsersForSync provides a mock function with given fields: filter
func (_m *SharedChannelStore) GetUsersForSync(filter model.GetUsersForSyncFilter) ([]*model.User, error) {
ret := _m.Called(filter)
if len(ret) == 0 {
panic("no return value specified for GetUsersForSync")
}
var r0 []*model.User
var r1 error
if rf, ok := ret.Get(0).(func(model.GetUsersForSyncFilter) ([]*model.User, error)); ok {
return rf(filter)
}
if rf, ok := ret.Get(0).(func(model.GetUsersForSyncFilter) []*model.User); ok {
r0 = rf(filter)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.User)
}
}
if rf, ok := ret.Get(1).(func(model.GetUsersForSyncFilter) error); ok {
r1 = rf(filter)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetUsersForUser provides a mock function with given fields: userID
func (_m *SharedChannelStore) GetUsersForUser(userID string) ([]*model.SharedChannelUser, error) {
ret := _m.Called(userID)
if len(ret) == 0 {
panic("no return value specified for GetUsersForUser")
}
var r0 []*model.SharedChannelUser
var r1 error
if rf, ok := ret.Get(0).(func(string) ([]*model.SharedChannelUser, error)); ok {
return rf(userID)
}
if rf, ok := ret.Get(0).(func(string) []*model.SharedChannelUser); ok {
r0 = rf(userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.SharedChannelUser)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// HasChannel provides a mock function with given fields: channelID
func (_m *SharedChannelStore) HasChannel(channelID string) (bool, error) {
ret := _m.Called(channelID)
if len(ret) == 0 {
panic("no return value specified for HasChannel")
}
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string) (bool, error)); ok {
return rf(channelID)
}
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(channelID)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(channelID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// HasRemote provides a mock function with given fields: channelID, remoteID
func (_m *SharedChannelStore) HasRemote(channelID string, remoteID string) (bool, error) {
ret := _m.Called(channelID, remoteID)
if len(ret) == 0 {
panic("no return value specified for HasRemote")
}
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (bool, error)); ok {
return rf(channelID, remoteID)
}
if rf, ok := ret.Get(0).(func(string, string) bool); ok {
r0 = rf(channelID, remoteID)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(channelID, remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Save provides a mock function with given fields: sc
func (_m *SharedChannelStore) Save(sc *model.SharedChannel) (*model.SharedChannel, error) {
ret := _m.Called(sc)
if len(ret) == 0 {
panic("no return value specified for Save")
}
var r0 *model.SharedChannel
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannel) (*model.SharedChannel, error)); ok {
return rf(sc)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannel) *model.SharedChannel); ok {
r0 = rf(sc)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannel)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannel) error); ok {
r1 = rf(sc)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SaveAttachment provides a mock function with given fields: remote
func (_m *SharedChannelStore) SaveAttachment(remote *model.SharedChannelAttachment) (*model.SharedChannelAttachment, error) {
ret := _m.Called(remote)
if len(ret) == 0 {
panic("no return value specified for SaveAttachment")
}
var r0 *model.SharedChannelAttachment
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannelAttachment) (*model.SharedChannelAttachment, error)); ok {
return rf(remote)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannelAttachment) *model.SharedChannelAttachment); ok {
r0 = rf(remote)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelAttachment)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannelAttachment) error); ok {
r1 = rf(remote)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SaveRemote provides a mock function with given fields: remote
func (_m *SharedChannelStore) SaveRemote(remote *model.SharedChannelRemote) (*model.SharedChannelRemote, error) {
ret := _m.Called(remote)
if len(ret) == 0 {
panic("no return value specified for SaveRemote")
}
var r0 *model.SharedChannelRemote
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannelRemote) (*model.SharedChannelRemote, error)); ok {
return rf(remote)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannelRemote) *model.SharedChannelRemote); ok {
r0 = rf(remote)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelRemote)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannelRemote) error); ok {
r1 = rf(remote)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SaveUser provides a mock function with given fields: remote
func (_m *SharedChannelStore) SaveUser(remote *model.SharedChannelUser) (*model.SharedChannelUser, error) {
ret := _m.Called(remote)
if len(ret) == 0 {
panic("no return value specified for SaveUser")
}
var r0 *model.SharedChannelUser
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannelUser) (*model.SharedChannelUser, error)); ok {
return rf(remote)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannelUser) *model.SharedChannelUser); ok {
r0 = rf(remote)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelUser)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannelUser) error); ok {
r1 = rf(remote)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: sc
func (_m *SharedChannelStore) Update(sc *model.SharedChannel) (*model.SharedChannel, error) {
ret := _m.Called(sc)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 *model.SharedChannel
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannel) (*model.SharedChannel, error)); ok {
return rf(sc)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannel) *model.SharedChannel); ok {
r0 = rf(sc)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannel)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannel) error); ok {
r1 = rf(sc)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateAttachmentLastSyncAt provides a mock function with given fields: id, syncTime
func (_m *SharedChannelStore) UpdateAttachmentLastSyncAt(id string, syncTime int64) error {
ret := _m.Called(id, syncTime)
if len(ret) == 0 {
panic("no return value specified for UpdateAttachmentLastSyncAt")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(id, syncTime)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateRemote provides a mock function with given fields: remote
func (_m *SharedChannelStore) UpdateRemote(remote *model.SharedChannelRemote) (*model.SharedChannelRemote, error) {
ret := _m.Called(remote)
if len(ret) == 0 {
panic("no return value specified for UpdateRemote")
}
var r0 *model.SharedChannelRemote
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannelRemote) (*model.SharedChannelRemote, error)); ok {
return rf(remote)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannelRemote) *model.SharedChannelRemote); ok {
r0 = rf(remote)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SharedChannelRemote)
}
}
if rf, ok := ret.Get(1).(func(*model.SharedChannelRemote) error); ok {
r1 = rf(remote)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateRemoteCursor provides a mock function with given fields: id, cursor
func (_m *SharedChannelStore) UpdateRemoteCursor(id string, cursor model.GetPostsSinceForSyncCursor) error {
ret := _m.Called(id, cursor)
if len(ret) == 0 {
panic("no return value specified for UpdateRemoteCursor")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, model.GetPostsSinceForSyncCursor) error); ok {
r0 = rf(id, cursor)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateRemoteMembershipCursor provides a mock function with given fields: id, syncTime
func (_m *SharedChannelStore) UpdateRemoteMembershipCursor(id string, syncTime int64) error {
ret := _m.Called(id, syncTime)
if len(ret) == 0 {
panic("no return value specified for UpdateRemoteMembershipCursor")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(id, syncTime)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateUserLastSyncAt provides a mock function with given fields: userID, channelID, remoteID
func (_m *SharedChannelStore) UpdateUserLastSyncAt(userID string, channelID string, remoteID string) error {
ret := _m.Called(userID, channelID, remoteID)
if len(ret) == 0 {
panic("no return value specified for UpdateUserLastSyncAt")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string) error); ok {
r0 = rf(userID, channelID, remoteID)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpsertAttachment provides a mock function with given fields: remote
func (_m *SharedChannelStore) UpsertAttachment(remote *model.SharedChannelAttachment) (string, error) {
ret := _m.Called(remote)
if len(ret) == 0 {
panic("no return value specified for UpsertAttachment")
}
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*model.SharedChannelAttachment) (string, error)); ok {
return rf(remote)
}
if rf, ok := ret.Get(0).(func(*model.SharedChannelAttachment) string); ok {
r0 = rf(remote)
} else {
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(*model.SharedChannelAttachment) error); ok {
r1 = rf(remote)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewSharedChannelStore creates a new instance of SharedChannelStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewSharedChannelStore(t interface {
mock.TestingT
Cleanup(func())
}) *SharedChannelStore {
mock := &SharedChannelStore{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}