mattermost-community-enterp.../cmd/mmctl/commands/command_test.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

1060 lines
33 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
"github.com/spf13/cobra"
)
func (s *MmctlUnitTestSuite) TestCommandCreateCmd() {
s.Run("Create a new custom slash command for a specified team", func() {
printer.Clean()
teamArg := "example-team-id"
titleArg := "example-command-name"
descriptionArg := "example-description-text"
triggerWordArg := "example-trigger-word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
creatorUsernameArg := "example-user"
responseUsernameArg := "example-username2"
iconArg := "icon-url"
method := "G"
autocomplete := false
autocompleteDesc := "autocompleteDesc"
autocompleteHint := "autocompleteHint"
mockTeam := model.Team{Id: teamArg, Name: "TeamRed"}
mockUser := model.User{Id: creatorIDArg, Username: creatorUsernameArg}
mockCommand := model.Command{
TeamId: teamArg,
DisplayName: titleArg,
Description: descriptionArg,
Trigger: triggerWordArg,
URL: urlArg,
CreatorId: creatorIDArg,
Username: responseUsernameArg,
IconURL: iconArg,
Method: method,
AutoComplete: autocomplete,
AutoCompleteDesc: autocompleteDesc,
AutoCompleteHint: autocompleteHint,
}
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
cmd.Flags().String("title", titleArg, "")
cmd.Flags().String("description", descriptionArg, "")
cmd.Flags().String("trigger-word", triggerWordArg, "")
cmd.Flags().String("url", urlArg, "")
cmd.Flags().String("creator", creatorIDArg, "")
cmd.Flags().String("response-username", responseUsernameArg, "")
cmd.Flags().String("icon", iconArg, "")
cmd.Flags().String("method", method, "")
cmd.Flags().Bool("autocomplete", autocomplete, "")
cmd.Flags().String("autocompleteDesc", autocompleteDesc, "")
cmd.Flags().String("autocompleteHint", autocompleteHint, "")
// createCommandCmdF will call getTeamFromTeamArg, getUserFromUserArg which then calls GetUserByUsername
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), creatorIDArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
CreateCommand(context.TODO(), &mockCommand).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Equal(&mockCommand, printer.GetLines()[0])
s.Len(printer.GetErrorLines(), 0)
})
s.Run("Create a slash command only providing team, trigger word, url, creator", func() {
printer.Clean()
teamArg := "example-team-id"
triggerWordArg := "example-trigger-word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
creatorUsernameArg := "example-user"
method := "G"
mockTeam := model.Team{Id: teamArg}
mockUser := model.User{Id: creatorIDArg, Username: creatorUsernameArg}
mockCommand := model.Command{
TeamId: teamArg,
Trigger: triggerWordArg,
URL: urlArg,
CreatorId: creatorIDArg,
Method: method,
}
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
cmd.Flags().String("trigger-word", triggerWordArg, "")
cmd.Flags().String("url", urlArg, "")
cmd.Flags().String("creator", creatorIDArg, "")
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), creatorIDArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
CreateCommand(context.TODO(), &mockCommand).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Equal(&mockCommand, printer.GetLines()[0])
s.Len(printer.GetErrorLines(), 0)
})
s.Run("Create slash command for a nonexistent team", func() {
printer.Clean()
teamArg := "example-team-id"
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), teamArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "unable to find team '"+teamArg+"'")
})
s.Run("Create slash command with a space in trigger word", func() {
printer.Clean()
teamArg := "example-team-id"
titleArg := "example-command-name"
descriptionArg := "example-description-text"
triggerWordArg := "example trigger word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
creatorUsernameArg := "example-user"
responseUsernameArg := "example-username2"
iconArg := "icon-url"
method := "G"
autocomplete := false
autocompleteDesc := "autocompleteDesc"
autocompleteHint := "autocompleteHint"
mockTeam := model.Team{Id: teamArg}
mockUser := model.User{Id: creatorIDArg, Username: creatorUsernameArg}
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
cmd.Flags().String("title", titleArg, "")
cmd.Flags().String("description", descriptionArg, "")
cmd.Flags().String("trigger-word", triggerWordArg, "")
cmd.Flags().String("url", urlArg, "")
cmd.Flags().String("creator", creatorIDArg, "")
cmd.Flags().String("response-username", responseUsernameArg, "")
cmd.Flags().String("icon", iconArg, "")
cmd.Flags().String("method", method, "")
cmd.Flags().Bool("autocomplete", autocomplete, "")
cmd.Flags().String("autocompleteDesc", autocompleteDesc, "")
cmd.Flags().String("autocompleteHint", autocompleteHint, "")
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), creatorIDArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "a trigger word must not contain spaces")
})
s.Run("Create slash command with trigger word prefixed with /", func() {
printer.Clean()
teamArg := "example-team-id"
titleArg := "example-command-name"
descriptionArg := "example-description-text"
triggerWordArg := "/example-trigger-word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
creatorUsernameArg := "example-user"
responseUsernameArg := "example-username2"
iconArg := "icon-url"
method := "G"
autocomplete := false
autocompleteDesc := "autocompleteDesc"
autocompleteHint := "autocompleteHint"
mockTeam := model.Team{Id: teamArg}
mockUser := model.User{Id: creatorIDArg, Username: creatorUsernameArg}
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
cmd.Flags().String("title", titleArg, "")
cmd.Flags().String("description", descriptionArg, "")
cmd.Flags().String("trigger-word", triggerWordArg, "")
cmd.Flags().String("url", urlArg, "")
cmd.Flags().String("creator", creatorIDArg, "")
cmd.Flags().String("response-username", responseUsernameArg, "")
cmd.Flags().String("icon", iconArg, "")
cmd.Flags().String("method", method, "")
cmd.Flags().Bool("autocomplete", autocomplete, "")
cmd.Flags().String("autocompleteDesc", autocompleteDesc, "")
cmd.Flags().String("autocompleteHint", autocompleteHint, "")
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), creatorIDArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "a trigger word cannot begin with a /")
})
s.Run("Create slash command fail", func() {
printer.Clean()
teamArg := "example-team-id"
titleArg := "example-command-name"
descriptionArg := "example-description-text"
triggerWordArg := "example-trigger-word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
creatorUsernameArg := "example-user"
responseUsernameArg := "example-username2"
iconArg := "icon-url"
method := "G"
autocomplete := false
autocompleteDesc := "autocompleteDesc"
autocompleteHint := "autocompleteHint"
mockTeam := model.Team{Id: teamArg}
mockUser := model.User{Id: creatorIDArg, Username: creatorUsernameArg}
mockCommand := model.Command{
TeamId: teamArg,
DisplayName: titleArg,
Description: descriptionArg,
Trigger: triggerWordArg,
URL: urlArg,
CreatorId: creatorIDArg,
Username: responseUsernameArg,
IconURL: iconArg,
Method: method,
AutoComplete: autocomplete,
AutoCompleteDesc: autocompleteDesc,
AutoCompleteHint: autocompleteHint,
}
cmd := &cobra.Command{}
cmd.Flags().String("team", teamArg, "")
cmd.Flags().String("title", titleArg, "")
cmd.Flags().String("description", descriptionArg, "")
cmd.Flags().String("trigger-word", triggerWordArg, "")
cmd.Flags().String("url", urlArg, "")
cmd.Flags().String("creator", creatorIDArg, "")
cmd.Flags().String("response-username", responseUsernameArg, "")
cmd.Flags().String("icon", iconArg, "")
cmd.Flags().String("method", method, "")
cmd.Flags().Bool("autocomplete", autocomplete, "")
cmd.Flags().String("autocompleteDesc", autocompleteDesc, "")
cmd.Flags().String("autocompleteHint", autocompleteHint, "")
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), creatorIDArg, "").
Return(&mockUser, &model.Response{}, nil).
Times(1)
mockError := errors.New("mock error, simulated error for CreateCommand")
s.client.
EXPECT().
CreateCommand(context.TODO(), &mockCommand).
Return(nil, &model.Response{}, mockError).
Times(1)
err := createCommandCmdF(s.client, cmd, []string{teamArg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "unable to create command '"+mockCommand.DisplayName+"'. "+mockError.Error())
})
}
func (s *MmctlUnitTestSuite) TestArchiveCommandCmd() {
s.Run("Delete without errors", func() {
printer.Clean()
arg := "cmd1"
outputMessage := map[string]any{"status": "ok"}
s.client.
EXPECT().
DeleteCommand(context.TODO(), arg).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := archiveCommandCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(printer.GetLines()[0], outputMessage)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Not able to delete", func() {
printer.Clean()
arg := "cmd1"
outputMessage := map[string]any{"status": "error"}
s.client.
EXPECT().
DeleteCommand(context.TODO(), arg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := archiveCommandCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(printer.GetLines()[0], outputMessage)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Delete with response error", func() {
printer.Clean()
arg := "cmd1"
mockError := errors.New("mock error")
s.client.
EXPECT().
DeleteCommand(context.TODO(), arg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
err := archiveCommandCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().NotNil(err)
s.Require().Equal(err, errors.New("Unable to archive command '"+arg+"' error: "+mockError.Error()))
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestCommandListCmdF() {
s.Run("List all commands from all teams", func() {
printer.Clean()
team1ID := "team-id-1"
team2Id := "team-id-2"
commandTeam1ID := "command-team1-id"
commandTeam2Id := "command-team2-id"
teams := []*model.Team{
{Id: team1ID},
{Id: team2Id},
}
team1Commands := []*model.Command{
{
Id: commandTeam1ID,
},
}
team2Commands := []*model.Command{
{
Id: commandTeam2Id,
},
}
cmd := &cobra.Command{}
s.client.EXPECT().GetAllTeams(context.TODO(), "", 0, DefaultPageSize).Return(teams, &model.Response{}, nil).Times(1)
s.client.EXPECT().GetAllTeams(context.TODO(), "", 1, DefaultPageSize).Return([]*model.Team{}, &model.Response{}, nil).Times(1)
s.client.EXPECT().ListCommands(context.TODO(), team1ID, true).Return(team1Commands, &model.Response{}, nil).Times(1)
s.client.EXPECT().ListCommands(context.TODO(), team2Id, true).Return(team2Commands, &model.Response{}, nil).Times(1)
err := listCommandCmdF(s.client, cmd, []string{})
s.Require().Nil(err)
s.Len(printer.GetLines(), 2)
s.Equal(team1Commands[0], printer.GetLines()[0])
s.Equal(team2Commands[0], printer.GetLines()[1])
s.Len(printer.GetErrorLines(), 0)
})
s.Run("List commands for a specific team", func() {
printer.Clean()
teamID := "team-id"
commandID := "command-id"
team := &model.Team{Id: teamID}
teamCommand := []*model.Command{
{
Id: commandID,
},
}
cmd := &cobra.Command{}
s.client.EXPECT().GetTeam(context.TODO(), teamID, "").Return(team, &model.Response{}, nil).Times(1)
s.client.EXPECT().ListCommands(context.TODO(), teamID, true).Return(teamCommand, &model.Response{}, nil).Times(1)
err := listCommandCmdF(s.client, cmd, []string{teamID})
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Equal(teamCommand[0], printer.GetLines()[0])
s.Len(printer.GetErrorLines(), 0)
})
s.Run("List commands for a non existing team", func() {
teamID := "non-existing-team"
printer.Clean()
cmd := &cobra.Command{}
// first try to get team by id
s.client.EXPECT().GetTeam(context.TODO(), teamID, "").Return(nil, &model.Response{}, nil).Times(1)
// second try to search the team by name
s.client.EXPECT().GetTeamByName(context.TODO(), teamID, "").Return(nil, &model.Response{}, nil).Times(1)
err := listCommandCmdF(s.client, cmd, []string{teamID})
s.Require().Error(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 1)
s.Equal("Unable to find team '"+teamID+"'", printer.GetErrorLines()[0])
})
s.Run("Failling to list commands for an existing team", func() {
teamID := "team-id"
printer.Clean()
cmd := &cobra.Command{}
team := &model.Team{Id: teamID}
s.client.EXPECT().GetTeam(context.TODO(), teamID, "").Return(team, &model.Response{}, nil).Times(1)
s.client.EXPECT().ListCommands(context.TODO(), teamID, true).Return(nil, &model.Response{}, errors.New("")).Times(1)
err := listCommandCmdF(s.client, cmd, []string{teamID})
s.Require().Error(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 1)
s.Equal("Unable to list commands for '"+teamID+"'", printer.GetErrorLines()[0])
})
}
func (s *MmctlUnitTestSuite) TestCommandModifyCmd() {
arg := "cmd1"
teamID := "example-team-id"
titleArg := "example-command-name"
descriptionArg := "example-description-text"
triggerWordArg := "example-trigger-word"
urlArg := "http://localhost:8000/example"
creatorIDArg := "example-user-id"
responseUsernameArg := "example-username2"
iconArg := "icon-url"
method := "G"
autocomplete := false
autocompleteDesc := "autocompleteDesc"
autocompleteHint := "autocompleteHint"
mockCommand := model.Command{
TeamId: teamID,
DisplayName: titleArg,
Description: descriptionArg,
Trigger: triggerWordArg,
URL: urlArg,
CreatorId: creatorIDArg,
Username: responseUsernameArg,
IconURL: iconArg,
Method: method,
AutoComplete: autocomplete,
AutoCompleteDesc: autocompleteDesc,
AutoCompleteHint: autocompleteHint,
}
s.Run("Modify a custom slash command by id", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.DisplayName = titleArg + "_modified"
mockCommandModified.Description = descriptionArg + "_modified"
mockCommandModified.Trigger = triggerWordArg + "_modified"
mockCommandModified.URL = urlArg + "_modified"
mockCommandModified.CreatorId = creatorIDArg + "_modified"
mockCommandModified.Username = responseUsernameArg + "_modified"
mockCommandModified.IconURL = iconArg + "_modified"
mockCommandModified.Method = method
mockCommandModified.AutoComplete = !autocomplete
mockCommandModified.AutoCompleteDesc = autocompleteDesc + "_modified"
mockCommandModified.AutoCompleteHint = autocompleteHint + "_modified"
cli := []string{
arg,
"--title=" + mockCommandModified.DisplayName,
"--description=" + mockCommandModified.Description,
"--trigger-word=" + mockCommandModified.Trigger,
"--url=" + mockCommandModified.URL,
"--creator=" + mockCommandModified.CreatorId,
"--response-username=" + mockCommandModified.Username,
"--icon=" + mockCommandModified.IconURL,
"--autocomplete=" + strconv.FormatBool(mockCommandModified.AutoComplete),
"--autocompleteDesc=" + mockCommandModified.AutoCompleteDesc,
"--autocompleteHint=" + mockCommandModified.AutoCompleteHint,
"--post=" + strconv.FormatBool(method2Bool(mockCommandModified.Method)),
}
// modifyCommandCmdF will call getCommandById, GetUserByUsername and UpdateCommand
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), mockCommandModified.CreatorId, "").
Return(&model.User{Id: mockCommandModified.CreatorId}, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
UpdateCommand(context.TODO(), &mockCommand).
Return(mockCommandModified, &model.Response{}, nil).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().Nil(err)
s.Len(printer.GetLines(), 1)
s.Equal(mockCommandModified, printer.GetLines()[0])
s.Len(printer.GetErrorLines(), 0)
})
s.Run("Modify slash command using a nonexistent commandID", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.DisplayName = titleArg + "_modified"
cli := []string{
arg,
"--title=" + mockCommandModified.DisplayName,
}
// modifyCommandCmdF will call getCommandById
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(nil, &model.Response{}, nil).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true for all flags on the CLI.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "unable to find command '"+arg+"'")
})
s.Run("Modify slash command with invalid user name", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.CreatorId = creatorIDArg + "_modified"
bogusUsername := "bogus"
cli := []string{
arg,
"--creator=" + bogusUsername,
}
// modifyCommandCmdF will call getCommandById, then try looking up user
// via email, username, and id.
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUserByUsername(context.TODO(), bogusUsername, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetUser(context.TODO(), bogusUsername, "").
Return(nil, &model.Response{}, nil).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true for all flags on the CLI.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "unable to find user '"+bogusUsername+"'")
})
s.Run("Modify slash command with a space in trigger word", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.Trigger = creatorIDArg + " modified with space"
cli := []string{
arg,
"--trigger-word=" + mockCommandModified.Trigger,
}
// modifyCommandCmdF will call getCommandById
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true for all flags on the CLI.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "a trigger word must not contain spaces")
})
s.Run("Modify slash command with trigger word prefixed with /", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.Trigger = "/modified_with_slash"
cli := []string{
arg,
"--trigger-word=" + mockCommandModified.Trigger,
}
// modifyCommandCmdF will call getCommandById
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true for all flags on the CLI.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "a trigger word cannot begin with a /")
})
s.Run("Modify slash command fail", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.Trigger = creatorIDArg + "_modified"
cli := []string{
arg,
"--trigger-word=" + mockCommandModified.Trigger,
}
// modifyCommandCmdF will call getCommandById then UpdateCommand
s.client.
EXPECT().
GetCommandById(context.TODO(), arg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
mockError := errors.New("mock error, simulated error for CreateCommand")
s.client.
EXPECT().
UpdateCommand(context.TODO(), &mockCommand).
Return(nil, &model.Response{}, mockError).
Times(1)
// Reset the cmd and parse to force Flag.Changed to be true for all flags on the CLI.
cmd := CommandModifyCmd
cmd.ResetFlags()
addCommandFieldsFlags(cmd)
err := cmd.ParseFlags(cli)
s.Require().Nil(err)
err = modifyCommandCmdF(s.client, cmd, []string{arg})
s.Require().NotNil(err)
s.Len(printer.GetLines(), 0)
s.Len(printer.GetErrorLines(), 0)
s.EqualError(err, "unable to modify command '"+mockCommand.DisplayName+"': "+mockError.Error())
})
}
//nolint:golint,unused
func method2Bool(method string) bool {
switch strings.ToUpper(method) {
case "P":
return true
case "G":
return false
default:
panic(fmt.Errorf("invalid method '%s'", method))
}
}
//nolint:golint,unused
func copyCommand(cmd *model.Command) *model.Command {
c := *cmd
return &c
}
func (s *MmctlUnitTestSuite) TestCommandMoveCmd() {
commandArg := "cmd1"
commandArgBogus := "bogus-command-id"
teamArg := "dest-team-id"
teamArgBogus := "bogus-team-id"
mockTeamDest := model.Team{Id: teamArg}
mockCommand := model.Command{
Id: commandArg,
TeamId: "orig-team-id",
DisplayName: "example-title",
Trigger: "example-trigger",
}
mockError := errors.New("mock error")
outputMessageOK := map[string]any{"status": "ok"}
outputMessageError := map[string]any{"status": "error"}
s.Run("Move custom slash command to another team by id", func() {
printer.Clean()
mockCommandModified := copyCommand(&mockCommand)
mockCommandModified.TeamId = teamArg
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), teamArg, "").
Return(&mockTeamDest, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
MoveCommand(context.TODO(), teamArg, mockCommand.Id).
Return(&model.Response{StatusCode: http.StatusOK}, nil).
Times(1)
err := moveCommandCmdF(s.client, &cobra.Command{}, []string{teamArg, mockCommand.Id})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(printer.GetLines()[0], outputMessageOK)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Move custom slash command to invalid team by id", func() {
printer.Clean()
s.client.
EXPECT().
GetTeam(context.TODO(), teamArgBogus, "").
Return(nil, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetTeamByName(context.TODO(), teamArgBogus, "").
Return(nil, &model.Response{}, nil).
Times(1)
err := moveCommandCmdF(s.client, &cobra.Command{}, []string{teamArgBogus, commandArg})
s.Require().NotNil(err)
s.EqualError(err, "unable to find team '"+teamArgBogus+"'")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Move custom slash command to different team by invalid id", func() {
printer.Clean()
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeamDest, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArgBogus).
Return(nil, &model.Response{}, nil).
Times(1)
err := moveCommandCmdF(s.client, &cobra.Command{}, []string{teamArg, commandArgBogus})
s.Require().NotNil(err)
s.EqualError(err, "unable to find command '"+commandArgBogus+"'")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Unable to move custom slash command", func() {
printer.Clean()
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeamDest, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
MoveCommand(context.TODO(), teamArg, commandArg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, nil).
Times(1)
err := moveCommandCmdF(s.client, &cobra.Command{}, []string{teamArg, commandArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(printer.GetLines()[0], outputMessageError)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Move custom slash command with response error", func() {
printer.Clean()
s.client.
EXPECT().
GetTeam(context.TODO(), teamArg, "").
Return(&mockTeamDest, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
MoveCommand(context.TODO(), teamArg, commandArg).
Return(&model.Response{StatusCode: http.StatusBadRequest}, mockError).
Times(1)
err := moveCommandCmdF(s.client, &cobra.Command{}, []string{teamArg, commandArg})
s.Require().NotNil(err)
s.Require().EqualError(err, "unable to move command '"+commandArg+"': "+mockError.Error())
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
}
func (s *MmctlUnitTestSuite) TestCommandShowCmd() {
commandArg := "example-command-id"
commandArgBogus := "bogus-command-id"
mockCommand := model.Command{
Id: commandArg,
TeamId: "example-team-id",
DisplayName: "example-command-name",
Description: "example-description-text",
Trigger: "example-trigger-word",
URL: "http://localhost:8000/example",
CreatorId: "example-user-id",
Username: "example-username2",
IconURL: "http://mydomain/example-icon-url",
Method: "G",
AutoComplete: false,
AutoCompleteDesc: "example autocomplete description",
AutoCompleteHint: "autocompleteHint",
}
mockTeam := model.Team{Id: "mockteamid", Name: "TeamRed"}
s.Run("Show custom slash command via id", func() {
printer.Clean()
// showCommandCmdF will look up command by id
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArg).
Return(&mockCommand, &model.Response{}, nil).
Times(1)
err := showCommandCmdF(s.client, &cobra.Command{}, []string{commandArg})
s.Require().Nil(err)
s.Require().Len(printer.GetLines(), 1)
s.Equal(&mockCommand, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Show custom slash command with invalid id", func() {
printer.Clean()
// showCommandCmdF will look up command by id
s.client.
EXPECT().
GetCommandById(context.TODO(), commandArgBogus).
Return(nil, &model.Response{}, nil).
Times(1)
err := showCommandCmdF(s.client, &cobra.Command{}, []string{commandArgBogus})
s.Require().NotNil(err)
s.EqualError(err, "unable to find command '"+commandArgBogus+"'")
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Show custom slash command via team:trigger", func() {
printer.Clean()
list := []*model.Command{copyCommand(&mockCommand), &mockCommand, copyCommand(&mockCommand)}
list[0].Trigger = "bloop"
list[2].Trigger = "bleep"
s.client.
EXPECT().
GetTeamByName(context.TODO(), mockTeam.Name, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ListCommands(context.TODO(), mockTeam.Id, false).
Return(list, &model.Response{}, nil).
Times(1)
err := showCommandCmdF(s.client, &cobra.Command{}, []string{fmt.Sprintf("%s:%s", mockTeam.Name, mockCommand.Trigger)})
s.Require().NoError(err)
s.Require().Len(printer.GetLines(), 1)
s.Equal(&mockCommand, printer.GetLines()[0])
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Show custom slash command via team:trigger with invalid team", func() {
printer.Clean()
list := []*model.Command{copyCommand(&mockCommand), &mockCommand, copyCommand(&mockCommand)}
list[0].Trigger = "bloop"
list[2].Trigger = "bleep"
const teamName = "bogus_team"
teamTrigger := fmt.Sprintf("%s:%s", teamName, mockCommand.Trigger)
s.client.
EXPECT().
GetTeamByName(context.TODO(), teamName, "").
Return(nil, &model.Response{}, errors.New("team not found")).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), teamTrigger).
Return(nil, &model.Response{}, errors.New("command not found")).
Times(1)
err := showCommandCmdF(s.client, &cobra.Command{}, []string{teamTrigger})
s.Require().EqualError(err, fmt.Sprintf("unable to find command '%s'", teamTrigger))
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Show custom slash command via team:trigger with invalid trigger", func() {
printer.Clean()
list := []*model.Command{copyCommand(&mockCommand), &mockCommand, copyCommand(&mockCommand)}
list[0].Trigger = "bloop"
list[2].Trigger = "bleep"
const trigger = "bogus_trigger"
teamTrigger := fmt.Sprintf("%s:%s", mockTeam.Name, trigger)
s.client.
EXPECT().
GetTeamByName(context.TODO(), mockTeam.Name, "").
Return(&mockTeam, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
ListCommands(context.TODO(), mockTeam.Id, false).
Return(list, &model.Response{}, nil).
Times(1)
s.client.
EXPECT().
GetCommandById(context.TODO(), teamTrigger).
Return(nil, &model.Response{}, errors.New("bogus")).
Times(1)
err := showCommandCmdF(s.client, &cobra.Command{}, []string{teamTrigger})
s.Require().EqualError(err, fmt.Sprintf("unable to find command '%s'", teamTrigger))
s.Require().Len(printer.GetLines(), 0)
s.Require().Len(printer.GetErrorLines(), 0)
})
s.Run("Avoid path traversal", func() {
printer.Clean()
arg := "\"test/../hello?\"move"
err := showCommandCmdF(s.client, &cobra.Command{}, []string{arg})
s.Require().NotNil(err)
s.EqualError(err, "unable to find command '\"test/../hello?\"move'")
})
}