MM-62158: group store no SELECT * (Part 1) (#30276)

* improved test coverage

* initial pass on removing SELECT * from group store
Этот коммит содержится в:
Jesse Hallam
2025-05-01 09:39:05 -03:00
коммит произвёл GitHub
родитель 80c58a9742
Коммит e1f47e22e7
2 изменённых файлов: 450 добавлений и 154 удалений

Просмотреть файл

@@ -34,6 +34,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
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) })
@@ -50,6 +51,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
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) })
@@ -74,6 +76,7 @@ func TestGroupStore(t *testing.T, rctx request.CTX, ss store.Store) {
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) })
@@ -1618,6 +1621,44 @@ func testGetGroupSyncable(t *testing.T, rctx request.CTX, ss store.Store) {
require.Zero(t, gt1.DeleteAt)
}
func testGetGroupSyncableErrors(t *testing.T, rctx request.CTX, ss store.Store) {
// Create a group
g1 := &model.Group{
Name: model.NewPointer(model.NewId()),
DisplayName: model.NewId(),
Description: model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: model.NewPointer(model.NewId()),
}
group, err := ss.Group().Create(g1)
require.NoError(t, err)
// Test with invalid syncable type
invalidSyncableType := model.GroupSyncableType("invalid")
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), invalidSyncableType)
require.Error(t, err)
var nfErr *store.ErrNotFound
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with empty group ID
_, err = ss.Group().GetGroupSyncable("", model.NewId(), model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with empty syncable ID
_, err = ss.Group().GetGroupSyncable(group.Id, "", model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with completely non-existent IDs
randomGroupId := model.NewId()
randomTeamId := model.NewId()
_, err = ss.Group().GetGroupSyncable(randomGroupId, randomTeamId, model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr), "expected ErrNotFound, got %v", err)
// Test with valid group ID but non-existent syncable
_, err = ss.Group().GetGroupSyncable(group.Id, model.NewId(), model.GroupSyncableTypeTeam)
require.True(t, errors.As(err, &nfErr))
}
func testGetAllGroupSyncablesByGroup(t *testing.T, rctx request.CTX, ss store.Store) {
t.Run("team", func(t *testing.T) { testGetAllGroupSyncablesByGroupTeam(t, rctx, ss) })
t.Run("channel", func(t *testing.T) { testGetAllGroupSyncablesByGroupChannel(t, rctx, ss) })
@@ -4174,17 +4215,14 @@ func testGetGroups(t *testing.T, rctx request.CTX, ss store.Store) {
PerPage: 100,
Resultf: func(groups []*model.Group) bool {
for _, group := range groups {
fmt.Println(group.Id, group.ChannelMemberCount)
var channelMemberCount int
if group.ChannelMemberCount != nil {
channelMemberCount = *group.ChannelMemberCount
}
if group.Id == group1.Id && channelMemberCount != 2 {
fmt.Println("group1", group.Id, channelMemberCount)
return false
}
if group.Id == group2.Id && channelMemberCount != 1 {
fmt.Println("group2", group.Id, channelMemberCount)
return false
}
}
@@ -4205,11 +4243,9 @@ func testGetGroups(t *testing.T, rctx request.CTX, ss store.Store) {
channelMemberCount = *group.ChannelMemberCount
}
if group.Id == group1.Id && channelMemberCount != 1 {
fmt.Println("group1", group.Id, channelMemberCount)
return false
}
if group.Id == group2.Id && channelMemberCount != 2 {
fmt.Println("group2", group.Id, channelMemberCount)
return false
}
}
@@ -5800,3 +5836,266 @@ func groupTestGroupCountBySource(t *testing.T, rctx request.CTX, ss store.Store)
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)
}