mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* MM-68547: Tighten authorization on group syncable link and patch endpoints
Adds an additional permission check on the group syncable link and patch
endpoints. Callers must hold the role-management permission for the
target team or channel (or the sysconsole groups-management permission).
Made-with: Cursor
* Linting
* MM-68547: Extend group syncable scheme_admin authorization checks
Gate any explicit scheme_admin value (in either direction) on link and
patch. Populate SchemeAdmin in the singular getGroupSyncable so that
patches that do not touch scheme_admin no longer overwrite the persisted
value. Restrict PermittedSyncableAdmins to active syncables. Start the
link upsert from the existing active row to preserve fields the caller
did not, or could not, set.
Made-with: Cursor
* MM-68547: Add store-layer regression coverage for SchemeAdmin handling
Extend testGetGroupSyncable to round-trip SchemeAdmin: true through
UpdateGroupSyncable and re-fetch, locking in that getGroupSyncable
populates the field from the persisted row.
Strengthen groupTestPermittedSyncableAdmins{Team,Channel} to assert
that DeleteGroupSyncable preserves SchemeAdmin in the persisted row
and that PermittedSyncableAdmins still excludes the row, making the
coupling between the two store changes explicit.
Made-with: Cursor
* MM-68547: Fix group details role-change dedup on remove
The roleChangeKey helper was reading team_id/channel_id from the items
in itemsToRemove, but onRemoveTeamOrChannel pushes those items with a
generic id field. The deletion of the staged role change in
handleRemovedTeamsAndChannels therefore never matched the key produced
by onChangeRoles, and a stale patchGroupSyncable was dispatched after
the unlink.
Accept either id or team_id/channel_id when computing the key. Also
extend the e2e assertion to verify the channel removal took effect
(delete_at != 0) alongside the existing scheme_admin check.
Made-with: Cursor
* MM-68547: Mirror delete_at assertion on the removed-team e2e test
The team variant of "does not update the role of a removed X" was left
asserting only on scheme_admin. Add the matching delete_at != 0 check
already present in the channel variant so both tests verify the same
user-visible contract.
Made-with: Cursor
* Skip SyncSyncableRoles if no scheme_admin
(cherry picked from commit 6afe64ff9f)
Co-authored-by: Maria A Nunez <[email protected]>
6133 lines
187 KiB
Go
6133 lines
187 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.Equal(t, gt1.SchemeAdmin, dgt.SchemeAdmin)
|
|
require.NotZero(t, gt1.CreateAt)
|
|
require.NotZero(t, gt1.UpdateAt)
|
|
require.Zero(t, gt1.DeleteAt)
|
|
|
|
// Round-trip SchemeAdmin: true through UpdateGroupSyncable and re-fetch.
|
|
dgt.SchemeAdmin = true
|
|
_, err = ss.Group().UpdateGroupSyncable(dgt)
|
|
require.NoError(t, err)
|
|
|
|
dgt, err = ss.Group().GetGroupSyncable(groupTeam.GroupId, groupTeam.SyncableId, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.True(t, dgt.SchemeAdmin, "GetGroupSyncable must populate SchemeAdmin from the persisted row")
|
|
}
|
|
|
|
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) {
|
|
// Use a unique prefix for display names to avoid collisions with groups
|
|
// created by other parallel subtests sharing the same database.
|
|
uid := model.NewId()[:8]
|
|
|
|
// 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: uid + "-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() + "-" + uid + "-group-2"),
|
|
DisplayName: uid + "-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() + "-" + uid + "-group-deleted"),
|
|
DisplayName: uid + "-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() + "-" + uid + "-group-3"),
|
|
DisplayName: uid + "-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 := uid + "-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: uid + "-GrOuP-3"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) == 0 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if !strings.Contains(strings.ToLower(g.DisplayName), uid+"-group-3") {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
Restrictions: nil,
|
|
},
|
|
{
|
|
Name: "Get group matching multiple display names",
|
|
Opts: model.GroupSearchOpts{Q: uid + "-GrOuP"},
|
|
Page: 0,
|
|
PerPage: 100,
|
|
Resultf: func(groups []*model.Group) bool {
|
|
if len(groups) < 2 {
|
|
return false
|
|
}
|
|
for _, g := range groups {
|
|
if !strings.Contains(strings.ToLower(g.DisplayName), uid+"-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: uid + "-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: uid + "-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()
|
|
}{
|
|
"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()
|
|
}
|
|
|
|
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()
|
|
}{
|
|
"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()
|
|
}
|
|
|
|
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)
|
|
|
|
// The persisted row must still carry SchemeAdmin=true after soft-delete;
|
|
// PermittedSyncableAdmins excludes it via the DeleteAt = 0 predicate, not
|
|
// via the field having been silently cleared.
|
|
deletedSyncable, err := ss.Group().GetGroupSyncable(group1.Id, team.Id, model.GroupSyncableTypeTeam)
|
|
require.NoError(t, err)
|
|
require.True(t, deletedSyncable.SchemeAdmin)
|
|
require.NotZero(t, deletedSyncable.DeleteAt)
|
|
|
|
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)
|
|
|
|
// The persisted row must still carry SchemeAdmin=true after soft-delete;
|
|
// PermittedSyncableAdmins excludes it via the DeleteAt = 0 predicate, not
|
|
// via the field having been silently cleared.
|
|
deletedSyncable, err := ss.Group().GetGroupSyncable(group1.Id, channel.Id, model.GroupSyncableTypeChannel)
|
|
require.NoError(t, err)
|
|
require.True(t, deletedSyncable.SchemeAdmin)
|
|
require.NotZero(t, deletedSyncable.DeleteAt)
|
|
|
|
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)
|
|
}
|