diff --git a/loadtest/control/simulcontroller/actions.go b/loadtest/control/simulcontroller/actions.go index 4cbb52f6b..f2da4c118 100644 --- a/loadtest/control/simulcontroller/actions.go +++ b/loadtest/control/simulcontroller/actions.go @@ -223,11 +223,14 @@ func loadTeam(u user.User, team *model.Team) control.UserActionResponse { return control.UserActionResponse{Err: control.NewUserError(err)} } - _, err := u.GetUserThreads(team.Id, &model.GetUserThreadsOpts{ + if _, err := u.GetUserThreads(team.Id, &model.GetUserThreadsOpts{ TotalsOnly: true, ThreadsOnly: false, - }) - if err != nil { + }); err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } + + if err := u.GetSidebarCategories(u.Store().Id(), team.Id); err != nil { return control.UserActionResponse{Err: control.NewUserError(err)} } @@ -556,6 +559,78 @@ func deletePost(u user.User) control.UserActionResponse { return control.UserActionResponse{Info: fmt.Sprintf("post deleted, id %v", post.Id)} } +func (c *SimulController) createSidebarCategory(u user.User) control.UserActionResponse { + team, err := u.Store().CurrentTeam() + if err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } else if team == nil { + return control.UserActionResponse{Err: control.NewUserError(errors.New("current team should be set"))} + } + + category := &model.SidebarCategoryWithChannels{ + SidebarCategory: model.SidebarCategory{ + UserId: u.Store().Id(), + TeamId: team.Id, + DisplayName: "category" + control.PickRandomWord(), + }, + } + + sidebarCategory, err := u.CreateSidebarCategory(u.Store().Id(), team.Id, category) + if err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } + + return control.UserActionResponse{Info: fmt.Sprintf("created sidebar category, id %s", sidebarCategory.Id)} +} + +func (c *SimulController) updateSidebarCategory(u user.User) control.UserActionResponse { + team, err := u.Store().CurrentTeam() + if err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } else if team == nil { + return control.UserActionResponse{Err: control.NewUserError(errors.New("current team should be set"))} + } + + cat1, err := u.Store().RandomCategory(team.Id) + if err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } + + cat2, err := u.Store().RandomCategory(team.Id) + if err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } + + // Not repeatedly looping until we get a different category because there have been edge-cases before + // ending in infinite loop.s + if cat1.Id == cat2.Id { + return control.UserActionResponse{Info: "same categories returned. Skipping."} + } + if len(cat1.Channels) <= 1 { + return control.UserActionResponse{Info: "Not enough categories to remove. Skipping."} + } + + // We pick a random channel from first category and move to second category. + channelToMove := control.PickRandomString(cat1.Channels) + + // Find index + i := findIndex(cat1.Channels, channelToMove) + // Defense in depth + if i == -1 { + return control.UserActionResponse{Info: fmt.Sprintf("Channel %s not found in the category", channelToMove)} + } + + // Move from the first, and add to second. + cat1.Channels = append(cat1.Channels[:i], cat1.Channels[i+1:]...) + cat2.Channels = append(cat2.Channels, channelToMove) + + if err := u.UpdateSidebarCategory(u.Store().Id(), team.Id, []*model.SidebarCategoryWithChannels{&cat1, &cat2}); err != nil { + return control.UserActionResponse{Err: control.NewUserError(err)} + } + + return control.UserActionResponse{Info: fmt.Sprintf("updated sidebar categories, ids [%s, %s]", cat1.Id, cat2.Id)} +} + func editPost(u user.User) control.UserActionResponse { channel, err := u.Store().CurrentChannel() if err != nil { diff --git a/loadtest/control/simulcontroller/controller.go b/loadtest/control/simulcontroller/controller.go index 6f3b733db..d4214d55a 100644 --- a/loadtest/control/simulcontroller/controller.go +++ b/loadtest/control/simulcontroller/controller.go @@ -170,6 +170,14 @@ func (c *SimulController) Run() { run: deletePost, frequency: 0.06, }, + { + run: c.createSidebarCategory, + frequency: 0.06, + }, + { + run: c.updateSidebarCategory, + frequency: 0.06, + }, { run: searchGroupChannels, frequency: 0.1, diff --git a/loadtest/control/simulcontroller/utils.go b/loadtest/control/simulcontroller/utils.go index ad54dddf8..911d90166 100644 --- a/loadtest/control/simulcontroller/utils.go +++ b/loadtest/control/simulcontroller/utils.go @@ -154,3 +154,13 @@ func extractMentionFromMessage(msg string) string { } return mention[1:] } + +// findIndex returns the index of needle in a haystack. +func findIndex(haystack []string, needle string) int { + for i := range haystack { + if haystack[i] == needle { + return i + } + } + return -1 +} diff --git a/loadtest/store/memstore/random.go b/loadtest/store/memstore/random.go index d8bb49c08..0c67535a3 100644 --- a/loadtest/store/memstore/random.go +++ b/loadtest/store/memstore/random.go @@ -339,6 +339,24 @@ func (s *MemStore) RandomTeamMember(teamId string) (model.TeamMember, error) { return *teamMemberMap[key.(string)], nil } +func (s *MemStore) RandomCategory(teamID string) (model.SidebarCategoryWithChannels, error) { + s.lock.RLock() + defer s.lock.RUnlock() + + teamCat := s.sidebarCategories[teamID] + + key, err := pickRandomKeyFromMap(teamCat) + if err != nil { + return model.SidebarCategoryWithChannels{}, err + } + + category := *teamCat[key.(string)] + tmp := make([]string, len(category.Channels)) + copy(tmp, category.Channels) + category.Channels = tmp + return category, nil +} + func pickRandomKeyFromMap(m interface{}) (interface{}, error) { val := reflect.ValueOf(m) if val.Kind() != reflect.Map { diff --git a/loadtest/store/memstore/store.go b/loadtest/store/memstore/store.go index 394672e5d..86e344ec7 100644 --- a/loadtest/store/memstore/store.go +++ b/loadtest/store/memstore/store.go @@ -44,6 +44,7 @@ type MemStore struct { serverVersion string threads map[string]*model.ThreadResponse threadsQueue *CQueue + sidebarCategories map[string]map[string]*model.SidebarCategoryWithChannels } // New returns a new instance of MemStore with the given config. @@ -100,6 +101,7 @@ func (s *MemStore) Clear() { s.channelViews = map[string]int64{} s.threads = map[string]*model.ThreadResponse{} s.threadsQueue.Reset() + s.sidebarCategories = map[string]map[string]*model.SidebarCategoryWithChannels{} } func (s *MemStore) setupQueues(config *Config) error { @@ -1020,6 +1022,18 @@ func (s *MemStore) SetThreads(trs []*model.ThreadResponse) error { return nil } +func (s *MemStore) SetCategories(teamID string, sidebarCategories *model.OrderedSidebarCategories) error { + s.lock.Lock() + defer s.lock.Unlock() + + teamCat := make(map[string]*model.SidebarCategoryWithChannels) + for _, cat := range sidebarCategories.Categories { + teamCat[cat.SidebarCategory.Id] = cat + } + s.sidebarCategories[teamID] = teamCat + return nil +} + func (s *MemStore) getThreads(unreadOnly bool) ([]*model.ThreadResponse, error) { var threads []*model.ThreadResponse for _, thread := range s.threads { diff --git a/loadtest/store/store.go b/loadtest/store/store.go index 3aa7a720a..67fdfab79 100644 --- a/loadtest/store/store.go +++ b/loadtest/store/store.go @@ -105,6 +105,8 @@ type UserStore interface { RandomTeamMember(teamId string) (model.TeamMember, error) // RandomThread returns a random thread. RandomThread() (model.ThreadResponse, error) + // RandomCategory returns a random category from a team + RandomCategory(teamID string) (model.SidebarCategoryWithChannels, error) // profile // ProfileImage returns whether the profile image for the given user has been @@ -238,4 +240,7 @@ type MutableUserStore interface { SetThreads(threads []*model.ThreadResponse) error // MarkAllThreadsInTeamAsRead marks all threads in the given team as read MarkAllThreadsInTeamAsRead(teamId string) error + + // SidebarCategories + SetCategories(teamID string, sidebarCategories *model.OrderedSidebarCategories) error } diff --git a/loadtest/user/user.go b/loadtest/user/user.go index c136ba4e5..74750ca53 100644 --- a/loadtest/user/user.go +++ b/loadtest/user/user.go @@ -283,4 +283,9 @@ type User interface { MarkAllThreadsInTeamAsRead(teamId string) error // UpdateThreadRead updates the read timestamp of the thread UpdateThreadRead(teamId, threadId string, timestamp int64) error + + // SidebarCategories + GetSidebarCategories(userID, teamID string) error + CreateSidebarCategory(userID, teamID string, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, error) + UpdateSidebarCategory(userID, teamID string, categories []*model.SidebarCategoryWithChannels) error } diff --git a/loadtest/user/userentity/actions.go b/loadtest/user/userentity/actions.go index 43fae1fb3..a41fb1c71 100644 --- a/loadtest/user/userentity/actions.go +++ b/loadtest/user/userentity/actions.go @@ -1212,3 +1212,36 @@ func (ue *UserEntity) UpdateThreadRead(teamId, threadId string, timestamp int64) } return ue.store.SetThreads([]*model.ThreadResponse{thread}) } + +// GetSidebarCategories fetches and stores the sidebar categories for an user. +func (ue *UserEntity) GetSidebarCategories(userID, teamID string) error { + categories, _, err := ue.client.GetSidebarCategoriesForTeamForUser(userID, teamID, "") + if err != nil { + return err + } + + return ue.store.SetCategories(teamID, categories) +} + +func (ue *UserEntity) CreateSidebarCategory(userID, teamID string, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, error) { + cat, _, err := ue.client.CreateSidebarCategoryForTeamForUser(userID, teamID, category) + if err != nil { + return nil, err + } + + // The client fetches and stores all categories again. + if err := ue.GetSidebarCategories(userID, teamID); err != nil { + return nil, err + } + return cat, nil +} + +func (ue *UserEntity) UpdateSidebarCategory(userID, teamID string, categories []*model.SidebarCategoryWithChannels) error { + _, _, err := ue.client.UpdateSidebarCategoriesForTeamForUser(userID, teamID, categories) + if err != nil { + return err + } + + // The client fetches and stores all categories again. + return ue.GetSidebarCategories(userID, teamID) +}