[MM-44336] Add timestamp parameter to test data slash commands (#21400)

This commit is contained in:
cyrilzhang-mm
2022-10-26 09:08:24 -04:00
committed by GitHub
parent 0323e40d3c
commit 82096a8cbb
6 changed files with 276 additions and 114 deletions

View File

@@ -20,6 +20,7 @@ type AutoChannelCreator struct {
NameLen utils.Range
NameCharset string
ChannelType model.ChannelType
CreateTime int64
}
func NewAutoChannelCreator(a *app.App, team *model.Team, userID string) *AutoChannelCreator {
@@ -33,6 +34,7 @@ func NewAutoChannelCreator(a *app.App, team *model.Team, userID string) *AutoCha
NameLen: ChannelNameLen,
NameCharset: utils.LOWERCASE,
ChannelType: ChannelType,
CreateTime: 0,
}
}
@@ -51,6 +53,7 @@ func (cfg *AutoChannelCreator) createRandomChannel(c request.CTX) (*model.Channe
Name: name,
Type: cfg.ChannelType,
CreatorId: cfg.userID,
CreateAt: cfg.CreateTime,
}
channel, err := cfg.a.CreateChannel(c, channel, true)
@@ -74,23 +77,3 @@ func (cfg *AutoChannelCreator) CreateTestChannels(c request.CTX, num utils.Range
return channels, nil
}
func (cfg *AutoChannelCreator) CreateTestDMs(c request.CTX, num utils.Range) ([]*model.Channel, error) {
numDMs := utils.RandIntFromRange(num)
dms := make([]*model.Channel, numDMs)
users, err := cfg.a.GetUsersFromProfiles(&model.UserGetOptions{Page: 0, PerPage: numDMs})
if err != nil {
return nil, err
}
for i, user := range users {
var err *model.AppError
dms[i], err = cfg.a.GetOrCreateDirectChannel(c, cfg.userID, user.Id)
if err != nil {
return nil, err
}
}
return dms, nil
}

View File

@@ -17,31 +17,37 @@ import (
)
type AutoPostCreator struct {
a *app.App
channelid string
userid string
Fuzzy bool
TextLength utils.Range
HasImage bool
ImageFilenames []string
Users []string
Mentions utils.Range
Tags utils.Range
a *app.App
channelid string
userid string
Fuzzy bool
TextLength utils.Range
HasImage bool
ImageFilenames []string
Users []string
UsersToPostFrom []string
Mentions utils.Range
Tags utils.Range
CreateTime int64
postsCreated int
}
// Automatic poster used for testing
func NewAutoPostCreator(a *app.App, channelid, userid string) *AutoPostCreator {
return &AutoPostCreator{
a: a,
channelid: channelid,
userid: userid,
Fuzzy: false,
TextLength: utils.Range{Begin: 100, End: 200},
HasImage: false,
ImageFilenames: TestImageFileNames,
Users: []string{},
Mentions: utils.Range{Begin: 0, End: 5},
Tags: utils.Range{Begin: 0, End: 7},
a: a,
channelid: channelid,
userid: userid,
Fuzzy: false,
TextLength: utils.Range{Begin: 100, End: 200},
HasImage: false,
ImageFilenames: TestImageFileNames,
Users: []string{},
UsersToPostFrom: []string{},
Mentions: utils.Range{Begin: 0, End: 5},
Tags: utils.Range{Begin: 0, End: 7},
CreateTime: 0,
postsCreated: 0,
}
}
@@ -97,9 +103,23 @@ func (cfg *AutoPostCreator) CreateRandomPostNested(c request.CTX, rootId string)
Message: postText,
FileIds: fileIDs,
}
if cfg.CreateTime != 0 {
// Creating posts with the exact same timestamp results in some posts being skipped
// when they are retrieved by the API based on timestamp.
post.CreateAt = cfg.CreateTime + int64(cfg.postsCreated)
}
if len(cfg.UsersToPostFrom) != 0 {
i := utils.RandIntFromRange(utils.Range{Begin: 0, End: len(cfg.UsersToPostFrom)})
if i < len(cfg.UsersToPostFrom) {
post.UserId = cfg.UsersToPostFrom[i]
}
}
rpost, err := cfg.a.CreatePostMissingChannel(c, post, true)
if err != nil {
return nil, err
}
cfg.postsCreated += 1
return rpost, nil
}

View File

@@ -23,6 +23,7 @@ type AutoUserCreator struct {
NameLength utils.Range
NameCharset string
Fuzzy bool
JoinTime int64
}
func NewAutoUserCreator(a *app.App, client *model.Client4, team *model.Team) *AutoUserCreator {
@@ -35,6 +36,7 @@ func NewAutoUserCreator(a *app.App, client *model.Client4, team *model.Team) *Au
NameLength: UserNameLen,
NameCharset: utils.LOWERCASE,
Fuzzy: false,
JoinTime: 0,
}
}
@@ -92,14 +94,22 @@ func (cfg *AutoUserCreator) createRandomUser(c request.CTX) (*model.User, error)
user := &model.User{
Email: userEmail,
Nickname: userName,
Password: UserPassword}
Password: UserPassword,
CreateAt: cfg.JoinTime,
}
ruser, appErr := cfg.app.CreateUserWithInviteId(c, user, cfg.team.InviteId, "")
if appErr != nil {
return nil, appErr
}
status := &model.Status{UserId: ruser.Id, Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
status := &model.Status{
UserId: ruser.Id,
Status: model.StatusOnline,
Manual: false,
LastActivityAt: ruser.CreateAt,
ActiveChannel: "",
}
if err := cfg.app.Srv().Store().Status().SaveOrUpdate(status); err != nil {
return nil, err
}
@@ -110,6 +120,18 @@ func (cfg *AutoUserCreator) createRandomUser(c request.CTX) (*model.User, error)
return nil, err
}
if cfg.JoinTime != 0 {
teamMember, appErr := cfg.app.GetTeamMember(cfg.team.Id, ruser.Id)
if appErr != nil {
return nil, appErr
}
teamMember.CreateAt = cfg.JoinTime
_, err := cfg.app.Srv().Store().Team().UpdateMember(teamMember)
if err != nil {
return nil, err
}
}
return ruser, nil
}

View File

@@ -30,34 +30,52 @@ var usage = `Mattermost testing commands to help configure the system
/test setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>
Example:
/test setup teams fuzz 10 20 50
/test setup teams fuzz 10 20 50
Users - Add a specified number of random users with fuzz text to current team.
/test users [fuzz] <Min Users> <Max Users>
Users - Add a specified number of random users with fuzz text to current team, at the specified Unix timestamp in milliseconds.
/test users [fuzz] [range=min[,max]] [time=user_join_timestamp]
Default: range=2,5 time=
Examples:
/test users fuzz range=3,8 time=1565076128000
/test users range=1
Channels - Add a specified number of random public (o) or private (p) channels with fuzz text to current team, at the specified Unix timestamp in milliseconds.
/test channels [fuzz] [range=min[,max]] [type=(o|p)] [time=channel_create_timestamp]
Default: range=2,5 type=o time=
Examples:
/test channels fuzz range=5,10 type=p time=1565076128000
/test channels range=1
DMs - Add a specified number of random DM messages between the current user and a specified user, at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI.
/test dms u=@username [range=min[,max]] [time=dm_create_timestamp]
Default: range=2,5 time=
Examples:
/test dms u=@user range=5,10 time=1565076128000
/test dms u=@user range=2
ThreadedPost - Create a threaded post with a specified number of replies at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI.
/test threaded_post [range=min[,max]] [time=post_timestamp]
Default: range=1000 time=
Examples:
/test threaded_post
/test threaded_post range=100,200 time=1565076128000
Posts - Add some random posts with fuzz text to current channel, at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI.
/test posts [fuzz] [range=min[,max]] [images=max_images] [time=post_timestamp]
Default: range=2,5 images=0 time=
Example:
/test users fuzz 5 10
Channels - Add a specified number of random channels with fuzz text to current team.
/test channels [fuzz] <Min Channels> <Max Channels>
Example:
/test channels fuzz 5 10
DMs - Add a specified number of DMs for current user. Requires enough users to be loaded.
/test dms <Min DMs> <Max DMs>
Example:
/test dms 5 10
ThreadedPost - create a large threaded post
/test threaded_post
Posts - Add some random posts with fuzz text to current channel.
/test posts [fuzz] <Min Posts> <Max Posts> <Max Images>
Example:
/test posts fuzz 5 10 3
/test posts fuzz range=5,10 images=3 time=1565076128000
/test posts range=2
Post - Add post to a channel as another user.
/test post u=@username p=passwd c=~channelname t=teamname "message"
@@ -72,10 +90,10 @@ var usage = `Mattermost testing commands to help configure the system
/test http://www.example.com/sample_file.md
Json - Add a post using the JSON file as payload to the current channel.
/test json url
/test json url
Example
/test json http://www.example.com/sample_body.json
Example:
/test json http://www.example.com/sample_body.json
`
@@ -84,11 +102,16 @@ const (
)
var (
userRE = regexp.MustCompile(`u=@([^\s]+)`)
userRE = regexp.MustCompile(`u=@?([^\s]+)`)
passwdRE = regexp.MustCompile(`p=([^\s]+)`)
teamRE = regexp.MustCompile(`t=([^\s]+)`)
channelRE = regexp.MustCompile(`c=~([^\s]+)`)
messageRE = regexp.MustCompile(`"(.*)"`)
fuzzRE = regexp.MustCompile(`fuzz`)
rangeRE = regexp.MustCompile(`range=([^\s]+)`)
timeRE = regexp.MustCompile(`time=([^\s]+)`)
imagesRE = regexp.MustCompile(`images=([^\s]+)`)
typeRE = regexp.MustCompile(`type=([^\s])+`)
)
type LoadTestProvider struct {
@@ -289,14 +312,19 @@ func (*LoadTestProvider) UsersCommand(a *app.App, c request.CTX, args *model.Com
cmd := strings.TrimSpace(strings.TrimPrefix(message, "users"))
doFuzz := false
if strings.Index(cmd, "fuzz") == 0 {
if fuzzRE.MatchString(cmd) {
doFuzz = true
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
}
usersr, ok := parseRange(cmd, "")
if !ok {
usersr = utils.Range{Begin: 2, End: 5}
var err error
rng := utils.Range{Begin: 2, End: 5}
rangeParam := getMatch(rangeRE, cmd)
if rangeParam != "" {
rng, err = parseRange(rangeParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to add users: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
team, err := a.Srv().Store().Team().Get(args.TeamId)
@@ -304,10 +332,20 @@ func (*LoadTestProvider) UsersCommand(a *app.App, c request.CTX, args *model.Com
return &model.CommandResponse{Text: "Failed to add users", ResponseType: model.CommandResponseTypeEphemeral}, err
}
time := int64(0)
timeParam := getMatch(timeRE, cmd)
if timeParam != "" {
time, err = strconv.ParseInt(timeParam, 10, 64)
if err != nil || time < 0 {
return &model.CommandResponse{Text: "Failed to add users: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter")
}
}
client := model.NewAPIv4Client(args.SiteURL)
userCreator := NewAutoUserCreator(a, client, team)
userCreator.Fuzzy = doFuzz
if _, err := userCreator.CreateTestUsers(c, usersr); err != nil {
userCreator.JoinTime = time
if _, err := userCreator.CreateTestUsers(c, rng); err != nil {
return &model.CommandResponse{Text: "Failed to add users: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
@@ -318,14 +356,19 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model.
cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels"))
doFuzz := false
if strings.Index(cmd, "fuzz") == 0 {
if fuzzRE.MatchString(cmd) {
doFuzz = true
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
}
channelsr, ok := parseRange(cmd, "")
if !ok {
channelsr = utils.Range{Begin: 2, End: 5}
var err error
rng := utils.Range{Begin: 2, End: 5}
rangeParam := getMatch(rangeRE, cmd)
if rangeParam != "" {
rng, err = parseRange(rangeParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to add channels: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
team, err := a.Srv().Store().Team().Get(args.TeamId)
@@ -333,9 +376,32 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model.
return &model.CommandResponse{Text: "Failed to add channels", ResponseType: model.CommandResponseTypeEphemeral}, err
}
typ := model.ChannelTypeOpen
typeParam := getMatch(typeRE, cmd)
if typeParam != "" {
switch strings.ToUpper(typeParam) {
case "O":
case "P":
typ = model.ChannelTypePrivate
default:
return &model.CommandResponse{Text: "Failed to add channels: Invalid type parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid type parameter")
}
}
time := int64(0)
timeParam := getMatch(timeRE, cmd)
if timeParam != "" {
time, err = strconv.ParseInt(timeParam, 10, 64)
if err != nil || time < 0 {
return &model.CommandResponse{Text: "Failed to add channels: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter")
}
}
channelCreator := NewAutoChannelCreator(a, team, args.UserId)
channelCreator.Fuzzy = doFuzz
if _, err := channelCreator.CreateTestChannels(c, channelsr); err != nil {
channelCreator.CreateTime = time
channelCreator.ChannelType = typ
if _, err := channelCreator.CreateTestChannels(c, rng); err != nil {
return &model.CommandResponse{Text: "Failed to create test channels: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
@@ -345,20 +411,71 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model.
func (*LoadTestProvider) DMsCommand(a *app.App, c request.CTX, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
cmd := strings.TrimSpace(strings.TrimPrefix(message, "dms"))
channelsr, ok := parseRange(cmd, "")
if !ok {
channelsr = utils.Range{Begin: 2, End: 5}
var err error
username := getMatch(userRE, message)
user, appErr := a.GetUserByUsername(username)
if appErr != nil {
return &model.CommandResponse{Text: "Failed to add DMS: Invalid username", ResponseType: model.CommandResponseTypeEphemeral}, appErr
}
channelCreator := NewAutoChannelCreator(a, nil, args.UserId)
if _, err := channelCreator.CreateTestDMs(c, channelsr); err != nil {
return &model.CommandResponse{Text: "Failed to create test DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
rng := utils.Range{Begin: 2, End: 5}
rangeParam := getMatch(rangeRE, cmd)
if rangeParam != "" {
rng, err = parseRange(rangeParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to add DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
time := int64(0)
timeParam := getMatch(timeRE, cmd)
if timeParam != "" {
time, err = strconv.ParseInt(timeParam, 10, 64)
if err != nil || time < 0 {
return &model.CommandResponse{Text: "Failed to add DMs: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter")
}
}
channel, err := a.GetOrCreateDirectChannel(c, args.UserId, user.Id)
postCreator := NewAutoPostCreator(a, channel.Id, args.UserId)
postCreator.CreateTime = time
postCreator.UsersToPostFrom = []string{user.Id}
numPosts := utils.RandIntFromRange(rng)
for i := 0; i < numPosts; i++ {
if _, err := postCreator.CreateRandomPost(c); err != nil {
return &model.CommandResponse{Text: "Failed to create test DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
return &model.CommandResponse{Text: "Added DMs", ResponseType: model.CommandResponseTypeEphemeral}, nil
}
func (*LoadTestProvider) ThreadedPostCommand(a *app.App, c request.CTX, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
cmd := strings.TrimSpace(strings.TrimPrefix(message, "threaded_post"))
var err error
rng := utils.Range{Begin: 1000, End: 1000}
rangeParam := getMatch(rangeRE, cmd)
if rangeParam != "" {
rng, err = parseRange(rangeParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to create post: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
time := int64(0)
timeParam := getMatch(timeRE, cmd)
if timeParam != "" {
time, err = strconv.ParseInt(timeParam, 10, 64)
if err != nil || time < 0 {
return &model.CommandResponse{Text: "Failed to create post: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter")
}
}
var usernames []string
options := &model.UserGetOptions{InTeamId: args.TeamId, Page: 0, PerPage: 1000}
if profileUsers, err := a.Srv().Store().User().GetProfiles(options); err == nil {
@@ -373,11 +490,13 @@ func (*LoadTestProvider) ThreadedPostCommand(a *app.App, c request.CTX, args *mo
testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId)
testPoster.Fuzzy = true
testPoster.Users = usernames
testPoster.CreateTime = time
rpost, err2 := testPoster.CreateRandomPost(c)
if err2 != nil {
return &model.CommandResponse{Text: "Failed to create a post", ResponseType: model.CommandResponseTypeEphemeral}, err2
}
for i := 0; i < 1000; i++ {
numPosts := utils.RandIntFromRange(rng)
for i := 0; i < numPosts; i++ {
testPoster.CreateRandomPostNested(c, rpost.Id)
}
@@ -388,21 +507,36 @@ func (*LoadTestProvider) PostsCommand(a *app.App, c request.CTX, args *model.Com
cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts"))
doFuzz := false
if strings.Index(cmd, "fuzz") == 0 {
if fuzzRE.MatchString(cmd) {
doFuzz = true
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
}
postsr, ok := parseRange(cmd, "")
if !ok {
postsr = utils.Range{Begin: 20, End: 30}
var err error
rng := utils.Range{Begin: 2, End: 5}
rangeParam := getMatch(rangeRE, cmd)
if rangeParam != "" {
rng, err = parseRange(rangeParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to add posts: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err
}
}
tokens := strings.Fields(cmd)
rimages := utils.Range{Begin: 0, End: 0}
if len(tokens) >= 3 {
if numImages, err := strconv.Atoi(tokens[2]); err == nil {
rimages = utils.Range{Begin: numImages, End: numImages}
maxImages := 0
imagesParam := getMatch(imagesRE, cmd)
if imagesParam != "" {
maxImages, err = strconv.Atoi(imagesParam)
if err != nil {
return &model.CommandResponse{Text: "Failed to add posts: Invalid images parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid images parameter")
}
}
time := int64(0)
timeParam := getMatch(timeRE, cmd)
if timeParam != "" {
time, err = strconv.ParseInt(timeParam, 10, 64)
if err != nil || time < 0 {
return &model.CommandResponse{Text: "Failed to add posts: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter")
}
}
@@ -420,9 +554,10 @@ func (*LoadTestProvider) PostsCommand(a *app.App, c request.CTX, args *model.Com
testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId)
testPoster.Fuzzy = doFuzz
testPoster.Users = usernames
testPoster.CreateTime = time
numImages := utils.RandIntFromRange(rimages)
numPosts := utils.RandIntFromRange(postsr)
numImages := utils.RandIntFromRange(utils.Range{Begin: 0, End: maxImages})
numPosts := utils.RandIntFromRange(rng)
for i := 0; i < numPosts; i++ {
testPoster.HasImage = (i < numImages)
_, err := testPoster.CreateRandomPost(c)
@@ -585,8 +720,8 @@ func (*LoadTestProvider) JsonCommand(a *app.App, c request.CTX, args *model.Comm
return &model.CommandResponse{Text: "Loaded data", ResponseType: model.CommandResponseTypeEphemeral}, nil
}
func parseRange(command string, cmd string) (utils.Range, bool) {
tokens := strings.Fields(strings.TrimPrefix(command, cmd))
func parseRange(rng string) (utils.Range, error) {
tokens := strings.Split(rng, ",")
var begin int
var end int
var err1 error
@@ -594,20 +729,20 @@ func parseRange(command string, cmd string) (utils.Range, bool) {
switch {
case len(tokens) == 1:
begin, err1 = strconv.Atoi(tokens[0])
end = begin
if err1 != nil {
return utils.Range{Begin: 0, End: 0}, false
if err1 != nil || begin < 0 {
return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter")
}
case len(tokens) >= 2:
end = begin
case len(tokens) == 2:
begin, err1 = strconv.Atoi(tokens[0])
end, err2 = strconv.Atoi(tokens[1])
if err1 != nil || err2 != nil {
return utils.Range{Begin: 0, End: 0}, false
if err1 != nil || err2 != nil || begin < 0 || end < begin {
return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter")
}
default:
return utils.Range{Begin: 0, End: 0}, false
return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter")
}
return utils.Range{Begin: begin, End: end}, true
return utils.Range{Begin: begin, End: end}, nil
}
func contains(items []string, token string) bool {

View File

@@ -437,7 +437,9 @@ func (u *User) PreSave() {
u.Username = NormalizeUsername(u.Username)
u.Email = NormalizeEmail(u.Email)
u.CreateAt = GetMillis()
if u.CreateAt == 0 {
u.CreateAt = GetMillis()
}
u.UpdateAt = u.CreateAt
u.LastPasswordUpdate = u.CreateAt

View File

@@ -876,7 +876,7 @@ func (s SqlTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*mod
}
if _, err := s.GetMasterX().NamedExec(`UPDATE TeamMembers
SET Roles=:Roles, DeleteAt=:DeleteAt, SchemeGuest=:SchemeGuest,
SET Roles=:Roles, DeleteAt=:DeleteAt, CreateAt=:CreateAt, SchemeGuest=:SchemeGuest,
SchemeUser=:SchemeUser, SchemeAdmin=:SchemeAdmin
WHERE TeamId=:TeamId AND UserId=:UserId`, newTeamMember); err != nil {
return nil, errors.Wrap(err, "failed to update TeamMember")