Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6105 lines
185 KiB
Go
6105 lines
185 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package storetest
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
)
|
|
|
|
func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("Create", func(t *testing.T) { testGroupStoreCreate(t, rctx, ss) })
|
|
t.Run("CreateWithUserIds", func(t *testing.T) { testGroupCreateWithUserIds(t, rctx, ss) })
|
|
|
|
t.Run("Get", func(t *testing.T) { testGroupStoreGet(t, rctx, ss) })
|
|
t.Run("GetByName", func(t *testing.T) { testGroupStoreGetByName(t, rctx, ss) })
|
|
t.Run("GetByIDs", func(t *testing.T) { testGroupStoreGetByIDs(t, rctx, ss) })
|
|
t.Run("GetByRemoteID", func(t *testing.T) { testGroupStoreGetByRemoteID(t, rctx, ss) })
|
|
t.Run("GetAllBySource", func(t *testing.T) { testGroupAllBySource(t, rctx, ss) })
|
|
t.Run("GetByUser", func(t *testing.T) { testGroupStoreGetByUser(t, rctx, ss) })
|
|
t.Run("Update", func(t *testing.T) { testGroupStoreUpdate(t, rctx, ss) })
|
|
t.Run("Delete", func(t *testing.T) { testGroupStoreDelete(t, rctx, ss) })
|
|
t.Run("Restore", func(t *testing.T) { testGroupStoreRestore(t, rctx, ss) })
|
|
t.Run("ToModelChannelAssociations", func(t *testing.T) { testGroupStoreToModelChannelAssociations(t, rctx, ss) })
|
|
|
|
t.Run("GetMemberUsers", func(t *testing.T) { testGroupGetMemberUsers(t, rctx, ss) })
|
|
t.Run("GetMemberUsersPage", func(t *testing.T) { testGroupGetMemberUsersPage(t, rctx, ss) })
|
|
t.Run("GetMemberUsersSortedPage", func(t *testing.T) { testGroupGetMemberUsersSortedPage(t, rctx, ss) })
|
|
|
|
t.Run("GetMemberUsersInTeam", func(t *testing.T) { testGroupGetMemberUsersInTeam(t, rctx, ss) })
|
|
t.Run("GetMemberUsersNotInChannel", func(t *testing.T) { testGroupGetMemberUsersNotInChannel(t, rctx, ss) })
|
|
|
|
t.Run("UpsertMember", func(t *testing.T) { testUpsertMember(t, rctx, ss) })
|
|
t.Run("UpsertMembers", func(t *testing.T) { testUpsertMembers(t, rctx, ss) })
|
|
t.Run("DeleteMember", func(t *testing.T) { testGroupDeleteMember(t, rctx, ss) })
|
|
t.Run("DeleteMembers", func(t *testing.T) { testGroupDeleteMembers(t, rctx, ss) })
|
|
t.Run("PermanentDeleteMembersByUser", func(t *testing.T) { testGroupPermanentDeleteMembersByUser(t, rctx, ss) })
|
|
|
|
t.Run("CreateGroupSyncable", func(t *testing.T) { testCreateGroupSyncable(t, rctx, ss) })
|
|
t.Run("GetGroupSyncable", func(t *testing.T) { testGetGroupSyncable(t, rctx, ss) })
|
|
t.Run("GetGroupSyncableErrors", func(t *testing.T) { testGetGroupSyncableErrors(t, rctx, ss) })
|
|
t.Run("GetAllGroupSyncablesByGroupId", func(t *testing.T) { testGetAllGroupSyncablesByGroup(t, rctx, ss) })
|
|
t.Run("UpdateGroupSyncable", func(t *testing.T) { testUpdateGroupSyncable(t, rctx, ss) })
|
|
t.Run("DeleteGroupSyncable", func(t *testing.T) { testDeleteGroupSyncable(t, rctx, ss) })
|
|
|
|
t.Run("TeamMembersToAdd", func(t *testing.T) { testTeamMembersToAdd(t, rctx, ss) })
|
|
t.Run("TeamMembersToAdd_SingleTeam", func(t *testing.T) { testTeamMembersToAddSingleTeam(t, rctx, ss) })
|
|
|
|
t.Run("ChannelMembersToAdd", func(t *testing.T) { testChannelMembersToAdd(t, rctx, ss) })
|
|
t.Run("ChannelMembersToAdd_SingleChannel", func(t *testing.T) { testChannelMembersToAddSingleChannel(t, rctx, ss) })
|
|
|
|
t.Run("TeamMembersToRemove", func(t *testing.T) { testTeamMembersToRemove(t, rctx, ss) })
|
|
t.Run("TeamMembersToRemove_SingleTeam", func(t *testing.T) { testTeamMembersToRemoveSingleTeam(t, rctx, ss) })
|
|
|
|
t.Run("ChannelMembersToRemove", func(t *testing.T) { testChannelMembersToRemove(t, rctx, ss) })
|
|
t.Run("ChannelMembersToRemove_SingleChannel", func(t *testing.T) { testChannelMembersToRemoveSingleChannel(t, rctx, ss) })
|
|
|
|
t.Run("GetGroupsByChannel", func(t *testing.T) { testGetGroupsByChannel(t, rctx, ss) })
|
|
t.Run("GetGroupsAssociatedToChannelsByTeam", func(t *testing.T) { testGetGroupsAssociatedToChannelsByTeam(t, rctx, ss) })
|
|
t.Run("GetGroupsByTeam", func(t *testing.T) { testGetGroupsByTeam(t, rctx, ss) })
|
|
|
|
t.Run("GetGroups", func(t *testing.T) { testGetGroups(t, rctx, ss) })
|
|
|
|
t.Run("TeamMembersMinusGroupMembers", func(t *testing.T) { testTeamMembersMinusGroupMembers(t, rctx, ss) })
|
|
t.Run("ChannelMembersMinusGroupMembers", func(t *testing.T) { testChannelMembersMinusGroupMembers(t, rctx, ss) })
|
|
t.Run("CountMembersMinusGroupMembers", func(t *testing.T) { testCountMembersMinusGroupMembers(t, rctx, ss) })
|
|
|
|
t.Run("GetMemberCount", func(t *testing.T) { groupTestGetMemberCount(t, rctx, ss) })
|
|
|
|
t.Run("AdminRoleGroupsForSyncableMember_Channel", func(t *testing.T) { groupTestAdminRoleGroupsForSyncableMemberChannel(t, rctx, ss) })
|
|
t.Run("AdminRoleGroupsForSyncableMember_Team", func(t *testing.T) { groupTestAdminRoleGroupsForSyncableMemberTeam(t, rctx, ss) })
|
|
t.Run("PermittedSyncableAdmins_Team", func(t *testing.T) { groupTestPermittedSyncableAdminsTeam(t, rctx, ss) })
|
|
t.Run("PermittedSyncableAdmins_Channel", func(t *testing.T) { groupTestPermittedSyncableAdminsChannel(t, rctx, ss) })
|
|
t.Run("UpdateMembersRole_Team", func(t *testing.T) { groupTestUpdateMembersRoleTeam(t, rctx, ss) })
|
|
t.Run("UpdateMembersRole_Channel", func(t *testing.T) { groupTestpUpdateMembersRoleChannel(t, rctx, ss) })
|
|
|
|
t.Run("GroupCount", func(t *testing.T) { groupTestGroupCount(t, rctx, ss) })
|
|
t.Run("GroupTeamCount", func(t *testing.T) { groupTestGroupTeamCount(t, rctx, ss) })
|
|
t.Run("GroupChannelCount", func(t *testing.T) { groupTestGroupChannelCount(t, rctx, ss) })
|
|
t.Run("GroupMemberCount", func(t *testing.T) { groupTestGroupMemberCount(t, rctx, ss) })
|
|
t.Run("DistinctGroupMemberCount", func(t *testing.T) { groupTestDistinctGroupMemberCount(t, rctx, ss) })
|
|
t.Run("GroupCountWithAllowReference", func(t *testing.T) { groupTestGroupCountWithAllowReference(t, rctx, ss) })
|
|
|
|
t.Run("GetMember", func(t *testing.T) { groupTestGetMember(t, rctx, ss) })
|
|
t.Run("GetNonMemberUsersPage", func(t *testing.T) { groupTestGetNonMemberUsersPage(t, rctx, ss) })
|
|
|
|
t.Run("DistinctGroupMemberCountForSource", func(t *testing.T) { groupTestDistinctGroupMemberCountForSource(t, rctx, ss) })
|
|
t.Run("GroupCountBySource", func(t *testing.T) { groupTestGroupCountBySource(t, rctx, ss) })
|
|
}
|
|
|
|
func testGroupStoreCreate(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a new group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
// Happy path
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
require.Equal(t, *g1.Name, *d1.Name)
|
|
require.Equal(t, g1.DisplayName, d1.DisplayName)
|
|
require.Equal(t, g1.Description, d1.Description)
|
|
require.Equal(t, g1.RemoteId, d1.RemoteId)
|
|
require.NotZero(t, d1.CreateAt)
|
|
require.NotZero(t, d1.UpdateAt)
|
|
require.Zero(t, d1.DeleteAt)
|
|
|
|
// Requires display name
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "",
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
data, err := ss.Group().Create(g2)
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
var appErr *model.AppError
|
|
require.True(t, errors.As(err, &appErr))
|
|
require.Equal(t, appErr.Id, "model.group.display_name.app_error")
|
|
|
|
// Won't accept a duplicate name
|
|
g4 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
_, err = ss.Group().Create(g4)
|
|
require.NoError(t, err)
|
|
g4b := &model.Group{
|
|
Name: g4.Name,
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
data, err = ss.Group().Create(g4b)
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), fmt.Sprintf("Group with name %s already exists", *g4b.Name))
|
|
|
|
// Fields cannot be greater than max values
|
|
g5 := &model.Group{
|
|
Name: model.NewPointer(strings.Repeat("x", model.GroupNameMaxLength)),
|
|
DisplayName: strings.Repeat("x", model.GroupDisplayNameMaxLength),
|
|
Description: strings.Repeat("x", model.GroupDescriptionMaxLength),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
require.Nil(t, g5.IsValidForCreate())
|
|
|
|
g5.Name = model.NewPointer(*g5.Name + "x")
|
|
require.Equal(t, g5.IsValidForCreate().Id, "model.group.name.invalid_length.app_error")
|
|
g5.Name = model.NewPointer(model.NewId())
|
|
require.Nil(t, g5.IsValidForCreate())
|
|
|
|
g5.DisplayName = g5.DisplayName + "x"
|
|
require.Equal(t, g5.IsValidForCreate().Id, "model.group.display_name.app_error")
|
|
g5.DisplayName = model.NewId()
|
|
require.Nil(t, g5.IsValidForCreate())
|
|
|
|
g5.Description = g5.Description + "x"
|
|
require.Equal(t, g5.IsValidForCreate().Id, "model.group.description.app_error")
|
|
g5.Description = model.NewId()
|
|
require.Nil(t, g5.IsValidForCreate())
|
|
|
|
// Must use a valid type
|
|
g6 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSource("fake"),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
require.Equal(t, g6.IsValidForCreate().Id, "model.group.source.app_error")
|
|
|
|
// must use valid characters
|
|
g7 := &model.Group{
|
|
Name: model.NewPointer("%^#@$$"),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
require.Equal(t, g7.IsValidForCreate().Id, "model.group.name.invalid_chars.app_error")
|
|
}
|
|
|
|
func testGroupCreateWithUserIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create user 1
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create user 2
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
// Save a new group
|
|
guids1 := &model.GroupWithUserIds{
|
|
Group: *g1,
|
|
UserIds: []string{user1.Id, user2.Id},
|
|
}
|
|
|
|
// Happy path
|
|
d1, err := ss.Group().CreateWithUserIds(guids1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
require.Equal(t, *guids1.Name, *d1.Name)
|
|
require.Equal(t, guids1.DisplayName, d1.DisplayName)
|
|
require.Equal(t, guids1.Description, d1.Description)
|
|
require.Equal(t, guids1.RemoteId, d1.RemoteId)
|
|
require.NotZero(t, d1.CreateAt)
|
|
require.NotZero(t, d1.UpdateAt)
|
|
require.Zero(t, d1.DeleteAt)
|
|
require.Equal(t, *model.NewPointer(2), *d1.MemberCount)
|
|
|
|
// Requires display name
|
|
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "",
|
|
Source: model.GroupSourceCustom,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
guids2 := &model.GroupWithUserIds{
|
|
Group: *g2,
|
|
UserIds: []string{user1.Id, user2.Id},
|
|
}
|
|
data, err := ss.Group().CreateWithUserIds(guids2)
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
var appErr *model.AppError
|
|
require.True(t, errors.As(err, &appErr))
|
|
require.Equal(t, appErr.Id, "model.group.display_name.app_error")
|
|
|
|
// Won't accept a duplicate name
|
|
g4 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids4 := &model.GroupWithUserIds{
|
|
Group: *g4,
|
|
UserIds: []string{user1.Id, user2.Id},
|
|
}
|
|
_, err = ss.Group().CreateWithUserIds(guids4)
|
|
require.NoError(t, err)
|
|
g4b := &model.Group{
|
|
Name: g4.Name,
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids4b := &model.GroupWithUserIds{
|
|
Group: *g4b,
|
|
UserIds: []string{user1.Id},
|
|
}
|
|
data, err = ss.Group().CreateWithUserIds(guids4b)
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unique constraint: Name")
|
|
|
|
// Fields cannot be greater than max values
|
|
g5 := &model.Group{
|
|
Name: model.NewPointer(strings.Repeat("x", model.GroupNameMaxLength)),
|
|
DisplayName: strings.Repeat("x", model.GroupDisplayNameMaxLength),
|
|
Description: strings.Repeat("x", model.GroupDescriptionMaxLength),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids5 := &model.GroupWithUserIds{
|
|
Group: *g5,
|
|
}
|
|
require.Nil(t, guids5.IsValidForCreate())
|
|
|
|
guids5.Name = model.NewPointer(*guids5.Name + "x")
|
|
require.Equal(t, guids5.IsValidForCreate().Id, "model.group.name.invalid_length.app_error")
|
|
guids5.Name = model.NewPointer(model.NewId())
|
|
require.Nil(t, guids5.IsValidForCreate())
|
|
|
|
guids5.DisplayName = guids5.DisplayName + "x"
|
|
require.Equal(t, guids5.IsValidForCreate().Id, "model.group.display_name.app_error")
|
|
guids5.DisplayName = model.NewId()
|
|
require.Nil(t, guids5.IsValidForCreate())
|
|
|
|
guids5.Description = guids5.Description + "x"
|
|
require.Equal(t, guids5.IsValidForCreate().Id, "model.group.description.app_error")
|
|
guids5.Description = model.NewId()
|
|
require.Nil(t, guids5.IsValidForCreate())
|
|
|
|
// Must use a valid type
|
|
g6 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSource("fake"),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids6 := &model.GroupWithUserIds{
|
|
Group: *g6,
|
|
}
|
|
require.Equal(t, guids6.IsValidForCreate().Id, "model.group.source.app_error")
|
|
|
|
// must use valid characters
|
|
g7 := &model.Group{
|
|
Name: model.NewPointer("%^#@$$"),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids7 := &model.GroupWithUserIds{
|
|
Group: *g7,
|
|
}
|
|
require.Equal(t, guids7.IsValidForCreate().Id, "model.group.name.invalid_chars.app_error")
|
|
|
|
// Invalid user ids
|
|
g8 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids8 := &model.GroupWithUserIds{
|
|
Group: *g8,
|
|
UserIds: []string{"1234uid"},
|
|
}
|
|
data, err = ss.Group().CreateWithUserIds(guids8)
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
require.Equal(t, store.NewErrNotFound("User", "1234uid"), err)
|
|
}
|
|
|
|
func testGroupStoreGet(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
|
|
// Get the group
|
|
d2, err := ss.Group().Get(d1.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, d1.Id, d2.Id)
|
|
require.Equal(t, *d1.Name, *d2.Name)
|
|
require.Equal(t, d1.DisplayName, d2.DisplayName)
|
|
require.Equal(t, d1.Description, d2.Description)
|
|
require.Equal(t, d1.RemoteId, d2.RemoteId)
|
|
require.Equal(t, d1.CreateAt, d2.CreateAt)
|
|
require.Equal(t, d1.UpdateAt, d2.UpdateAt)
|
|
require.Equal(t, d1.DeleteAt, d2.DeleteAt)
|
|
|
|
// Get an invalid group
|
|
_, err = ss.Group().Get(model.NewId())
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupStoreGetByName(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
g1Opts := model.GroupSearchOpts{
|
|
FilterAllowReference: false,
|
|
}
|
|
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
|
|
// Get the group
|
|
d2, err := ss.Group().GetByName(*d1.Name, g1Opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, d1.Id, d2.Id)
|
|
require.Equal(t, *d1.Name, *d2.Name)
|
|
require.Equal(t, d1.DisplayName, d2.DisplayName)
|
|
require.Equal(t, d1.Description, d2.Description)
|
|
require.Equal(t, d1.RemoteId, d2.RemoteId)
|
|
require.Equal(t, d1.CreateAt, d2.CreateAt)
|
|
require.Equal(t, d1.UpdateAt, d2.UpdateAt)
|
|
require.Equal(t, d1.DeleteAt, d2.DeleteAt)
|
|
|
|
// Get an invalid group
|
|
_, err = ss.Group().GetByName(model.NewId(), g1Opts)
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupStoreGetByIDs(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
var group1 *model.Group
|
|
var group2 *model.Group
|
|
|
|
for i := range 2 {
|
|
group := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(group)
|
|
require.NoError(t, err)
|
|
switch i {
|
|
case 0:
|
|
group1 = group
|
|
case 1:
|
|
group2 = group
|
|
}
|
|
}
|
|
|
|
groups, err := ss.Group().GetByIDs([]string{group1.Id, group2.Id})
|
|
require.NoError(t, err)
|
|
require.Len(t, groups, 2)
|
|
|
|
for i := range 2 {
|
|
require.True(t, (groups[i].Id == group1.Id || groups[i].Id == group2.Id))
|
|
}
|
|
|
|
require.True(t, groups[0].Id != groups[1].Id)
|
|
}
|
|
|
|
func testGroupStoreGetByRemoteID(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
|
|
// Get the group
|
|
d2, err := ss.Group().GetByRemoteID(*d1.RemoteId, model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, d1.Id, d2.Id)
|
|
require.Equal(t, *d1.Name, *d2.Name)
|
|
require.Equal(t, d1.DisplayName, d2.DisplayName)
|
|
require.Equal(t, d1.Description, d2.Description)
|
|
require.Equal(t, d1.RemoteId, d2.RemoteId)
|
|
require.Equal(t, d1.CreateAt, d2.CreateAt)
|
|
require.Equal(t, d1.UpdateAt, d2.UpdateAt)
|
|
require.Equal(t, d1.DeleteAt, d2.DeleteAt)
|
|
|
|
// Get an invalid group
|
|
_, err = ss.Group().GetByRemoteID(model.NewId(), model.GroupSource("fake"))
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupAllBySource(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create groups with different sources
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
customGroup, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(customGroup.Id)
|
|
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
ldapGroup, err := ss.Group().Create(g2)
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(ldapGroup.Id)
|
|
|
|
g3 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
ldapGroup2, err := ss.Group().Create(g3)
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(ldapGroup2.Id)
|
|
|
|
// Test filtering by LDAP source
|
|
ldapGroups, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
|
|
// Verify we got at least the 2 LDAP groups we created
|
|
found1, found2 := false, false
|
|
for _, group := range ldapGroups {
|
|
if group.Id == ldapGroup.Id {
|
|
found1 = true
|
|
}
|
|
if group.Id == ldapGroup2.Id {
|
|
found2 = true
|
|
}
|
|
// Make sure all returned groups are LDAP source
|
|
require.Equal(t, model.GroupSourceLdap, group.Source)
|
|
}
|
|
require.True(t, found1, "Failed to find the first LDAP group")
|
|
require.True(t, found2, "Failed to find the second LDAP group")
|
|
|
|
// Test filtering by Custom source
|
|
customGroups, err := ss.Group().GetAllBySource(model.GroupSourceCustom)
|
|
require.NoError(t, err)
|
|
|
|
// Verify we got at least the custom group we created
|
|
foundCustom := false
|
|
for _, group := range customGroups {
|
|
if group.Id == customGroup.Id {
|
|
foundCustom = true
|
|
}
|
|
// Make sure all returned groups are Custom source
|
|
require.Equal(t, model.GroupSourceCustom, group.Source)
|
|
}
|
|
require.True(t, foundCustom, "Failed to find the custom group")
|
|
|
|
// Test with deleted group to ensure it's not returned
|
|
_, err = ss.Group().Delete(ldapGroup2.Id)
|
|
require.NoError(t, err)
|
|
|
|
ldapGroupsAfterDelete, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
|
|
// Verify the deleted group is not returned
|
|
for _, group := range ldapGroupsAfterDelete {
|
|
require.NotEqual(t, ldapGroup2.Id, group.Id, "Deleted group should not be returned")
|
|
}
|
|
}
|
|
|
|
func testGroupStoreGetByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
g1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
AllowReference: true,
|
|
}
|
|
g2, err = ss.Group().Create(g2)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
u1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(g1.Id, u1.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(g2.Id, u1.Id)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
u2, nErr = ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(g2.Id, u2.Id)
|
|
require.NoError(t, err)
|
|
|
|
groups, err := ss.Group().GetByUser(u1.Id, model.GroupSearchOpts{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(groups))
|
|
found1 := false
|
|
found2 := false
|
|
for _, g := range groups {
|
|
if g.Id == g1.Id {
|
|
found1 = true
|
|
}
|
|
if g.Id == g2.Id {
|
|
found2 = true
|
|
}
|
|
}
|
|
assert.True(t, found1)
|
|
assert.True(t, found2)
|
|
|
|
groups, err = ss.Group().GetByUser(u2.Id, model.GroupSearchOpts{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groups))
|
|
assert.Equal(t, g2.Id, groups[0].Id)
|
|
|
|
groups, err = ss.Group().GetByUser(model.NewId(), model.GroupSearchOpts{})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, len(groups))
|
|
|
|
groups, err = ss.Group().GetByUser(u1.Id, model.GroupSearchOpts{FilterAllowReference: true})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, len(groups))
|
|
assert.Equal(t, g2.Id, groups[0].Id)
|
|
}
|
|
|
|
func testGroupStoreUpdate(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a new group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer("g1-test"),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
// Create a group
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Update happy path
|
|
g1Update := &model.Group{}
|
|
*g1Update = *g1
|
|
g1Update.Name = model.NewPointer(model.NewId())
|
|
g1Update.DisplayName = model.NewId()
|
|
g1Update.Description = model.NewId()
|
|
g1Update.RemoteId = model.NewPointer(model.NewId())
|
|
|
|
ud1, err := ss.Group().Update(g1Update)
|
|
require.NoError(t, err)
|
|
// Not changed...
|
|
require.Equal(t, d1.Id, ud1.Id)
|
|
require.Equal(t, d1.CreateAt, ud1.CreateAt)
|
|
require.Equal(t, d1.Source, ud1.Source)
|
|
// Still zero...
|
|
require.Zero(t, ud1.DeleteAt)
|
|
// Updated...
|
|
require.Equal(t, *g1Update.Name, *ud1.Name)
|
|
require.Equal(t, g1Update.DisplayName, ud1.DisplayName)
|
|
require.Equal(t, g1Update.Description, ud1.Description)
|
|
require.Equal(t, g1Update.RemoteId, ud1.RemoteId)
|
|
|
|
// Requires display name
|
|
data, err := ss.Group().Update(&model.Group{
|
|
Id: d1.Id,
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "",
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.Nil(t, data)
|
|
require.Error(t, err)
|
|
var appErr *model.AppError
|
|
require.True(t, errors.As(err, &appErr))
|
|
require.Equal(t, appErr.Id, "model.group.display_name.app_error")
|
|
|
|
// Create another Group
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
d2, err := ss.Group().Create(g2)
|
|
require.NoError(t, err)
|
|
|
|
// Can't update the name to be a duplicate of an existing group's name
|
|
_, err = ss.Group().Update(&model.Group{
|
|
Id: d2.Id,
|
|
Name: g1Update.Name,
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unique constraint: Name")
|
|
|
|
// Cannot update CreateAt
|
|
someVal := model.GetMillis()
|
|
d1.CreateAt = someVal
|
|
d3, err := ss.Group().Update(d1)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, someVal, d3.CreateAt)
|
|
|
|
// Cannot update DeleteAt to non-zero
|
|
d1.DeleteAt = 1
|
|
_, err = ss.Group().Update(d1)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "DeleteAt should be 0 when updating")
|
|
|
|
//...except for 0 for DeleteAt
|
|
d1.DeleteAt = 0
|
|
d4, err := ss.Group().Update(d1)
|
|
require.NoError(t, err)
|
|
require.Zero(t, d4.DeleteAt)
|
|
}
|
|
|
|
func testGroupStoreDelete(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
|
|
// Check the group is retrievable
|
|
_, err = ss.Group().Get(d1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Get the before count
|
|
d7, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
beforeCount := len(d7)
|
|
|
|
// Delete the group
|
|
_, err = ss.Group().Delete(d1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Check the group is deleted
|
|
d4, err := ss.Group().Get(d1.Id)
|
|
require.NoError(t, err)
|
|
require.NotZero(t, d4.DeleteAt)
|
|
|
|
// Check the after count
|
|
d5, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
afterCount := len(d5)
|
|
require.Condition(t, func() bool { return beforeCount == afterCount+1 }, beforeCount, "==", afterCount+1)
|
|
|
|
// Try and delete a nonexistent group
|
|
_, err = ss.Group().Delete(model.NewId())
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Cannot delete again
|
|
_, err = ss.Group().Delete(d1.Id)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupStoreRestore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
|
|
d1, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1.Id, 26)
|
|
|
|
// Check the group is retrievable
|
|
_, err = ss.Group().Get(d1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Delete the group
|
|
_, err = ss.Group().Delete(d1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Get the before count
|
|
d7, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
beforeCount := len(d7)
|
|
|
|
// restore the group
|
|
_, err = ss.Group().Restore(d1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Check the group is restored
|
|
d4, err := ss.Group().Get(d1.Id)
|
|
require.NoError(t, err)
|
|
require.Zero(t, d4.DeleteAt)
|
|
|
|
// Check the after count
|
|
d5, err := ss.Group().GetAllBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
afterCount := len(d5)
|
|
require.Condition(t, func() bool { return beforeCount == afterCount-1 })
|
|
|
|
// Try and restore a nonexistent group
|
|
_, err = ss.Group().Delete(model.NewId())
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Cannot restore again
|
|
_, err = ss.Group().Restore(d1.Id)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupGetMemberUsers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Check returns members
|
|
groupMembers, err := ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
|
|
// Check madeup id
|
|
groupMembers, err = ss.Group().GetMemberUsers(model.NewId())
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
// Delete a member
|
|
_, err = ss.Group().DeleteMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Should not return deleted members
|
|
groupMembers, err = ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
}
|
|
|
|
func testGroupGetMemberUsersPage(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "user1" + model.NewId(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "user2" + model.NewId(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
u3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "user3" + model.NewId(),
|
|
}
|
|
user3, nErr := ss.User().Save(rctx, u3)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Check returns members
|
|
groupMembers, err := ss.Group().GetMemberUsersPage(group.Id, 0, 100, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(groupMembers))
|
|
|
|
// Check page 1
|
|
groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 2, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user1, user2}, groupMembers)
|
|
|
|
// Check page 2
|
|
groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 1, 2, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user3}, groupMembers)
|
|
|
|
// Check madeup id
|
|
groupMembers, err = ss.Group().GetMemberUsersPage(model.NewId(), 0, 100, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
// Delete a member
|
|
_, err = ss.Group().DeleteMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Should not return deleted members
|
|
groupMembers, err = ss.Group().GetMemberUsersPage(group.Id, 0, 100, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
}
|
|
|
|
func testGroupGetMemberUsersSortedPage(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// First by nickname, third by full name, second by username
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "y" + model.NewId(),
|
|
Nickname: model.NewUsername(),
|
|
FirstName: "z" + model.NewId(),
|
|
LastName: "z" + model.NewId(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Second by nickname, first by full name, third by username
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "z" + model.NewId(),
|
|
FirstName: "b" + model.NewId(),
|
|
LastName: "b" + model.NewId(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Third by nickname, second by full name, first by username
|
|
u3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "d" + model.NewId(),
|
|
}
|
|
user3, nErr := ss.User().Save(rctx, u3)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Check nickname ordering, paged
|
|
groupMembers, err := ss.Group().GetMemberUsersSortedPage(group.Id, 0, 2, nil, model.ShowNicknameFullName)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user1, user2}, groupMembers)
|
|
groupMembers, err = ss.Group().GetMemberUsersSortedPage(group.Id, 1, 2, nil, model.ShowNicknameFullName)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user3}, groupMembers)
|
|
|
|
// Check full name ordering, paged
|
|
groupMembers, err = ss.Group().GetMemberUsersSortedPage(group.Id, 0, 2, nil, model.ShowFullName)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user2, user3}, groupMembers)
|
|
groupMembers, err = ss.Group().GetMemberUsersSortedPage(group.Id, 1, 2, nil, model.ShowFullName)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user1}, groupMembers)
|
|
|
|
// Check username ordering
|
|
groupMembers, err = ss.Group().GetMemberUsersSortedPage(group.Id, 0, 2, nil, model.ShowUsername)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user3, user1}, groupMembers)
|
|
groupMembers, err = ss.Group().GetMemberUsersSortedPage(group.Id, 1, 2, nil, model.ShowUsername)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
require.ElementsMatch(t, []*model.User{user2}, groupMembers)
|
|
}
|
|
|
|
func testGroupGetMemberUsersInTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err := ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
u3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err := ss.User().Save(rctx, u3)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
// returns no members when team does not exist
|
|
groupMembers, err := ss.Group().GetMemberUsersInTeam(group.Id, "non-existent-channel-id")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
// returns no members when group has no members in the team
|
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
m1 := &model.TeamMember{TeamId: team.Id, UserId: user1.Id}
|
|
_, nErr := ss.Team().SaveMember(rctx, m1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// returns single member in team
|
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
|
|
m2 := &model.TeamMember{TeamId: team.Id, UserId: user2.Id}
|
|
m3 := &model.TeamMember{TeamId: team.Id, UserId: user3.Id}
|
|
_, nErr = ss.Team().SaveMember(rctx, m2, -1)
|
|
require.NoError(t, nErr)
|
|
_, nErr = ss.Team().SaveMember(rctx, m3, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// returns all members when all members are in team
|
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(groupMembers))
|
|
}
|
|
|
|
func testGroupGetMemberUsersNotInChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Save a team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err := ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
// Save a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
u3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err := ss.User().Save(rctx, u3)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Create Channel
|
|
channel := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "Channel",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen, // Query does not look at type so this shouldn't matter.
|
|
}
|
|
channel, nErr := ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// returns no members when channel does not exist
|
|
groupMembers, err := ss.Group().GetMemberUsersNotInChannel(group.Id, "non-existent-channel-id")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
// returns no members when group has no members in the team that the channel belongs to
|
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
|
|
m1 := &model.TeamMember{TeamId: team.Id, UserId: user1.Id}
|
|
_, nErr = ss.Team().SaveMember(rctx, m1, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// returns single member in team and not in channel
|
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(groupMembers))
|
|
|
|
m2 := &model.TeamMember{TeamId: team.Id, UserId: user2.Id}
|
|
m3 := &model.TeamMember{TeamId: team.Id, UserId: user3.Id}
|
|
_, nErr = ss.Team().SaveMember(rctx, m2, -1)
|
|
require.NoError(t, nErr)
|
|
_, nErr = ss.Team().SaveMember(rctx, m3, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// returns all members when all members are in team and not in channel
|
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 3, len(groupMembers))
|
|
|
|
cm1 := &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user1.Id,
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
SchemeAdmin: false,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
_, err = ss.Channel().SaveMember(rctx, cm1)
|
|
require.NoError(t, err)
|
|
|
|
// returns both members not yet added to channel
|
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(groupMembers))
|
|
|
|
cm2 := &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user2.Id,
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
SchemeAdmin: false,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
cm3 := &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user3.Id,
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
SchemeAdmin: false,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
|
|
_, err = ss.Channel().SaveMember(rctx, cm2)
|
|
require.NoError(t, err)
|
|
_, err = ss.Channel().SaveMember(rctx, cm3)
|
|
require.NoError(t, err)
|
|
|
|
// returns none when all members have been added to team and channel
|
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(groupMembers))
|
|
}
|
|
|
|
func testUpsertMember(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create user
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Happy path
|
|
d2, err := ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, d2.GroupId, group.Id)
|
|
require.Equal(t, d2.UserId, user.Id)
|
|
require.NotZero(t, d2.CreateAt)
|
|
require.Zero(t, d2.DeleteAt)
|
|
|
|
// Duplicate composite key (GroupId, UserId)
|
|
// Ensure new CreateAt > previous CreateAt for the same (groupId, userId)
|
|
time.Sleep(2 * time.Millisecond)
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Invalid GroupId
|
|
_, err = ss.Group().UpsertMember(model.NewId(), user.Id)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "failed to get UserGroup with")
|
|
|
|
// Restores a deleted member
|
|
// Ensure new CreateAt > previous CreateAt for the same (groupId, userId)
|
|
time.Sleep(2 * time.Millisecond)
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
groupMembers, err := ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
beforeRestoreCount := len(groupMembers)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
groupMembers, err = ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
afterRestoreCount := len(groupMembers)
|
|
|
|
require.Equal(t, beforeRestoreCount+1, afterRestoreCount)
|
|
}
|
|
|
|
func testUpsertMembers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create user
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create user
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
// Happy path
|
|
m, err := ss.Group().UpsertMembers(group.Id, []string{user.Id, user2.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(m))
|
|
|
|
// Duplicate composite key (GroupId, UserId)
|
|
// Ensure new CreateAt > previous CreateAt for the same (groupId, userId)
|
|
// time.Sleep(2 * time.Millisecond)
|
|
_, err = ss.Group().UpsertMembers(group.Id, []string{user.Id})
|
|
require.NoError(t, err)
|
|
|
|
// Invalid GroupId
|
|
_, err = ss.Group().UpsertMembers(model.NewId(), []string{user.Id})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "failed to get UserGroup with")
|
|
|
|
// Restores a deleted member
|
|
// Ensure new CreateAt > previous CreateAt for the same (groupId, userId)
|
|
time.Sleep(2 * time.Millisecond)
|
|
_, err = ss.Group().UpsertMembers(group.Id, []string{user.Id, user2.Id})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().DeleteMembers(group.Id, []string{user.Id})
|
|
require.NoError(t, err)
|
|
|
|
groupMembers, err := ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
beforeRestoreCount := len(groupMembers)
|
|
|
|
_, err = ss.Group().UpsertMembers(group.Id, []string{user.Id, user2.Id})
|
|
require.NoError(t, err)
|
|
|
|
groupMembers, err = ss.Group().GetMemberUsers(group.Id)
|
|
require.NoError(t, err)
|
|
afterRestoreCount := len(groupMembers)
|
|
|
|
require.Equal(t, beforeRestoreCount+1, afterRestoreCount)
|
|
}
|
|
|
|
func testGroupDeleteMember(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create user
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create member
|
|
d1, err := ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Happy path
|
|
d2, err := ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, d2.GroupId, group.Id)
|
|
require.Equal(t, d2.UserId, user.Id)
|
|
require.Equal(t, d2.CreateAt, d1.CreateAt)
|
|
require.NotZero(t, d2.DeleteAt)
|
|
|
|
// Delete an already deleted member
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Delete with non-existent User
|
|
_, err = ss.Group().DeleteMember(group.Id, model.NewId())
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Delete non-existent Group
|
|
_, err = ss.Group().DeleteMember(model.NewId(), group.Id)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupDeleteMembers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create user
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
// Create group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
guids := &model.GroupWithUserIds{
|
|
Group: *g1,
|
|
UserIds: []string{user.Id},
|
|
}
|
|
group, err := ss.Group().CreateWithUserIds(guids)
|
|
require.NoError(t, err)
|
|
|
|
// Happy path
|
|
d2, err := ss.Group().DeleteMembers(group.Id, []string{user.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, d2[0].GroupId, group.Id)
|
|
require.Equal(t, d2[0].UserId, user.Id)
|
|
require.NotZero(t, d2[0].DeleteAt)
|
|
|
|
// Delete an already deleted member
|
|
_, err = ss.Group().DeleteMembers(group.Id, []string{user.Id})
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Delete with non-existent User
|
|
_, err = ss.Group().DeleteMembers(group.Id, []string{model.NewId()})
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Delete non-existent Group
|
|
_, err = ss.Group().DeleteMembers(model.NewId(), []string{user.Id})
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGroupPermanentDeleteMembersByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
var g *model.Group
|
|
var groups []*model.Group
|
|
numberOfGroups := 5
|
|
|
|
for range numberOfGroups {
|
|
g = &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g)
|
|
groups = append(groups, group)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create user
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
// Create members
|
|
for _, group := range groups {
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Happy path
|
|
err = ss.Group().PermanentDeleteMembersByUser(user.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testCreateGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Invalid GroupID
|
|
_, err := ss.Group().CreateGroupSyncable(model.NewGroupTeam("x", model.NewId(), false))
|
|
var appErr *model.AppError
|
|
require.True(t, errors.As(err, &appErr))
|
|
require.Equal(t, appErr.Id, "model.group_syncable.group_id.app_error")
|
|
|
|
// Create Group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create Team
|
|
t1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(t1)
|
|
require.NoError(t, nErr)
|
|
|
|
// New GroupSyncable, happy path
|
|
gt1 := model.NewGroupTeam(group.Id, team.Id, false)
|
|
d1, err := ss.Group().CreateGroupSyncable(gt1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, gt1.SyncableId, d1.SyncableId)
|
|
require.Equal(t, gt1.GroupId, d1.GroupId)
|
|
require.Equal(t, gt1.AutoAdd, d1.AutoAdd)
|
|
require.NotZero(t, d1.CreateAt)
|
|
require.Zero(t, d1.DeleteAt)
|
|
}
|
|
|
|
func testGetGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create Team
|
|
t1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(t1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupSyncable
|
|
gt1 := model.NewGroupTeam(group.Id, team.Id, false)
|
|
groupTeam, err := ss.Group().CreateGroupSyncable(gt1)
|
|
require.NoError(t, err)
|
|
|
|
// Get GroupSyncable
|
|
dgt, err := ss.Group().GetGroupSyncable(groupTeam.GroupId, groupTeam.SyncableId, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.Equal(t, gt1.GroupId, dgt.GroupId)
|
|
require.Equal(t, gt1.SyncableId, dgt.SyncableId)
|
|
require.Equal(t, gt1.AutoAdd, dgt.AutoAdd)
|
|
require.NotZero(t, gt1.CreateAt)
|
|
require.NotZero(t, gt1.UpdateAt)
|
|
require.Zero(t, gt1.DeleteAt)
|
|
}
|
|
|
|
func testGetGroupSyncableErrors(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create a group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Test with invalid syncable type
|
|
invalidSyncableType := model.GroupSyncableType("invalid")
|
|
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), invalidSyncableType)
|
|
require.Error(t, err)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
|
|
|
|
// Test with empty group ID
|
|
_, err = ss.Group().GetGroupSyncable("", model.NewId(), model.GroupSyncableTypeTeam)
|
|
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
|
|
|
|
// Test with empty syncable ID
|
|
_, err = ss.Group().GetGroupSyncable(group.Id, "", model.GroupSyncableTypeTeam)
|
|
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
|
|
|
|
// Test with completely non-existent IDs
|
|
randomGroupId := model.NewId()
|
|
randomTeamId := model.NewId()
|
|
_, err = ss.Group().GetGroupSyncable(randomGroupId, randomTeamId, model.GroupSyncableTypeTeam)
|
|
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
|
|
|
|
// Test with valid group ID but non-existent syncable
|
|
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), model.GroupSyncableTypeTeam)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
}
|
|
|
|
func testGetAllGroupSyncablesByGroup(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("team", func(t *testing.T) { testGetAllGroupSyncablesByGroupTeam(t, rctx, ss) })
|
|
t.Run("channel", func(t *testing.T) { testGetAllGroupSyncablesByGroupChannel(t, rctx, ss) })
|
|
}
|
|
|
|
func testGetAllGroupSyncablesByGroupTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
numGroupSyncables := 10
|
|
|
|
// Create group
|
|
g := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g)
|
|
require.NoError(t, err)
|
|
|
|
groupTeams := []*model.GroupSyncable{}
|
|
|
|
// Create groupTeams
|
|
for range numGroupSyncables {
|
|
// Create Team
|
|
t1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
var team *model.Team
|
|
team, nErr := ss.Team().Save(t1)
|
|
require.NoError(t, nErr)
|
|
|
|
// create groupteam
|
|
var groupTeam *model.GroupSyncable
|
|
gt := model.NewGroupTeam(group.Id, team.Id, false)
|
|
gt.SchemeAdmin = true
|
|
groupTeam, err = ss.Group().CreateGroupSyncable(gt)
|
|
require.NoError(t, err)
|
|
groupTeams = append(groupTeams, groupTeam)
|
|
}
|
|
|
|
// Returns all the group teams
|
|
d1, err := ss.Group().GetAllGroupSyncablesByGroupId(group.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.Len(t, d1, numGroupSyncables)
|
|
for _, expectedGroupTeam := range groupTeams {
|
|
present := false
|
|
for _, dbGroupTeam := range d1 {
|
|
if dbGroupTeam.GroupId == expectedGroupTeam.GroupId && dbGroupTeam.SyncableId == expectedGroupTeam.SyncableId {
|
|
require.True(t, dbGroupTeam.SchemeAdmin)
|
|
present = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, present)
|
|
}
|
|
}
|
|
|
|
func testGetAllGroupSyncablesByGroupChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(team)
|
|
require.NoError(t, nErr)
|
|
|
|
numGroupSyncables := 10
|
|
|
|
// Create group
|
|
g := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g)
|
|
require.NoError(t, err)
|
|
|
|
groupChannels := []*model.GroupSyncable{}
|
|
|
|
// Create groupChannels
|
|
for range numGroupSyncables {
|
|
// Create Channel
|
|
channel := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channel, nErr = ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create groupChannel
|
|
groupChannel := model.NewGroupChannel(group.Id, channel.Id, false)
|
|
groupChannel.SchemeAdmin = true
|
|
groupChannel, err = ss.Group().CreateGroupSyncable(groupChannel)
|
|
require.NoError(t, err)
|
|
groupChannels = append(groupChannels, groupChannel)
|
|
}
|
|
|
|
// Returns all the group channels
|
|
groupSyncables, err := ss.Group().GetAllGroupSyncablesByGroupId(group.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.Len(t, groupSyncables, numGroupSyncables)
|
|
for _, expectedGroupChannel := range groupChannels {
|
|
present := false
|
|
for _, dbGroupChannel := range groupSyncables {
|
|
if dbGroupChannel.GroupId == expectedGroupChannel.GroupId && dbGroupChannel.SyncableId == expectedGroupChannel.SyncableId {
|
|
require.True(t, dbGroupChannel.SchemeAdmin)
|
|
present = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, present)
|
|
}
|
|
}
|
|
|
|
func testUpdateGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create Team
|
|
t1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(t1)
|
|
require.NoError(t, nErr)
|
|
|
|
// New GroupSyncable, happy path
|
|
gt1 := model.NewGroupTeam(group.Id, team.Id, false)
|
|
d1, err := ss.Group().CreateGroupSyncable(gt1)
|
|
require.NoError(t, err)
|
|
|
|
// Update existing group team
|
|
gt1.AutoAdd = true
|
|
d2, err := ss.Group().UpdateGroupSyncable(gt1)
|
|
require.NoError(t, err)
|
|
require.True(t, d2.AutoAdd)
|
|
|
|
// Non-existent Group
|
|
gt2 := model.NewGroupTeam(model.NewId(), team.Id, false)
|
|
_, err = ss.Group().UpdateGroupSyncable(gt2)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Non-existent Team
|
|
gt3 := model.NewGroupTeam(group.Id, model.NewId(), false)
|
|
_, err = ss.Group().UpdateGroupSyncable(gt3)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Cannot update CreateAt or DeleteAt
|
|
origCreateAt := d1.CreateAt
|
|
d1.CreateAt = model.GetMillis()
|
|
d1.AutoAdd = true
|
|
d3, err := ss.Group().UpdateGroupSyncable(d1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, origCreateAt, d3.CreateAt)
|
|
|
|
// Cannot update DeleteAt to arbitrary value
|
|
d1.DeleteAt = 1
|
|
_, err = ss.Group().UpdateGroupSyncable(d1)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "DeleteAt should be 0 when updating")
|
|
|
|
// Can update DeleteAt to 0
|
|
d1.DeleteAt = 0
|
|
d4, err := ss.Group().UpdateGroupSyncable(d1)
|
|
require.NoError(t, err)
|
|
require.Zero(t, d4.DeleteAt)
|
|
}
|
|
|
|
func testDeleteGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Group
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
// Create Team
|
|
t1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(t1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupSyncable
|
|
gt1 := model.NewGroupTeam(group.Id, team.Id, false)
|
|
groupTeam, err := ss.Group().CreateGroupSyncable(gt1)
|
|
require.NoError(t, err)
|
|
|
|
// Non-existent Group
|
|
_, err = ss.Group().DeleteGroupSyncable(model.NewId(), groupTeam.SyncableId, model.GroupSyncableTypeTeam)
|
|
var nfErr *store.ErrNotFound
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Non-existent Team
|
|
_, err = ss.Group().DeleteGroupSyncable(groupTeam.GroupId, model.NewId(), model.GroupSyncableTypeTeam)
|
|
require.True(t, errors.As(err, &nfErr))
|
|
|
|
// Happy path...
|
|
d1, err := ss.Group().DeleteGroupSyncable(groupTeam.GroupId, groupTeam.SyncableId, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.NotZero(t, d1.DeleteAt)
|
|
require.Equal(t, d1.GroupId, groupTeam.GroupId)
|
|
require.Equal(t, d1.SyncableId, groupTeam.SyncableId)
|
|
require.Equal(t, d1.AutoAdd, groupTeam.AutoAdd)
|
|
require.Equal(t, d1.CreateAt, groupTeam.CreateAt)
|
|
require.Condition(t, func() bool { return d1.UpdateAt >= groupTeam.UpdateAt }, d1.UpdateAt, ">=", groupTeam.UpdateAt)
|
|
|
|
// Record already deleted
|
|
_, err = ss.Group().DeleteGroupSyncable(d1.GroupId, d1.SyncableId, d1.Type)
|
|
require.Error(t, err)
|
|
var invErr *store.ErrInvalidInput
|
|
require.True(t, errors.As(err, &invErr))
|
|
}
|
|
|
|
func testTeamMembersToAdd(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Group
|
|
group, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "TeamMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create User
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, user)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupMember
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Create Team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr = ss.Team().Save(team)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupTeam
|
|
syncable, err := ss.Group().CreateGroupSyncable(model.NewGroupTeam(group.Id, team.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
// Time before syncable was created
|
|
teamMembers, err := ss.Group().TeamMembersToAdd(syncable.CreateAt-1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
require.Equal(t, user.Id, teamMembers[0].UserID)
|
|
require.Equal(t, team.Id, teamMembers[0].TeamID)
|
|
|
|
// Time after syncable was created
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// Delete and restore GroupMember should return result
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(syncable.CreateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
pristineSyncable := *syncable
|
|
|
|
_, err = ss.Group().UpdateGroupSyncable(syncable)
|
|
require.NoError(t, err)
|
|
|
|
// Time before syncable was updated
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt-1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
require.Equal(t, user.Id, teamMembers[0].UserID)
|
|
require.Equal(t, team.Id, teamMembers[0].TeamID)
|
|
|
|
// Time after syncable was updated
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(syncable.UpdateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// Only includes if auto-add
|
|
syncable.AutoAdd = false
|
|
_, err = ss.Group().UpdateGroupSyncable(syncable)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// reset state of syncable and verify
|
|
_, err = ss.Group().UpdateGroupSyncable(&pristineSyncable)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
// No result if Group deleted
|
|
_, err = ss.Group().Delete(group.Id)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// reset state of group and verify
|
|
group.DeleteAt = 0
|
|
_, err = ss.Group().Update(group)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
// No result if Team deleted
|
|
team.DeleteAt = model.GetMillis()
|
|
team, nErr = ss.Team().Update(team)
|
|
require.NoError(t, nErr)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// reset state of team and verify
|
|
team.DeleteAt = 0
|
|
team, nErr = ss.Team().Update(team)
|
|
require.NoError(t, nErr)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
// No result if GroupTeam deleted
|
|
_, err = ss.Group().DeleteGroupSyncable(group.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// reset GroupTeam and verify
|
|
_, err = ss.Group().UpdateGroupSyncable(&pristineSyncable)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
// No result if GroupMember deleted
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// restore group member and verify
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
|
|
// adding team membership stops returning result
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{
|
|
TeamId: team.Id,
|
|
UserId: user.Id,
|
|
}, 999)
|
|
require.NoError(t, nErr)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// Leaving Team should still not return result
|
|
_, nErr = ss.Team().UpdateMember(rctx, &model.TeamMember{
|
|
TeamId: team.Id,
|
|
UserId: user.Id,
|
|
DeleteAt: model.GetMillis(),
|
|
})
|
|
require.NoError(t, nErr)
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, teamMembers)
|
|
|
|
// If reAddRemovedMembers is set to true, removed members should be added back in
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, nil, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
}
|
|
|
|
func testTeamMembersToAddSingleTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "TeamMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "TeamMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, user1)
|
|
require.NoError(t, nErr)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr = ss.User().Save(rctx, user2)
|
|
require.NoError(t, nErr)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, nErr = ss.User().Save(rctx, user3)
|
|
require.NoError(t, nErr)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
_, err = ss.Group().UpsertMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
_, err = ss.Group().UpsertMember(group2.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
team1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team1, nErr = ss.Team().Save(team1)
|
|
require.NoError(t, nErr)
|
|
|
|
team2 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team2, nErr = ss.Team().Save(team2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group1.Id, team1.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group2.Id, team2.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
teamMembers, err := ss.Group().TeamMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 3)
|
|
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, &team1.Id, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 2)
|
|
|
|
teamMembers, err = ss.Group().TeamMembersToAdd(0, &team2.Id, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
}
|
|
|
|
func testChannelMembersToAdd(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Group
|
|
group, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "ChannelMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create User
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, nErr := ss.User().Save(rctx, user)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupMember
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Create Channel
|
|
channel := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen, // Query does not look at type so this shouldn't matter.
|
|
}
|
|
channel, nErr = ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create GroupChannel
|
|
syncable, err := ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, channel.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
// Time before syncable was created
|
|
channelMembers, err := ss.Group().ChannelMembersToAdd(syncable.CreateAt-1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
require.Equal(t, user.Id, channelMembers[0].UserID)
|
|
require.Equal(t, channel.Id, channelMembers[0].ChannelID)
|
|
|
|
// Time after syncable was created
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// Delete and restore GroupMember should return result
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.CreateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
pristineSyncable := *syncable
|
|
|
|
_, err = ss.Group().UpdateGroupSyncable(syncable)
|
|
require.NoError(t, err)
|
|
|
|
// Time before syncable was updated
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt-1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
require.Equal(t, user.Id, channelMembers[0].UserID)
|
|
require.Equal(t, channel.Id, channelMembers[0].ChannelID)
|
|
|
|
// Time after syncable was updated
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(syncable.UpdateAt+1, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// Only includes if auto-add
|
|
syncable.AutoAdd = false
|
|
_, err = ss.Group().UpdateGroupSyncable(syncable)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// reset state of syncable and verify
|
|
_, err = ss.Group().UpdateGroupSyncable(&pristineSyncable)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// No result if Group deleted
|
|
_, err = ss.Group().Delete(group.Id)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// reset state of group and verify
|
|
group.DeleteAt = 0
|
|
_, err = ss.Group().Update(group)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// No result if Channel deleted
|
|
nErr = ss.Channel().Delete(channel.Id, model.GetMillis())
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// reset state of channel and verify
|
|
channel.DeleteAt = 0
|
|
_, nErr = ss.Channel().Update(rctx, channel)
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// No result if GroupChannel deleted
|
|
_, err = ss.Group().DeleteGroupSyncable(group.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// reset GroupChannel and verify
|
|
_, err = ss.Group().UpdateGroupSyncable(&pristineSyncable)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// No result if GroupMember deleted
|
|
_, err = ss.Group().DeleteMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// restore group member and verify
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// Adding Channel (ChannelMemberHistory) should stop returning result
|
|
nErr = ss.ChannelMemberHistory().LogJoinEvent(user.Id, channel.Id, model.GetMillis())
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// Leaving Channel (ChannelMemberHistory) should still not return result
|
|
nErr = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, model.GetMillis())
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, channelMembers)
|
|
|
|
// Purging ChannelMemberHistory re-returns the result
|
|
_, _, nErr = ss.ChannelMemberHistory().PermanentDeleteBatchForRetentionPolicies(model.RetentionPolicyBatchConfigs{
|
|
Now: 0,
|
|
GlobalPolicyEndTime: model.GetMillis() + 1,
|
|
Limit: 100,
|
|
}, model.RetentionPolicyCursor{})
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
|
|
// If reAddRemovedMembers is set to true, removed members should be added back in
|
|
nErr = ss.ChannelMemberHistory().LogLeaveEvent(user.Id, channel.Id, model.GetMillis())
|
|
require.NoError(t, nErr)
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, nil, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
}
|
|
|
|
func testChannelMembersToAddSingleChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "TeamMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "TeamMembersToAdd Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, user1)
|
|
require.NoError(t, nErr)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr = ss.User().Save(rctx, user2)
|
|
require.NoError(t, nErr)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, nErr = ss.User().Save(rctx, user3)
|
|
require.NoError(t, nErr)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
_, err = ss.Group().UpsertMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
_, err = ss.Group().UpsertMember(group2.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
channel1 := &model.Channel{
|
|
DisplayName: "Name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel1, nErr = ss.Channel().Save(rctx, channel1, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
channel2 := &model.Channel{
|
|
DisplayName: "Name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel2, nErr = ss.Channel().Save(rctx, channel2, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group1.Id, channel1.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group2.Id, channel2.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
channelMembers, err := ss.Group().ChannelMembersToAdd(0, nil, false)
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, len(channelMembers), 3)
|
|
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, &channel1.Id, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 2)
|
|
|
|
channelMembers, err = ss.Group().ChannelMembersToAdd(0, &channel2.Id, false)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
}
|
|
|
|
func testTeamMembersToRemove(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
data := pendingMemberRemovalsDataSetup(t, rctx, ss)
|
|
|
|
// one result when both users are in the group (for user C)
|
|
teamMembers, err := ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
require.Equal(t, data.UserC.Id, teamMembers[0].UserId)
|
|
|
|
_, err = ss.Group().DeleteMember(data.Group.Id, data.UserB.Id)
|
|
require.NoError(t, err)
|
|
|
|
// user b and c should now be returned
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 2)
|
|
|
|
var userIDs []string
|
|
for _, item := range teamMembers {
|
|
userIDs = append(userIDs, item.UserId)
|
|
}
|
|
require.Contains(t, userIDs, data.UserB.Id)
|
|
require.Contains(t, userIDs, data.UserC.Id)
|
|
require.Equal(t, data.ConstrainedTeam.Id, teamMembers[0].TeamId)
|
|
require.Equal(t, data.ConstrainedTeam.Id, teamMembers[1].TeamId)
|
|
|
|
_, err = ss.Group().DeleteMember(data.Group.Id, data.UserA.Id)
|
|
require.NoError(t, err)
|
|
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 3)
|
|
|
|
// Make one of them a bot
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
teamMember := teamMembers[0]
|
|
bot := &model.Bot{
|
|
UserId: teamMember.UserId,
|
|
Username: "un_" + model.NewId(),
|
|
DisplayName: "dn_" + model.NewId(),
|
|
OwnerId: teamMember.UserId,
|
|
}
|
|
bot, nErr := ss.Bot().Save(bot)
|
|
require.NoError(t, nErr)
|
|
|
|
// verify that bot is not returned in results
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 2)
|
|
|
|
// delete the bot
|
|
nErr = ss.Bot().PermanentDelete(bot.UserId)
|
|
require.NoError(t, nErr)
|
|
|
|
// Should be back to 3 users
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 3)
|
|
|
|
// add users back to groups
|
|
res := ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserA.Id)
|
|
require.NoError(t, res)
|
|
res = ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserB.Id)
|
|
require.NoError(t, res)
|
|
res = ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserC.Id)
|
|
require.NoError(t, res)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserA.Id)
|
|
require.NoError(t, nErr)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserB.Id)
|
|
require.NoError(t, nErr)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserC.Id)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
func testTeamMembersToRemoveSingleTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
|
|
team1 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
team1, nErr := ss.Team().Save(team1)
|
|
require.NoError(t, nErr)
|
|
|
|
team2 := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
team2, nErr = ss.Team().Save(team2)
|
|
require.NoError(t, nErr)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{
|
|
TeamId: team1.Id,
|
|
UserId: user.Id,
|
|
}, 999)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{
|
|
TeamId: team2.Id,
|
|
UserId: user3.Id,
|
|
}, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
teamMembers, err := ss.Group().TeamMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 3)
|
|
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(&team1.Id)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 2)
|
|
|
|
teamMembers, err = ss.Group().TeamMembersToRemove(&team2.Id)
|
|
require.NoError(t, err)
|
|
require.Len(t, teamMembers, 1)
|
|
}
|
|
|
|
func testChannelMembersToRemove(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
data := pendingMemberRemovalsDataSetup(t, rctx, ss)
|
|
|
|
// one result when both users are in the group (for user C)
|
|
channelMembers, err := ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
require.Equal(t, data.UserC.Id, channelMembers[0].UserId)
|
|
|
|
_, err = ss.Group().DeleteMember(data.Group.Id, data.UserB.Id)
|
|
require.NoError(t, err)
|
|
|
|
// user b and c should now be returned
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 2)
|
|
|
|
var userIDs []string
|
|
for _, item := range channelMembers {
|
|
userIDs = append(userIDs, item.UserId)
|
|
}
|
|
require.Contains(t, userIDs, data.UserB.Id)
|
|
require.Contains(t, userIDs, data.UserC.Id)
|
|
require.Equal(t, data.ConstrainedChannel.Id, channelMembers[0].ChannelId)
|
|
require.Equal(t, data.ConstrainedChannel.Id, channelMembers[1].ChannelId)
|
|
|
|
_, err = ss.Group().DeleteMember(data.Group.Id, data.UserA.Id)
|
|
require.NoError(t, err)
|
|
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 3)
|
|
|
|
// Make one of them a bot
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
channelMember := channelMembers[0]
|
|
bot := &model.Bot{
|
|
UserId: channelMember.UserId,
|
|
Username: "un_" + model.NewId(),
|
|
DisplayName: "dn_" + model.NewId(),
|
|
OwnerId: channelMember.UserId,
|
|
}
|
|
bot, nErr := ss.Bot().Save(bot)
|
|
require.NoError(t, nErr)
|
|
|
|
// verify that bot is not returned in results
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 2)
|
|
|
|
// delete the bot
|
|
nErr = ss.Bot().PermanentDelete(bot.UserId)
|
|
require.NoError(t, nErr)
|
|
|
|
// Should be back to 3 users
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 3)
|
|
|
|
// add users back to groups
|
|
res := ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserA.Id)
|
|
require.NoError(t, res)
|
|
res = ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserB.Id)
|
|
require.NoError(t, res)
|
|
res = ss.Team().RemoveMember(rctx, data.ConstrainedTeam.Id, data.UserC.Id)
|
|
require.NoError(t, res)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserA.Id)
|
|
require.NoError(t, nErr)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserB.Id)
|
|
require.NoError(t, nErr)
|
|
nErr = ss.Channel().RemoveMember(rctx, data.ConstrainedChannel.Id, data.UserC.Id)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
func testChannelMembersToRemoveSingleChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
|
|
channel1 := &model.Channel{
|
|
DisplayName: "Name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
channel1, nErr := ss.Channel().Save(rctx, channel1, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
channel2 := &model.Channel{
|
|
DisplayName: "Name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
channel2, nErr = ss.Channel().Save(rctx, channel2, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel1.Id,
|
|
UserId: user.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel2.Id,
|
|
UserId: user3.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, nErr)
|
|
|
|
channelMembers, err := ss.Group().ChannelMembersToRemove(nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 3)
|
|
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(&channel1.Id)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 2)
|
|
|
|
channelMembers, err = ss.Group().ChannelMembersToRemove(&channel2.Id)
|
|
require.NoError(t, err)
|
|
require.Len(t, channelMembers, 1)
|
|
}
|
|
|
|
type removalsData struct {
|
|
UserA *model.User
|
|
UserB *model.User
|
|
UserC *model.User
|
|
ConstrainedChannel *model.Channel
|
|
UnconstrainedChannel *model.Channel
|
|
ConstrainedTeam *model.Team
|
|
UnconstrainedTeam *model.Team
|
|
Group *model.Group
|
|
}
|
|
|
|
func pendingMemberRemovalsDataSetup(t *testing.T, rctx request.CTX, ss store.Store) *removalsData {
|
|
// create group
|
|
group, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "Pending[Channel|Team]MemberRemovals Test Group",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// create users
|
|
// userA will get removed from the group
|
|
userA := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
userA, nErr := ss.User().Save(rctx, userA)
|
|
require.NoError(t, nErr)
|
|
|
|
// userB will not get removed from the group
|
|
userB := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
userB, nErr = ss.User().Save(rctx, userB)
|
|
require.NoError(t, nErr)
|
|
|
|
// userC was never in the group
|
|
userC := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
userC, nErr = ss.User().Save(rctx, userC)
|
|
require.NoError(t, nErr)
|
|
|
|
// add users to group (but not userC)
|
|
_, err = ss.Group().UpsertMember(group.Id, userA.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, userB.Id)
|
|
require.NoError(t, err)
|
|
|
|
// create channels
|
|
channelConstrained := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
channelConstrained, nErr = ss.Channel().Save(rctx, channelConstrained, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
channelUnconstrained := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channelUnconstrained, nErr = ss.Channel().Save(rctx, channelUnconstrained, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// create teams
|
|
teamConstrained := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamInvite,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
teamConstrained, nErr = ss.Team().Save(teamConstrained)
|
|
require.NoError(t, nErr)
|
|
|
|
teamUnconstrained := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid1",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamInvite,
|
|
}
|
|
teamUnconstrained, nErr = ss.Team().Save(teamUnconstrained)
|
|
require.NoError(t, nErr)
|
|
|
|
// create groupteams
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group.Id, teamConstrained.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupTeam(group.Id, teamUnconstrained.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
// create groupchannels
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, channelConstrained.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, channelUnconstrained.Id, true))
|
|
require.NoError(t, err)
|
|
|
|
// add users to teams
|
|
userIDTeamIDs := [][]string{
|
|
{userA.Id, teamConstrained.Id},
|
|
{userB.Id, teamConstrained.Id},
|
|
{userC.Id, teamConstrained.Id},
|
|
{userA.Id, teamUnconstrained.Id},
|
|
{userB.Id, teamUnconstrained.Id},
|
|
{userC.Id, teamUnconstrained.Id},
|
|
}
|
|
|
|
for _, item := range userIDTeamIDs {
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{
|
|
UserId: item[0],
|
|
TeamId: item[1],
|
|
}, 99)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
// add users to channels
|
|
userIDChannelIDs := [][]string{
|
|
{userA.Id, channelConstrained.Id},
|
|
{userB.Id, channelConstrained.Id},
|
|
{userC.Id, channelConstrained.Id},
|
|
{userA.Id, channelUnconstrained.Id},
|
|
{userB.Id, channelUnconstrained.Id},
|
|
{userC.Id, channelUnconstrained.Id},
|
|
}
|
|
|
|
for _, item := range userIDChannelIDs {
|
|
_, err := ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
UserId: item[0],
|
|
ChannelId: item[1],
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
return &removalsData{
|
|
UserA: userA,
|
|
UserB: userB,
|
|
UserC: userC,
|
|
ConstrainedChannel: channelConstrained,
|
|
UnconstrainedChannel: channelUnconstrained,
|
|
ConstrainedTeam: teamConstrained,
|
|
UnconstrainedTeam: teamUnconstrained,
|
|
Group: group,
|
|
}
|
|
}
|
|
|
|
func testGetGroupsByChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Channel1
|
|
channel1 := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "Channel1",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel1, err := ss.Channel().Save(rctx, channel1, 9999)
|
|
require.NoError(t, err)
|
|
|
|
// Create Groups 1, 2 and a deleted group
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-1",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-2",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
deletedGroup, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-deleted",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
DeleteAt: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate them with Channel1
|
|
for _, g := range []*model.Group{group1, group2, deletedGroup} {
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel1.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: g.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create Channel2
|
|
channel2 := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "Channel2",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel2, nErr := ss.Channel().Save(rctx, channel2, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create Group3
|
|
group3, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-3",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate it to Channel2
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel2.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// add members
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
user2.DeleteAt = 1
|
|
_, err = ss.User().Update(rctx, user2, true)
|
|
require.NoError(t, err)
|
|
|
|
group1WithMemberCount := *group1
|
|
group1WithMemberCount.MemberCount = model.NewPointer(1)
|
|
|
|
group2WithMemberCount := *group2
|
|
group2WithMemberCount.MemberCount = model.NewPointer(0)
|
|
|
|
group1WSA := &model.GroupWithSchemeAdmin{Group: *group1, SchemeAdmin: model.NewPointer(false)}
|
|
group2WSA := &model.GroupWithSchemeAdmin{Group: *group2, SchemeAdmin: model.NewPointer(false)}
|
|
group3WSA := &model.GroupWithSchemeAdmin{Group: *group3, SchemeAdmin: model.NewPointer(false)}
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
ChannelId string
|
|
Page int
|
|
PerPage int
|
|
Result []*model.GroupWithSchemeAdmin
|
|
Opts model.GroupSearchOpts
|
|
TotalCount *int64
|
|
}{
|
|
{
|
|
Name: "Get the two Groups for Channel1",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA, group2WSA},
|
|
TotalCount: model.NewPointer(int64(2)),
|
|
},
|
|
{
|
|
Name: "Get first Group for Channel1 with page 0 with 1 element",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
},
|
|
{
|
|
Name: "Get second Group for Channel1 with page 1 with 1 element",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 1,
|
|
PerPage: 1,
|
|
Result: []*model.GroupWithSchemeAdmin{group2WSA},
|
|
},
|
|
{
|
|
Name: "Get third Group for Channel2",
|
|
ChannelId: channel2.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{group3WSA},
|
|
},
|
|
{
|
|
Name: "Get empty Groups for a fake id",
|
|
ChannelId: model.NewId(),
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{},
|
|
TotalCount: model.NewPointer(int64(0)),
|
|
},
|
|
{
|
|
Name: "Get group matching name",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{Q: string([]rune(*group1.Name)[2:10])}, // very low change of a name collision
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
TotalCount: model.NewPointer(int64(1)),
|
|
},
|
|
{
|
|
Name: "Get group matching display name",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "rouP-1"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
TotalCount: model.NewPointer(int64(1)),
|
|
},
|
|
{
|
|
Name: "Get group matching multiple display names",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "roUp-"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA, group2WSA},
|
|
TotalCount: model.NewPointer(int64(2)),
|
|
},
|
|
{
|
|
Name: "Include member counts",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
|
|
Page: 0,
|
|
PerPage: 2,
|
|
Result: []*model.GroupWithSchemeAdmin{
|
|
{Group: group1WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
{Group: group2WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
},
|
|
},
|
|
{
|
|
Name: "Include allow reference",
|
|
ChannelId: channel1.Id,
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
if tc.Opts.PageOpts == nil {
|
|
tc.Opts.PageOpts = &model.PageOpts{}
|
|
}
|
|
tc.Opts.PageOpts.Page = tc.Page
|
|
tc.Opts.PageOpts.PerPage = tc.PerPage
|
|
groups, err := ss.Group().GetGroupsByChannel(tc.ChannelId, tc.Opts)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.Result, groups)
|
|
if tc.TotalCount != nil {
|
|
var count int64
|
|
count, err = ss.Group().CountGroupsByChannel(tc.ChannelId, tc.Opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, *tc.TotalCount, count)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testGetGroupsAssociatedToChannelsByTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Team1
|
|
team1 := &model.Team{
|
|
DisplayName: "Team1",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team1, errt := ss.Team().Save(team1)
|
|
require.NoError(t, errt)
|
|
|
|
// Create Channel1
|
|
channel1 := &model.Channel{
|
|
TeamId: team1.Id,
|
|
DisplayName: "Channel1",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel1, err := ss.Channel().Save(rctx, channel1, 9999)
|
|
require.NoError(t, err)
|
|
|
|
// Create Groups 1, 2 and a deleted group
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-1",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-2",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
deletedGroup, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-deleted",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
DeleteAt: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate them with Channel1
|
|
for _, g := range []*model.Group{group1, group2, deletedGroup} {
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel1.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: g.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create Channel2
|
|
channel2 := &model.Channel{
|
|
TeamId: team1.Id,
|
|
DisplayName: "Channel2",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel2, err = ss.Channel().Save(rctx, channel2, 9999)
|
|
require.NoError(t, err)
|
|
|
|
// Create Group3
|
|
group3, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-3",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate it to Channel2
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel2.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// add members
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
user2.DeleteAt = 1
|
|
_, err = ss.User().Update(rctx, user2, true)
|
|
require.NoError(t, err)
|
|
|
|
group1WithMemberCount := *group1
|
|
group1WithMemberCount.MemberCount = model.NewPointer(1)
|
|
|
|
group2WithMemberCount := *group2
|
|
group2WithMemberCount.MemberCount = model.NewPointer(0)
|
|
|
|
group3WithMemberCount := *group3
|
|
group3WithMemberCount.MemberCount = model.NewPointer(0)
|
|
|
|
group1WSA := &model.GroupWithSchemeAdmin{Group: *group1, SchemeAdmin: model.NewPointer(false)}
|
|
group2WSA := &model.GroupWithSchemeAdmin{Group: *group2, SchemeAdmin: model.NewPointer(false)}
|
|
group3WSA := &model.GroupWithSchemeAdmin{Group: *group3, SchemeAdmin: model.NewPointer(false)}
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
TeamId string
|
|
Page int
|
|
PerPage int
|
|
Result map[string][]*model.GroupWithSchemeAdmin
|
|
Opts model.GroupSearchOpts
|
|
}{
|
|
{
|
|
Name: "Get the groups for Channel1 and Channel2",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group1WSA, group2WSA}, channel2.Id: {group3WSA}},
|
|
},
|
|
{
|
|
Name: "Get first Group for Channel1 with page 0 with 1 element",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group1WSA}},
|
|
},
|
|
{
|
|
Name: "Get second Group for Channel1 with page 1 with 1 element",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 1,
|
|
PerPage: 1,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group2WSA}},
|
|
},
|
|
{
|
|
Name: "Get empty Groups for a fake id",
|
|
TeamId: model.NewId(),
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{},
|
|
},
|
|
{
|
|
Name: "Get group matching name",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: string([]rune(*group1.Name)[2:10])}, // very low chance of a name collision
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group1WSA}},
|
|
},
|
|
{
|
|
Name: "Get group matching display name",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "rouP-1"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group1WSA}},
|
|
},
|
|
{
|
|
Name: "Get group matching multiple display names",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "roUp-"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{channel1.Id: {group1WSA, group2WSA}, channel2.Id: {group3WSA}},
|
|
},
|
|
{
|
|
Name: "Include member counts",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{
|
|
channel1.Id: {
|
|
{Group: group1WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
{Group: group2WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
},
|
|
channel2.Id: {
|
|
{Group: group3WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "Include allow reference",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true},
|
|
Page: 0,
|
|
PerPage: 2,
|
|
Result: map[string][]*model.GroupWithSchemeAdmin{
|
|
channel1.Id: {
|
|
group2WSA,
|
|
},
|
|
channel2.Id: {
|
|
group3WSA,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
if tc.Opts.PageOpts == nil {
|
|
tc.Opts.PageOpts = &model.PageOpts{}
|
|
}
|
|
tc.Opts.PageOpts.Page = tc.Page
|
|
tc.Opts.PageOpts.PerPage = tc.PerPage
|
|
groups, err := ss.Group().GetGroupsAssociatedToChannelsByTeam(tc.TeamId, tc.Opts)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.Result, groups)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testGetGroupsByTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Team1
|
|
team1 := &model.Team{
|
|
DisplayName: "Team1",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team1, err := ss.Team().Save(team1)
|
|
require.NoError(t, err)
|
|
|
|
// Create Groups 1, 2 and a deleted group
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-1",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-2",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
deletedGroup, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-deleted",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
DeleteAt: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate them with Team1
|
|
for _, g := range []*model.Group{group1, group2, deletedGroup} {
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team1.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: g.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create Team2
|
|
team2 := &model.Team{
|
|
DisplayName: "Team2",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamInvite,
|
|
}
|
|
team2, err = ss.Team().Save(team2)
|
|
require.NoError(t, err)
|
|
|
|
// Create Group3
|
|
group3, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-3",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate it to Team2
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team2.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// add members
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
user2.DeleteAt = 1
|
|
_, err = ss.User().Update(rctx, user2, true)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(deletedGroup.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
group1WithMemberCount := *group1
|
|
group1WithMemberCount.MemberCount = model.NewPointer(1)
|
|
|
|
group2WithMemberCount := *group2
|
|
group2WithMemberCount.MemberCount = model.NewPointer(0)
|
|
|
|
group1WSA := &model.GroupWithSchemeAdmin{Group: *group1, SchemeAdmin: model.NewPointer(false)}
|
|
group2WSA := &model.GroupWithSchemeAdmin{Group: *group2, SchemeAdmin: model.NewPointer(false)}
|
|
group3WSA := &model.GroupWithSchemeAdmin{Group: *group3, SchemeAdmin: model.NewPointer(false)}
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
TeamId string
|
|
Page int
|
|
PerPage int
|
|
Opts model.GroupSearchOpts
|
|
Result []*model.GroupWithSchemeAdmin
|
|
TotalCount *int64
|
|
}{
|
|
{
|
|
Name: "Get the two Groups for Team1",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA, group2WSA},
|
|
TotalCount: model.NewPointer(int64(2)),
|
|
},
|
|
{
|
|
Name: "Get first Group for Team1 with page 0 with 1 element",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
},
|
|
{
|
|
Name: "Get second Group for Team1 with page 1 with 1 element",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 1,
|
|
PerPage: 1,
|
|
Result: []*model.GroupWithSchemeAdmin{group2WSA},
|
|
},
|
|
{
|
|
Name: "Get third Group for Team2",
|
|
TeamId: team2.Id,
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{group3WSA},
|
|
TotalCount: model.NewPointer(int64(1)),
|
|
},
|
|
{
|
|
Name: "Get empty Groups for a fake id",
|
|
TeamId: model.NewId(),
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 60,
|
|
Result: []*model.GroupWithSchemeAdmin{},
|
|
TotalCount: model.NewPointer(int64(0)),
|
|
},
|
|
{
|
|
Name: "Get group matching name",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: string([]rune(*group1.Name)[2:10])}, // very low change of a name collision
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
TotalCount: model.NewPointer(int64(1)),
|
|
},
|
|
{
|
|
Name: "Get group matching display name",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "rouP-1"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA},
|
|
TotalCount: model.NewPointer(int64(1)),
|
|
},
|
|
{
|
|
Name: "Get group matching multiple display names",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{Q: "roUp-"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group1WSA, group2WSA},
|
|
TotalCount: model.NewPointer(int64(2)),
|
|
},
|
|
{
|
|
Name: "Include member counts",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
|
|
Page: 0,
|
|
PerPage: 2,
|
|
Result: []*model.GroupWithSchemeAdmin{
|
|
{Group: group1WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
{Group: group2WithMemberCount, SchemeAdmin: model.NewPointer(false)},
|
|
},
|
|
},
|
|
{
|
|
Name: "Include allow reference",
|
|
TeamId: team1.Id,
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Result: []*model.GroupWithSchemeAdmin{group2WSA},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
if tc.Opts.PageOpts == nil {
|
|
tc.Opts.PageOpts = &model.PageOpts{}
|
|
}
|
|
tc.Opts.PageOpts.Page = tc.Page
|
|
tc.Opts.PageOpts.PerPage = tc.PerPage
|
|
groups, err := ss.Group().GetGroupsByTeam(tc.TeamId, tc.Opts)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.Result, groups)
|
|
if tc.TotalCount != nil {
|
|
var count int64
|
|
count, err = ss.Group().CountGroupsByTeam(tc.TeamId, tc.Opts)
|
|
require.NoError(t, err)
|
|
require.Equal(t, *tc.TotalCount, count)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func testGetGroups(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create Team1
|
|
team1 := &model.Team{
|
|
DisplayName: "Team1",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
team1, err := ss.Team().Save(team1)
|
|
require.NoError(t, err)
|
|
|
|
startCreateTime := team1.UpdateAt - 1
|
|
|
|
// Create Channel1
|
|
channel1 := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "Channel1",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channel1, nErr := ss.Channel().Save(rctx, channel1, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create Groups 1 and 2
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: "group-1",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId() + "-group-2"),
|
|
DisplayName: "group-2",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
deletedGroup, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId() + "-group-deleted"),
|
|
DisplayName: "group-deleted",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: false,
|
|
DeleteAt: 1,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate them with Team1
|
|
for _, g := range []*model.Group{group1, group2, deletedGroup} {
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team1.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: g.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create Team2
|
|
team2 := &model.Team{
|
|
DisplayName: "Team2",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamInvite,
|
|
}
|
|
team2, err = ss.Team().Save(team2)
|
|
require.NoError(t, err)
|
|
|
|
// Create Channel2
|
|
channel2 := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "Channel2",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channel2, nErr = ss.Channel().Save(rctx, channel2, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create Channel3
|
|
channel3 := &model.Channel{
|
|
TeamId: team1.Id,
|
|
DisplayName: "Channel3",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channel3, nErr = ss.Channel().Save(rctx, channel3, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create Group3
|
|
group3, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId() + "-group-3"),
|
|
DisplayName: "group-3",
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
Source: model.GroupSourceLdap,
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate it to Team2
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team2.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group3.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate Group1 to Channel2
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel2.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group1.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// And associate Group2 and Group3 to Channel1
|
|
for _, g := range []*model.Group{group2, group3} {
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel1.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: g.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// add members
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, u1)
|
|
require.NoError(t, err)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err := ss.User().Save(rctx, u2)
|
|
require.NoError(t, err)
|
|
|
|
u3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
Timezone: model.StringMap{
|
|
"useAutomaticTimezone": "false",
|
|
"manualTimezone": "UTC",
|
|
},
|
|
}
|
|
user3, err := ss.User().Save(rctx, u3)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(deletedGroup.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
m1 := model.ChannelMember{
|
|
ChannelId: channel1.Id,
|
|
UserId: user1.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
_, err = ss.Channel().SaveMember(rctx, &m1)
|
|
require.NoError(t, err)
|
|
|
|
m2 := model.ChannelMember{
|
|
ChannelId: channel1.Id,
|
|
UserId: user2.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
_, err = ss.Channel().SaveMember(rctx, &m2)
|
|
require.NoError(t, err)
|
|
|
|
m3 := model.ChannelMember{
|
|
ChannelId: channel2.Id,
|
|
UserId: user2.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
_, err = ss.Channel().SaveMember(rctx, &m3)
|
|
require.NoError(t, err)
|
|
|
|
m4 := model.ChannelMember{
|
|
ChannelId: channel2.Id,
|
|
UserId: user3.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
}
|
|
_, err = ss.Channel().SaveMember(rctx, &m4)
|
|
require.NoError(t, err)
|
|
|
|
user2.DeleteAt = 1
|
|
u2Update, _ := ss.User().Update(rctx, user2, true)
|
|
|
|
group2NameSubstring := "group-2"
|
|
|
|
endCreateTime := u2Update.New.UpdateAt + 1
|
|
|
|
// Create Team3
|
|
team3 := &model.Team{
|
|
DisplayName: "Team3",
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamInvite,
|
|
}
|
|
team3, err = ss.Team().Save(team3)
|
|
require.NoError(t, err)
|
|
|
|
channel4 := &model.Channel{
|
|
TeamId: team3.Id,
|
|
DisplayName: "Channel4",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
}
|
|
channel4, nErr = ss.Channel().Save(rctx, channel4, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
Page int
|
|
PerPage int
|
|
Opts model.GroupSearchOpts
|
|
Resultf func([]*model.Group) bool
|
|
Restrictions *model.ViewUsersRestrictions
|
|
}{
|
|
{
|
|
Name: "Get all the Groups",
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 3,
|
|
Resultf: func(groups []*model.Group) bool { return len(groups) == 3 },
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get first Group with page 0 with 1 element",
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get single result from page 1",
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 1,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool { return len(groups) == 1 },
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get multiple results from page 1",
|
|
Opts: model.GroupSearchOpts{},
|
|
Page: 1,
|
|
PerPage: 2,
|
|
Resultf: func(groups []*model.Group) bool { return len(groups) == 2 },
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get group matching name",
|
|
Opts: model.GroupSearchOpts{Q: group2NameSubstring},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if !strings.Contains(*g.Name, group2NameSubstring) && !strings.Contains(g.DisplayName, group2NameSubstring) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get group matching display name",
|
|
Opts: model.GroupSearchOpts{Q: "rouP-3"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if !strings.Contains(strings.ToLower(g.DisplayName), "roup-3") {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get group matching multiple display names",
|
|
Opts: model.GroupSearchOpts{Q: "groUp"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if !strings.Contains(strings.ToLower(g.DisplayName), "group") {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include member counts",
|
|
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if g.MemberCount == nil {
|
|
return false
|
|
}
|
|
if (g.Id == group1.Id || g.Id == group2.Id) && *g.MemberCount != 1 {
|
|
return false
|
|
}
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include member counts with restrictions",
|
|
Opts: model.GroupSearchOpts{IncludeMemberCount: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if g.MemberCount == nil {
|
|
return false
|
|
}
|
|
if g.Id == group1.Id && *g.MemberCount != 1 {
|
|
return false
|
|
}
|
|
if g.Id == group2.Id && *g.MemberCount != 0 {
|
|
return false
|
|
}
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: &model.ViewUsersRestrictions{Channels: []string{channel1.Id}},
|
|
},
|
|
{
|
|
Name: "Not associated to team",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToTeam: team2.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if g.Id == group3.Id {
|
|
return false
|
|
}
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Not associated to other team",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToTeam: team1.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if g.Id == group1.Id || g.Id == group2.Id {
|
|
return false
|
|
}
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include allow reference",
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if !g.AllowReference {
|
|
return false
|
|
}
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Use Since return all",
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true, Since: startCreateTime},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if g.DeleteAt != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Use Since return none",
|
|
Opts: model.GroupSearchOpts{FilterAllowReference: true, Since: endCreateTime},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 0
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter groups from group-constrained teams",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToChannel: channel3.Id, FilterParentTeamPermitted: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 2 && groups[0].Id == group1.Id && groups[1].Id == group2.Id
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter groups from group-constrained page 0",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToChannel: channel3.Id, FilterParentTeamPermitted: true},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return groups[0].Id == group1.Id
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter groups from group-constrained page 1",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToChannel: channel3.Id, FilterParentTeamPermitted: true},
|
|
Page: 1,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return groups[0].Id == group2.Id
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Non-group constrained team with no associated groups still returns groups for the child channel",
|
|
Opts: model.GroupSearchOpts{NotAssociatedToChannel: channel4.Id, FilterParentTeamPermitted: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) > 0
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter by group member",
|
|
Opts: model.GroupSearchOpts{FilterHasMember: user1.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 1 && groups[0].Id == group1.Id
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter by non-existent group member",
|
|
Opts: model.GroupSearchOpts{FilterHasMember: model.NewId()},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 0
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Filter by non-member member",
|
|
Opts: model.GroupSearchOpts{FilterHasMember: user2.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 2
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include syncable sources only",
|
|
Opts: model.GroupSearchOpts{OnlySyncableSources: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if g.Source != model.GroupSourceLdap && !strings.HasPrefix(string(g.Source), "plugin_") {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include syncable sources with specific source",
|
|
Opts: model.GroupSearchOpts{OnlySyncableSources: true, Source: model.GroupSourceLdap},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, g := range groups {
|
|
if g.Source != model.GroupSourceLdap {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include archived groups",
|
|
Opts: model.GroupSearchOpts{IncludeArchived: true, Q: "group-deleted"},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 1
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Only return archived groups",
|
|
Opts: model.GroupSearchOpts{FilterArchived: true, Q: "group-1"},
|
|
Page: 0,
|
|
PerPage: 1,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
return len(groups) == 0
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel1 member count",
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: channel1.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberCount int
|
|
if group.ChannelMemberCount != nil {
|
|
channelMemberCount = *group.ChannelMemberCount
|
|
}
|
|
if group.Id == group1.Id && channelMemberCount != 2 {
|
|
return false
|
|
}
|
|
if group.Id == group2.Id && channelMemberCount != 1 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel2 member count",
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: channel2.Id},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberCount int
|
|
if group.ChannelMemberCount != nil {
|
|
channelMemberCount = *group.ChannelMemberCount
|
|
}
|
|
if group.Id == group1.Id && channelMemberCount != 1 {
|
|
return false
|
|
}
|
|
if group.Id == group2.Id && channelMemberCount != 2 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel member count for non-existent channel",
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: model.NewId()},
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberCount int
|
|
if group.ChannelMemberCount != nil {
|
|
channelMemberCount = *group.ChannelMemberCount
|
|
}
|
|
|
|
if channelMemberCount != 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel1 member count, with timezones",
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: channel1.Id, IncludeTimezones: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberTimezonesCount int
|
|
if group.ChannelMemberTimezonesCount != nil {
|
|
channelMemberTimezonesCount = *group.ChannelMemberTimezonesCount
|
|
}
|
|
if group.Id == group1.Id && channelMemberTimezonesCount != 0 {
|
|
return false
|
|
}
|
|
if group.Id == group2.Id && channelMemberTimezonesCount != 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel2 member count, with timezones",
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: channel2.Id, IncludeTimezones: true},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberTimezonesCount int
|
|
if group.ChannelMemberTimezonesCount != nil {
|
|
channelMemberTimezonesCount = *group.ChannelMemberTimezonesCount
|
|
}
|
|
if group.Id == group1.Id && channelMemberTimezonesCount != 0 {
|
|
return false
|
|
}
|
|
if group.Id == group2.Id && channelMemberTimezonesCount != 1 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Include channel member count for non-existent channel, with timezones",
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Opts: model.GroupSearchOpts{IncludeChannelMemberCount: model.NewId(), IncludeTimezones: true},
|
|
Resultf: func(groups []*model.Group) bool {
|
|
for _, group := range groups {
|
|
var channelMemberTimezonesCount int
|
|
if group.ChannelMemberTimezonesCount != nil {
|
|
channelMemberTimezonesCount = *group.ChannelMemberCount
|
|
}
|
|
|
|
if channelMemberTimezonesCount != 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
groups, err := ss.Group().GetGroups(tc.Page, tc.PerPage, tc.Opts, tc.Restrictions)
|
|
require.NoError(t, err)
|
|
require.True(t, tc.Resultf(groups))
|
|
})
|
|
}
|
|
}
|
|
|
|
func testTeamMembersMinusGroupMembers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
const numberOfGroups = 3
|
|
const numberOfUsers = 4
|
|
|
|
groups := []*model.Group{}
|
|
users := []*model.User{}
|
|
|
|
team := &model.Team{
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
CompanyName: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
team, err := ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
for i := range numberOfUsers {
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: fmt.Sprintf("a%d_%s", i, model.NewId()),
|
|
}
|
|
user, err = ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
users = append(users, user)
|
|
|
|
trueOrFalse := int(math.Mod(float64(i), 2)) == 0
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user.Id, SchemeUser: trueOrFalse, SchemeAdmin: !trueOrFalse}, 999)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
// Extra user outside of the group member users.
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "aa_" + model.NewId(),
|
|
}
|
|
user, err = ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
users = append(users, user)
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user.Id, SchemeUser: true, SchemeAdmin: false}, 999)
|
|
require.NoError(t, nErr)
|
|
|
|
for i := range numberOfGroups {
|
|
group := &model.Group{
|
|
Name: model.NewPointer(fmt.Sprintf("n_%d_%s", i, model.NewId())),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(group)
|
|
require.NoError(t, err)
|
|
groups = append(groups, group)
|
|
}
|
|
|
|
sort.Slice(users, func(i, j int) bool {
|
|
return users[i].Username < users[j].Username
|
|
})
|
|
|
|
// Add even users to even group, and the inverse
|
|
for i := range numberOfUsers {
|
|
groupIndex := int(math.Mod(float64(i), 2))
|
|
_, err := ss.Group().UpsertMember(groups[groupIndex].Id, users[i].Id)
|
|
require.NoError(t, err)
|
|
|
|
// Add everyone to group 2
|
|
_, err = ss.Group().UpsertMember(groups[numberOfGroups-1].Id, users[i].Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
testCases := map[string]struct {
|
|
expectedUserIDs []string
|
|
expectedTotalCount int64
|
|
groupIDs []string
|
|
page int
|
|
perPage int
|
|
setup func()
|
|
teardown func()
|
|
}{
|
|
"No group IDs, all members": {
|
|
expectedUserIDs: []string{users[0].Id, users[1].Id, users[2].Id, users[3].Id, user.Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"All members, page 1": {
|
|
expectedUserIDs: []string{users[0].Id, users[1].Id, users[2].Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 0,
|
|
perPage: 3,
|
|
},
|
|
"All members, page 2": {
|
|
expectedUserIDs: []string{users[3].Id, users[4].Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 1,
|
|
perPage: 3,
|
|
},
|
|
"Group 1, even users would be removed": {
|
|
expectedUserIDs: []string{users[0].Id, users[2].Id, users[4].Id},
|
|
expectedTotalCount: 3,
|
|
groupIDs: []string{groups[1].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"Group 0, odd users would be removed": {
|
|
expectedUserIDs: []string{users[1].Id, users[3].Id, users[4].Id},
|
|
expectedTotalCount: 3,
|
|
groupIDs: []string{groups[0].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"All groups, no users would be removed": {
|
|
expectedUserIDs: []string{users[4].Id},
|
|
expectedTotalCount: 1,
|
|
groupIDs: []string{groups[0].Id, groups[1].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
}
|
|
|
|
mapUserIDs := func(users []*model.UserWithGroups) []string {
|
|
ids := []string{}
|
|
for _, user := range users {
|
|
ids = append(ids, user.Id)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
for tcName, tc := range testCases {
|
|
t.Run(tcName, func(t *testing.T) {
|
|
if tc.setup != nil {
|
|
tc.setup()
|
|
}
|
|
|
|
if tc.teardown != nil {
|
|
defer tc.teardown()
|
|
}
|
|
|
|
actual, err := ss.Group().TeamMembersMinusGroupMembers(team.Id, tc.groupIDs, tc.page, tc.perPage)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.expectedUserIDs, mapUserIDs(actual))
|
|
|
|
actualCount, err := ss.Group().CountTeamMembersMinusGroupMembers(team.Id, tc.groupIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expectedTotalCount, actualCount)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testChannelMembersMinusGroupMembers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
const numberOfGroups = 3
|
|
const numberOfUsers = 4
|
|
|
|
groups := []*model.Group{}
|
|
users := []*model.User{}
|
|
|
|
channel := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypePrivate,
|
|
GroupConstrained: model.NewPointer(true),
|
|
}
|
|
channel, err := ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, err)
|
|
|
|
for i := range numberOfUsers {
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: fmt.Sprintf("a%d_%s", i, model.NewId()),
|
|
}
|
|
user, err = ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
users = append(users, user)
|
|
|
|
trueOrFalse := int(math.Mod(float64(i), 2)) == 0
|
|
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user.Id,
|
|
SchemeUser: trueOrFalse,
|
|
SchemeAdmin: !trueOrFalse,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Extra user outside of the group member users.
|
|
user, err := ss.User().Save(rctx, &model.User{
|
|
Email: MakeEmail(),
|
|
Username: "a99_" + model.NewId(),
|
|
})
|
|
require.NoError(t, err)
|
|
users = append(users, user)
|
|
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user.Id,
|
|
SchemeUser: true,
|
|
SchemeAdmin: false,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
for i := range numberOfGroups {
|
|
group := &model.Group{
|
|
Name: model.NewPointer(fmt.Sprintf("n_%d_%s", i, model.NewId())),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(group)
|
|
require.NoError(t, err)
|
|
groups = append(groups, group)
|
|
}
|
|
|
|
sort.Slice(users, func(i, j int) bool {
|
|
return users[i].Username < users[j].Username
|
|
})
|
|
|
|
// Add even users to even group, and the inverse
|
|
for i := range numberOfUsers {
|
|
groupIndex := int(math.Mod(float64(i), 2))
|
|
_, err := ss.Group().UpsertMember(groups[groupIndex].Id, users[i].Id)
|
|
require.NoError(t, err)
|
|
|
|
// Add everyone to group 2
|
|
_, err = ss.Group().UpsertMember(groups[numberOfGroups-1].Id, users[i].Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
testCases := map[string]struct {
|
|
expectedUserIDs []string
|
|
expectedTotalCount int64
|
|
groupIDs []string
|
|
page int
|
|
perPage int
|
|
setup func()
|
|
teardown func()
|
|
}{
|
|
"No group IDs, all members": {
|
|
expectedUserIDs: []string{users[0].Id, users[1].Id, users[2].Id, users[3].Id, users[4].Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"All members, page 1": {
|
|
expectedUserIDs: []string{users[0].Id, users[1].Id, users[2].Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 0,
|
|
perPage: 3,
|
|
},
|
|
"All members, page 2": {
|
|
expectedUserIDs: []string{users[3].Id, users[4].Id},
|
|
expectedTotalCount: numberOfUsers + 1,
|
|
groupIDs: []string{},
|
|
page: 1,
|
|
perPage: 3,
|
|
},
|
|
"Group 1, even users would be removed": {
|
|
expectedUserIDs: []string{users[0].Id, users[2].Id, users[4].Id},
|
|
expectedTotalCount: 3,
|
|
groupIDs: []string{groups[1].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"Group 0, odd users would be removed": {
|
|
expectedUserIDs: []string{users[1].Id, users[3].Id, users[4].Id},
|
|
expectedTotalCount: 3,
|
|
groupIDs: []string{groups[0].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
"All groups, no users would be removed": {
|
|
expectedUserIDs: []string{users[4].Id},
|
|
expectedTotalCount: 1,
|
|
groupIDs: []string{groups[0].Id, groups[1].Id},
|
|
page: 0,
|
|
perPage: 100,
|
|
},
|
|
}
|
|
|
|
mapUserIDs := func(users []*model.UserWithGroups) []string {
|
|
ids := []string{}
|
|
for _, user := range users {
|
|
ids = append(ids, user.Id)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
for tcName, tc := range testCases {
|
|
t.Run(tcName, func(t *testing.T) {
|
|
if tc.setup != nil {
|
|
tc.setup()
|
|
}
|
|
|
|
if tc.teardown != nil {
|
|
defer tc.teardown()
|
|
}
|
|
|
|
actual, err := ss.Group().ChannelMembersMinusGroupMembers(channel.Id, tc.groupIDs, tc.page, tc.perPage)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, tc.expectedUserIDs, mapUserIDs(actual))
|
|
|
|
actualCount, err := ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, tc.groupIDs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tc.expectedTotalCount, actualCount)
|
|
})
|
|
}
|
|
}
|
|
|
|
func groupTestGetMemberCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
group := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(group)
|
|
require.NoError(t, err)
|
|
|
|
var user *model.User
|
|
var nErr error
|
|
for i := range 2 {
|
|
user = &model.User{
|
|
Email: MakeEmail(),
|
|
Username: fmt.Sprintf("a%d_%s", i, model.NewId()),
|
|
}
|
|
user, nErr = ss.User().Save(rctx, user)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
count, err := ss.Group().GetMemberCount(group.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), count)
|
|
|
|
user.DeleteAt = 1
|
|
_, nErr = ss.User().Update(rctx, user, true)
|
|
require.NoError(t, nErr)
|
|
|
|
count, err = ss.Group().GetMemberCount(group.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count)
|
|
}
|
|
|
|
func groupTestAdminRoleGroupsForSyncableMemberChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, err := ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
|
|
group1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group1, err = ss.Group().Create(group1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
group2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group2, err = ss.Group().Create(group2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
channel := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel, nErr := ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group1.Id,
|
|
SchemeAdmin: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group2.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User is a member of both groups but only one is SchemeAdmin: true
|
|
actualGroupIDs, err := ss.Group().AdminRoleGroupsForSyncableMember(user.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group1.Id}, actualGroupIDs)
|
|
|
|
// Update the second group syncable to be SchemeAdmin: true and both groups should be returned
|
|
groupSyncable2.SchemeAdmin = true
|
|
_, err = ss.Group().UpdateGroupSyncable(groupSyncable2)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group1.Id, group2.Id}, actualGroupIDs)
|
|
|
|
// Deleting membership from group should stop the group from being returned
|
|
_, err = ss.Group().DeleteMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group2.Id}, actualGroupIDs)
|
|
|
|
// Deleting group syncable should stop it being returned
|
|
_, err = ss.Group().DeleteGroupSyncable(group2.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{}, actualGroupIDs)
|
|
}
|
|
|
|
func groupTestAdminRoleGroupsForSyncableMemberTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, err := ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
|
|
group1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group1, err = ss.Group().Create(group1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
group2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group2, err = ss.Group().Create(group2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user.Id)
|
|
require.NoError(t, err)
|
|
|
|
team := &model.Team{
|
|
DisplayName: "A Name",
|
|
Name: NewTestID(),
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(team)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group1.Id,
|
|
SchemeAdmin: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group2.Id,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// User is a member of both groups but only one is SchemeAdmin: true
|
|
actualGroupIDs, err := ss.Group().AdminRoleGroupsForSyncableMember(user.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group1.Id}, actualGroupIDs)
|
|
|
|
// Update the second group syncable to be SchemeAdmin: true and both groups should be returned
|
|
groupSyncable2.SchemeAdmin = true
|
|
_, err = ss.Group().UpdateGroupSyncable(groupSyncable2)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group1.Id, group2.Id}, actualGroupIDs)
|
|
|
|
// Deleting membership from group should stop the group from being returned
|
|
_, err = ss.Group().DeleteMember(group1.Id, user.Id)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{group2.Id}, actualGroupIDs)
|
|
|
|
// Deleting group syncable should stop it being returned
|
|
_, err = ss.Group().DeleteGroupSyncable(group2.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
actualGroupIDs, err = ss.Group().AdminRoleGroupsForSyncableMember(user.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{}, actualGroupIDs)
|
|
}
|
|
|
|
func groupTestPermittedSyncableAdminsTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
|
|
group1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group1, err = ss.Group().Create(group1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
group2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group2, err = ss.Group().Create(group2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
team := &model.Team{
|
|
DisplayName: "A Name",
|
|
Name: NewTestID(),
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, nErr := ss.Team().Save(team)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group1.Id,
|
|
SchemeAdmin: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group2.Id,
|
|
SchemeAdmin: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// group 1's users are returned because groupsyncable 2 has SchemeAdmin false.
|
|
actualUserIDs, err := ss.Group().PermittedSyncableAdmins(team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user2.Id}, actualUserIDs)
|
|
|
|
// update groupsyncable 2 to be SchemeAdmin true
|
|
groupSyncable2.SchemeAdmin = true
|
|
_, err = ss.Group().UpdateGroupSyncable(groupSyncable2)
|
|
require.NoError(t, err)
|
|
|
|
// group 2's users are now included in return value
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user2.Id, user3.Id}, actualUserIDs)
|
|
|
|
// deleted group member should not be included
|
|
ss.Group().DeleteMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user3.Id}, actualUserIDs)
|
|
|
|
// deleted group syncable no longer includes group members
|
|
_, err = ss.Group().DeleteGroupSyncable(group1.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user3.Id}, actualUserIDs)
|
|
}
|
|
|
|
func groupTestPermittedSyncableAdminsChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err := ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
|
|
group1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group1, err = ss.Group().Create(group1)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
group2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
Description: model.NewId(),
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group2, err = ss.Group().Create(group2)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(group2.Id, user3.Id)
|
|
require.NoError(t, err)
|
|
|
|
channel := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel, nErr := ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group1.Id,
|
|
SchemeAdmin: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: channel.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group2.Id,
|
|
SchemeAdmin: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// group 1's users are returned because groupsyncable 2 has SchemeAdmin false.
|
|
actualUserIDs, err := ss.Group().PermittedSyncableAdmins(channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user2.Id}, actualUserIDs)
|
|
|
|
// update groupsyncable 2 to be SchemeAdmin true
|
|
groupSyncable2.SchemeAdmin = true
|
|
_, err = ss.Group().UpdateGroupSyncable(groupSyncable2)
|
|
require.NoError(t, err)
|
|
|
|
// group 2's users are now included in return value
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user2.Id, user3.Id}, actualUserIDs)
|
|
|
|
// deleted group member should not be included
|
|
_, err = ss.Group().DeleteMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user1.Id, user3.Id}, actualUserIDs)
|
|
|
|
// deleted group syncable no longer includes group members
|
|
_, err = ss.Group().DeleteGroupSyncable(group1.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
actualUserIDs, err = ss.Group().PermittedSyncableAdmins(channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.ElementsMatch(t, []string{user3.Id}, actualUserIDs)
|
|
}
|
|
|
|
func groupTestUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err := ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err = ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
t.Log("Created user1", user1.Id)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
t.Log("Created user2", user2.Id)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
t.Log("Created user3", user3.Id)
|
|
|
|
user4 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user4, err = ss.User().Save(rctx, user4)
|
|
require.NoError(t, err)
|
|
t.Log("Created user4", user4.Id)
|
|
|
|
for _, user := range []*model.User{user1, user2, user3} {
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user.Id}, 9999)
|
|
require.NoError(t, nErr)
|
|
}
|
|
|
|
_, nErr := ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: user4.Id, SchemeGuest: true}, 9999)
|
|
require.NoError(t, nErr)
|
|
|
|
tests := []struct {
|
|
testName string
|
|
newAdmins []string
|
|
expectedUpdatedUsers []string
|
|
}{
|
|
{
|
|
"Two new admins",
|
|
[]string{user1.Id, user2.Id},
|
|
[]string{user1.Id, user2.Id},
|
|
},
|
|
{
|
|
"Demote one admin",
|
|
[]string{user1.Id},
|
|
[]string{user2.Id},
|
|
},
|
|
{
|
|
"Operation is idempotent",
|
|
[]string{user1.Id},
|
|
nil,
|
|
},
|
|
{
|
|
"Promote a team member",
|
|
[]string{user1.Id, user3.Id},
|
|
[]string{user3.Id},
|
|
},
|
|
{
|
|
"Guests never get promoted",
|
|
[]string{user1.Id, user3.Id, user4.Id},
|
|
nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.testName, func(t *testing.T) {
|
|
var updatedMembers []*model.TeamMember
|
|
updatedMembers, err = ss.Team().UpdateMembersRole(team.Id, tt.newAdmins)
|
|
require.NoError(t, err)
|
|
|
|
var updatedUserIDs []string
|
|
for _, member := range updatedMembers {
|
|
assert.False(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
|
|
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
}
|
|
|
|
updatedUserIDs = append(updatedUserIDs, member.UserId)
|
|
}
|
|
assert.ElementsMatch(t, tt.expectedUpdatedUsers, updatedUserIDs)
|
|
|
|
members, err := ss.Team().GetMembers(team.Id, 0, 100, nil)
|
|
require.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(members), 4) // sanity check for team membership
|
|
|
|
for _, member := range members {
|
|
// Ensure guest account never changes.
|
|
if member.UserId == user4.Id {
|
|
assert.False(t, member.SchemeUser, fmt.Sprintf("userID: %s", member.UserId))
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
assert.True(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
channel := &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: "A Name",
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen, // Query does not look at type so this shouldn't matter.
|
|
}
|
|
channel, err := ss.Channel().Save(rctx, channel, 9999)
|
|
require.NoError(t, err)
|
|
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err = ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
t.Log("Created user1", user1.Id)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
t.Log("Created user2", user2.Id)
|
|
|
|
user3 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user3, err = ss.User().Save(rctx, user3)
|
|
require.NoError(t, err)
|
|
t.Log("Created user3", user3.Id)
|
|
|
|
user4 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user4, err = ss.User().Save(rctx, user4)
|
|
require.NoError(t, err)
|
|
t.Log("Created user4", user4.Id)
|
|
|
|
for _, user := range []*model.User{user1, user2, user3} {
|
|
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
_, err = ss.Channel().SaveMember(rctx, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: user4.Id,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
testName string
|
|
newAdmins []string
|
|
expectedUpdatedUsers []string
|
|
}{
|
|
{
|
|
"Two new admins",
|
|
[]string{user1.Id, user2.Id},
|
|
[]string{user1.Id, user2.Id},
|
|
},
|
|
{
|
|
"Demote one admin",
|
|
[]string{user1.Id},
|
|
[]string{user2.Id},
|
|
},
|
|
{
|
|
"Operation is idempotent",
|
|
[]string{user1.Id},
|
|
nil,
|
|
},
|
|
{
|
|
"Promote a team member",
|
|
[]string{user1.Id, user3.Id},
|
|
[]string{user3.Id},
|
|
},
|
|
{
|
|
"Guests never get promoted",
|
|
[]string{user1.Id, user3.Id, user4.Id},
|
|
nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.testName, func(t *testing.T) {
|
|
var updatedMemmbers []*model.ChannelMember
|
|
updatedMemmbers, err = ss.Channel().UpdateMembersRole(channel.Id, tt.newAdmins)
|
|
require.NoError(t, err)
|
|
|
|
var updatedUserIDs []string
|
|
for _, member := range updatedMemmbers {
|
|
assert.False(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
|
|
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
}
|
|
|
|
updatedUserIDs = append(updatedUserIDs, member.UserId)
|
|
}
|
|
assert.ElementsMatch(t, tt.expectedUpdatedUsers, updatedUserIDs)
|
|
|
|
members, err := ss.Channel().GetMembers(model.ChannelMembersGetOptions{ChannelID: channel.Id, Offset: 0, Limit: 100})
|
|
require.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership
|
|
|
|
for _, member := range members {
|
|
// Ensure guest account never changes.
|
|
if member.UserId == user4.Id {
|
|
assert.False(t, member.SchemeUser, fmt.Sprintf("userID: %s", member.UserId))
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
assert.True(t, member.SchemeGuest, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
if slices.Contains(tt.newAdmins, member.UserId) {
|
|
assert.True(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
} else {
|
|
assert.False(t, member.SchemeAdmin, fmt.Sprintf("userID: %s", member.UserId))
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func groupTestGroupCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group1.Id)
|
|
|
|
count, err := ss.Group().GroupCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, count, int64(1))
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group2.Id)
|
|
|
|
countAfter, err := ss.Group().GroupCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, countAfter, count+1)
|
|
}
|
|
|
|
func groupTestGroupTeamCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
team, err := ss.Team().Save(&model.Team{
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
AllowOpenInvite: false,
|
|
InviteId: model.NewId(),
|
|
Name: NewTestID(),
|
|
Email: model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Team().PermanentDelete(team.Id)
|
|
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group1.Id)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group2.Id)
|
|
|
|
groupSyncable1, err := ss.Group().CreateGroupSyncable(model.NewGroupTeam(group1.Id, team.Id, false))
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteGroupSyncable(groupSyncable1.GroupId, groupSyncable1.SyncableId, groupSyncable1.Type)
|
|
|
|
count, err := ss.Group().GroupTeamCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, count, int64(1))
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(model.NewGroupTeam(group2.Id, team.Id, false))
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteGroupSyncable(groupSyncable2.GroupId, groupSyncable2.SyncableId, groupSyncable2.Type)
|
|
|
|
countAfter, err := ss.Group().GroupTeamCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, countAfter, count+1)
|
|
}
|
|
|
|
func groupTestGroupChannelCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
channel, err := ss.Channel().Save(rctx, &model.Channel{
|
|
TeamId: model.NewId(),
|
|
DisplayName: model.NewId(),
|
|
Name: model.NewId(),
|
|
Type: model.ChannelTypeOpen,
|
|
}, 9999)
|
|
require.NoError(t, err)
|
|
defer ss.Channel().Delete(channel.Id, 0)
|
|
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group1.Id)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group2.Id)
|
|
|
|
groupSyncable1, err := ss.Group().CreateGroupSyncable(model.NewGroupChannel(group1.Id, channel.Id, false))
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteGroupSyncable(groupSyncable1.GroupId, groupSyncable1.SyncableId, groupSyncable1.Type)
|
|
|
|
count, err := ss.Group().GroupChannelCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, count, int64(1))
|
|
|
|
groupSyncable2, err := ss.Group().CreateGroupSyncable(model.NewGroupChannel(group2.Id, channel.Id, false))
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteGroupSyncable(groupSyncable2.GroupId, groupSyncable2.SyncableId, groupSyncable2.Type)
|
|
|
|
countAfter, err := ss.Group().GroupChannelCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, countAfter, count+1)
|
|
}
|
|
|
|
func groupTestGroupMemberCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
user := &model.User{
|
|
Email: fmt.Sprintf("test.%s@localhost", model.NewId()),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user, err := ss.User().Save(rctx, user)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: fmt.Sprintf("test.%s@localhost", model.NewId()),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
group, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group.Id)
|
|
|
|
member1, err := ss.Group().UpsertMember(group.Id, user.Id)
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteMember(group.Id, member1.UserId)
|
|
|
|
count, err := ss.Group().GroupMemberCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, count, int64(1))
|
|
|
|
member2, err := ss.Group().UpsertMember(group.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteMember(group.Id, member2.UserId)
|
|
|
|
countAfter, err := ss.Group().GroupMemberCount()
|
|
require.NoError(t, err)
|
|
require.GreaterOrEqual(t, countAfter, count+1)
|
|
}
|
|
|
|
func groupTestDistinctGroupMemberCount(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
ss.DropAllTables()
|
|
|
|
// Create two groups
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group1.Id)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group2.Id)
|
|
|
|
// Create two users
|
|
user1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, err = ss.User().Save(rctx, user1)
|
|
require.NoError(t, err)
|
|
|
|
user2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, err = ss.User().Save(rctx, user2)
|
|
require.NoError(t, err)
|
|
|
|
// Add user1 to group1
|
|
member1, err := ss.Group().UpsertMember(group1.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
defer ss.Group().DeleteMember(group1.Id, member1.UserId)
|
|
|
|
// Verify count is now 1
|
|
count, err := ss.Group().DistinctGroupMemberCount()
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count)
|
|
|
|
// Add user2 to group1
|
|
_, err = ss.Group().UpsertMember(group1.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Verify count is now 2
|
|
countAfter1, err := ss.Group().DistinctGroupMemberCount()
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), countAfter1)
|
|
|
|
// Add user1 to group2 as well
|
|
_, err = ss.Group().UpsertMember(group2.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Verify count stays at 2 (user1 is already counted)
|
|
countAfter2, err := ss.Group().DistinctGroupMemberCount()
|
|
require.NoError(t, err)
|
|
require.Equal(t, countAfter1, countAfter2)
|
|
}
|
|
|
|
func groupTestGroupCountWithAllowReference(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
initialCount, err := ss.Group().GroupCountWithAllowReference()
|
|
require.NoError(t, err)
|
|
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group1.Id)
|
|
|
|
count, err := ss.Group().GroupCountWithAllowReference()
|
|
require.NoError(t, err)
|
|
require.Equal(t, count, initialCount)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
AllowReference: true,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.Group().Delete(group2.Id)
|
|
|
|
countAfter, err := ss.Group().GroupCountWithAllowReference()
|
|
require.NoError(t, err)
|
|
require.Greater(t, countAfter, count)
|
|
}
|
|
|
|
func groupTestGetMember(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
member, err := ss.Group().GetMember(g1.Id, u1.Id)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, member)
|
|
|
|
member, err = ss.Group().GetMember(g1.Id, user2.Id)
|
|
require.Error(t, err)
|
|
require.Nil(t, member)
|
|
}
|
|
|
|
func groupTestGetNonMemberUsersPage(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
group, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
_, nErr = ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
users, err := ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000, nil)
|
|
require.NoError(t, err)
|
|
|
|
originalLen := len(users)
|
|
|
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
users, err = ss.Group().GetNonMemberUsersPage(group.Id, 0, 1000, nil)
|
|
require.NoError(t, err)
|
|
require.Len(t, users, originalLen-1)
|
|
|
|
users, err = ss.Group().GetNonMemberUsersPage(model.NewId(), 0, 1000, nil)
|
|
require.Error(t, err)
|
|
require.Nil(t, users)
|
|
}
|
|
|
|
func groupTestDistinctGroupMemberCountForSource(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// get the before counts
|
|
customGroupCountBefore, err := ss.Group().DistinctGroupMemberCountForSource(model.GroupSourceCustom)
|
|
require.NoError(t, err)
|
|
ldapGroupCountBefore, err := ss.Group().DistinctGroupMemberCountForSource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
|
|
// create 2 groups, 1 custom and 1 ldap
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
customGroup, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
ldapGroup, err := ss.Group().Create(g2)
|
|
require.NoError(t, err)
|
|
|
|
// create a couple of users
|
|
u1 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user1, nErr := ss.User().Save(rctx, u1)
|
|
require.NoError(t, nErr)
|
|
|
|
u2 := &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
}
|
|
user2, nErr := ss.User().Save(rctx, u2)
|
|
require.NoError(t, nErr)
|
|
|
|
// add both new users to both new groups
|
|
_, err = ss.Group().UpsertMember(customGroup.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(ldapGroup.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().UpsertMember(customGroup.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(ldapGroup.Id, user2.Id)
|
|
require.NoError(t, err)
|
|
|
|
// remove one user from a group to ensure the 'where deleteat = 0' clause is working
|
|
_, err = ss.Group().DeleteMember(ldapGroup.Id, user1.Id)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
ss.Group().DeleteMember(ldapGroup.Id, user2.Id)
|
|
ss.Group().DeleteMember(customGroup.Id, user1.Id)
|
|
ss.Group().DeleteMember(customGroup.Id, user2.Id)
|
|
|
|
ss.Group().Delete(customGroup.Id)
|
|
ss.Group().Delete(ldapGroup.Id)
|
|
|
|
ss.User().PermanentDelete(rctx, user1.Id)
|
|
ss.User().PermanentDelete(rctx, user2.Id)
|
|
}()
|
|
|
|
customGroupCount, err := ss.Group().DistinctGroupMemberCountForSource(model.GroupSourceCustom)
|
|
require.NoError(t, err)
|
|
require.Equal(t, customGroupCountBefore+2, customGroupCount)
|
|
|
|
ldapGroupCount, err := ss.Group().DistinctGroupMemberCountForSource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ldapGroupCountBefore+1, ldapGroupCount)
|
|
}
|
|
|
|
func groupTestGroupCountBySource(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Get initial counts for different sources
|
|
customSourceCountBefore, err := ss.Group().GroupCountBySource(model.GroupSourceCustom)
|
|
require.NoError(t, err)
|
|
ldapSourceCountBefore, err := ss.Group().GroupCountBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
|
|
// Create groups with different sources
|
|
g1 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
customGroup, err := ss.Group().Create(g1)
|
|
require.NoError(t, err)
|
|
|
|
g2 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
ldapGroup, err := ss.Group().Create(g2)
|
|
require.NoError(t, err)
|
|
|
|
g3 := &model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
}
|
|
ldapGroup2, err := ss.Group().Create(g3)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
ss.Group().Delete(customGroup.Id)
|
|
ss.Group().Delete(ldapGroup.Id)
|
|
ss.Group().Delete(ldapGroup2.Id)
|
|
}()
|
|
|
|
// Check counts after creating groups
|
|
customSourceCountAfter, err := ss.Group().GroupCountBySource(model.GroupSourceCustom)
|
|
require.NoError(t, err)
|
|
require.Equal(t, customSourceCountBefore+1, customSourceCountAfter)
|
|
|
|
ldapSourceCountAfter, err := ss.Group().GroupCountBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ldapSourceCountBefore+2, ldapSourceCountAfter)
|
|
|
|
// Delete one LDAP group and verify count decreases
|
|
ss.Group().Delete(ldapGroup.Id)
|
|
ldapSourceCountAfterDelete, err := ss.Group().GroupCountBySource(model.GroupSourceLdap)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ldapSourceCountAfter-1, ldapSourceCountAfterDelete)
|
|
}
|
|
|
|
func testCountMembersMinusGroupMembers(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create test users
|
|
u1, err := ss.User().Save(rctx, &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
u2, err := ss.User().Save(rctx, &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
u3, err := ss.User().Save(rctx, &model.User{
|
|
Email: MakeEmail(),
|
|
Username: model.NewUsername(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create test team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err = ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
|
|
// Create a second team to test team-specific counts
|
|
team2 := &model.Team{
|
|
DisplayName: "Name 2",
|
|
Description: "Some description 2",
|
|
CompanyName: "Some company name 2",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid1",
|
|
Name: "z-z-" + model.NewId() + "b",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team2, err = ss.Team().Save(team2)
|
|
require.NoError(t, err)
|
|
|
|
// Create test channel in team 1
|
|
channel := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "Display Name",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel, nErr := ss.Channel().Save(rctx, channel, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Create test channel in team 2
|
|
channel2 := &model.Channel{
|
|
TeamId: team2.Id,
|
|
DisplayName: "Display Name 2",
|
|
Name: "z-z-" + model.NewId() + "b",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel2, nErr = ss.Channel().Save(rctx, channel2, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// Add users to teams and channels
|
|
// u1 and u2 in team 1
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: u1.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team.Id, UserId: u2.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// u3 in team 2
|
|
_, nErr = ss.Team().SaveMember(rctx, &model.TeamMember{TeamId: team2.Id, UserId: u3.Id}, -1)
|
|
require.NoError(t, nErr)
|
|
|
|
// u1 and u2 in channel 1
|
|
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel.Id, UserId: u1.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
|
require.NoError(t, nErr)
|
|
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel.Id, UserId: u2.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
|
require.NoError(t, nErr)
|
|
|
|
// u3 in channel 2
|
|
_, nErr = ss.Channel().SaveMember(rctx, &model.ChannelMember{ChannelId: channel2.Id, UserId: u3.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
|
require.NoError(t, nErr)
|
|
|
|
// Create groups
|
|
group1, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
group2, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Add u1 to group1, u3 to group2
|
|
_, err = ss.Group().UpsertMember(group1.Id, u1.Id)
|
|
require.NoError(t, err)
|
|
_, err = ss.Group().UpsertMember(group2.Id, u3.Id)
|
|
require.NoError(t, err)
|
|
|
|
// Test CountTeamMembersMinusGroupMembers with empty groupIDs
|
|
count, err := ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), count) // Both u1 and u2 are counted when no groups are excluded
|
|
|
|
// Test CountTeamMembersMinusGroupMembers with group1 ID
|
|
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{group1.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count) // Only u2 should be counted (not in the group)
|
|
|
|
// Test with non-existent team ID
|
|
count, err = ss.Group().CountTeamMembersMinusGroupMembers(model.NewId(), []string{group1.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), count) // No members in a non-existent team
|
|
|
|
// Test with multiple group IDs
|
|
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team.Id, []string{group1.Id, group2.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count) // Only u2 should be counted
|
|
|
|
// Test team 2
|
|
count, err = ss.Group().CountTeamMembersMinusGroupMembers(team2.Id, []string{group2.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), count) // No one should be counted (u3 is in group2)
|
|
|
|
// Test CountChannelMembersMinusGroupMembers with empty groupIDs
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), count) // Both users are counted when no groups are excluded
|
|
|
|
// Test CountChannelMembersMinusGroupMembers with the group ID
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{group1.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count) // Only u2 should be counted (not in the group)
|
|
|
|
// Test with multiple group IDs
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel.Id, []string{group1.Id, group2.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(1), count) // u2 should be counted
|
|
|
|
// Test channel 2
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers(channel2.Id, []string{group2.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), count) // No one should be counted (u3 is in group2)
|
|
|
|
// Test with non-existent channel ID
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers(model.NewId(), []string{group1.Id})
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), count) // No members in a non-existent channel
|
|
|
|
// Test error cases - passing invalid parameters
|
|
// 1. Empty team ID
|
|
count, err = ss.Group().CountTeamMembersMinusGroupMembers("", []string{group1.Id})
|
|
require.NoError(t, err) // Should handle this gracefully
|
|
require.Equal(t, int64(0), count)
|
|
|
|
// 2. Empty channel ID
|
|
count, err = ss.Group().CountChannelMembersMinusGroupMembers("", []string{group1.Id})
|
|
require.NoError(t, err) // Should handle this gracefully
|
|
require.Equal(t, int64(0), count)
|
|
}
|
|
|
|
func testGroupStoreToModelChannelAssociations(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
// Create test group
|
|
group, err := ss.Group().Create(&model.Group{
|
|
Name: model.NewPointer(model.NewId()),
|
|
DisplayName: model.NewId(),
|
|
Description: model.NewId(),
|
|
Source: model.GroupSourceCustom,
|
|
RemoteId: model.NewPointer(model.NewId()),
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, group)
|
|
|
|
// Create test team
|
|
team := &model.Team{
|
|
DisplayName: "Name",
|
|
Description: "Some description",
|
|
CompanyName: "Some company name",
|
|
AllowOpenInvite: false,
|
|
InviteId: "inviteid0",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
team, err = ss.Team().Save(team)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, team)
|
|
|
|
// Create test channel 1
|
|
channel1 := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "Display Name 1",
|
|
Name: "z-z-" + model.NewId() + "a",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel1, nErr := ss.Channel().Save(rctx, channel1, -1)
|
|
require.NoError(t, nErr)
|
|
require.NotNil(t, channel1)
|
|
|
|
// Create test channel 2
|
|
channel2 := &model.Channel{
|
|
TeamId: team.Id,
|
|
DisplayName: "Display Name 2",
|
|
Name: "z-z-" + model.NewId() + "b",
|
|
Type: model.ChannelTypeOpen,
|
|
}
|
|
channel2, nErr = ss.Channel().Save(rctx, channel2, -1)
|
|
require.NoError(t, nErr)
|
|
require.NotNil(t, channel2)
|
|
|
|
// Create group channel syncables
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
GroupId: group.Id,
|
|
SyncableId: channel1.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
SchemeAdmin: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.Group().CreateGroupSyncable(&model.GroupSyncable{
|
|
GroupId: group.Id,
|
|
SyncableId: channel2.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
SchemeAdmin: false,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Test the GetGroupsAssociatedToChannelsByTeam function
|
|
// This exercises the groupsAssociatedToChannelWithSchemeAdmin.ToModel method
|
|
result, err := ss.Group().GetGroupsAssociatedToChannelsByTeam(team.Id, model.GroupSearchOpts{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
|
|
// Verify channel 1 results
|
|
require.Contains(t, result, channel1.Id)
|
|
require.NotEmpty(t, result[channel1.Id])
|
|
require.Equal(t, group.Id, result[channel1.Id][0].Id)
|
|
require.NotNil(t, result[channel1.Id][0].SchemeAdmin)
|
|
require.True(t, *result[channel1.Id][0].SchemeAdmin)
|
|
|
|
// Verify channel 2 results (with different SchemeAdmin value)
|
|
require.Contains(t, result, channel2.Id)
|
|
require.NotEmpty(t, result[channel2.Id])
|
|
require.Equal(t, group.Id, result[channel2.Id][0].Id)
|
|
require.NotNil(t, result[channel2.Id][0].SchemeAdmin)
|
|
require.False(t, *result[channel2.Id][0].SchemeAdmin)
|
|
}
|