2019-11-29 06:59:40 -05:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2017-03-23 19:42:32 -04:00
package api4
import (
2023-06-06 17:29:29 -04:00
"context"
2021-07-26 04:11:02 -04:00
"encoding/json"
2026-03-25 12:06:34 -04:00
"fmt"
2018-05-14 13:24:22 -04:00
"net/http"
"net/http/httptest"
"net/url"
2020-01-24 09:32:56 -05:00
"strings"
2017-03-23 19:42:32 -04:00
"testing"
2018-11-19 15:27:17 -05:00
"github.com/stretchr/testify/assert"
2018-05-14 13:24:22 -04:00
"github.com/stretchr/testify/require"
2023-06-11 01:24:35 -04:00
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog"
2026-03-25 12:06:34 -04:00
"github.com/mattermost/mattermost/server/v8/channels/testlib"
2017-03-23 19:42:32 -04:00
)
func TestCreateCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2020-05-22 07:48:22 -04:00
LocalClient := th . LocalClient
2017-03-23 19:42:32 -04:00
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
2017-03-23 19:42:32 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
2017-03-23 19:42:32 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2017-03-23 19:42:32 -04:00
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger" ,
2025-05-30 07:58:26 -04:00
}
2017-03-23 19:42:32 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err := client . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-03-23 19:42:32 -04:00
CheckForbiddenStatus ( t , resp )
2023-06-06 17:29:29 -04:00
createdCmd , resp , err := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-03-31 09:56:20 -04:00
CheckCreatedStatus ( t , resp )
2019-10-24 09:32:31 -04:00
require . Equal ( t , th . SystemAdminUser . Id , createdCmd . CreatorId , "user ids didn't match" )
require . Equal ( t , th . BasicTeam . Id , createdCmd . TeamId , "team ids didn't match" )
2017-03-23 19:42:32 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-03-23 19:42:32 -04:00
CheckBadRequestStatus ( t , resp )
2021-08-13 07:12:16 -04:00
CheckErrorID ( t , err , "api.command.duplicate_trigger.app_error" )
2017-03-23 19:42:32 -04:00
2020-05-22 07:48:22 -04:00
newCmd . Trigger = "Local"
2025-11-13 06:12:30 -05:00
newCmd . CreatorId = th . BasicUser . Id
2023-06-06 17:29:29 -04:00
localCreatedCmd , resp , err := LocalClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-22 07:48:22 -04:00
CheckCreatedStatus ( t , resp )
require . Equal ( t , th . BasicUser . Id , localCreatedCmd . CreatorId , "local client: user ids didn't match" )
require . Equal ( t , th . BasicTeam . Id , localCreatedCmd . TeamId , "local client: team ids didn't match" )
2017-03-23 19:42:32 -04:00
newCmd . Method = "Wrong"
2017-06-15 11:05:43 -04:00
newCmd . Trigger = "testcommand"
2023-06-06 17:29:29 -04:00
_ , resp , err = th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-09-15 08:32:11 -04:00
CheckBadRequestStatus ( t , resp )
2021-08-13 07:12:16 -04:00
CheckErrorID ( t , err , "model.command.is_valid.method.app_error" )
2017-03-23 19:42:32 -04:00
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = false } )
2017-03-23 19:42:32 -04:00
newCmd . Method = "P"
2017-06-15 11:05:43 -04:00
newCmd . Trigger = "testcommand"
2023-06-06 17:29:29 -04:00
_ , resp , err = th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-03-23 19:42:32 -04:00
CheckNotImplementedStatus ( t , resp )
2021-08-13 07:12:16 -04:00
CheckErrorID ( t , err , "api.command.disabled.app_error" )
2020-05-22 07:48:22 -04:00
// Confirm that local clients can't override disable command setting
newCmd . Trigger = "LocalOverride"
2023-06-06 17:29:29 -04:00
_ , _ , err = LocalClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
CheckErrorID ( t , err , "api.command.disabled.app_error" )
2017-03-23 19:42:32 -04:00
}
2017-04-03 08:12:50 -04:00
2025-11-13 06:12:30 -05:00
func TestCreateCommandForOtherUser ( t * testing . T ) {
mainHelper . Parallel ( t )
th := Setup ( t ) . InitBasic ( t )
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithOnlyManageOwnCannotCreateForOthers" , func ( t * testing . T ) {
cmdForOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_for_other_fail" ,
}
_ , resp , err := th . Client . CreateCommand ( context . Background ( ) , cmdForOther )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "UserWithManageOthersCanCreateForOthers" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
cmdForOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_for_other_success" ,
}
createdCmd , _ , err := th . Client . CreateCommand ( context . Background ( ) , cmdForOther )
require . NoError ( t , err )
require . Equal ( t , th . BasicUser2 . Id , createdCmd . CreatorId , "command should be owned by BasicUser2" )
require . Equal ( t , th . BasicTeam . Id , createdCmd . TeamId )
} )
t . Run ( "UserWithManageOthersCannotCreateForNonExistentUser" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
cmdForInvalidUser := & model . Command {
CreatorId : model . NewId ( ) , // Non-existent user ID
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_invalid_user" ,
}
_ , resp , err := th . Client . CreateCommand ( context . Background ( ) , cmdForInvalidUser )
require . Error ( t , err )
CheckNotFoundStatus ( t , resp )
} )
t . Run ( "SystemAdminCanCreateForOthers" , func ( t * testing . T ) {
cmdForOther := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_admin_for_other" ,
}
createdCmd , _ , err := th . SystemAdminClient . CreateCommand ( context . Background ( ) , cmdForOther )
require . NoError ( t , err )
require . Equal ( t , th . BasicUser . Id , createdCmd . CreatorId , "command should be owned by BasicUser" )
require . Equal ( t , th . BasicTeam . Id , createdCmd . TeamId )
} )
}
2017-04-07 13:06:09 -04:00
func TestUpdateCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2017-04-07 13:06:09 -04:00
user := th . SystemAdminUser
team := th . BasicTeam
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
2017-04-07 13:06:09 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
2017-04-07 13:06:09 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2017-04-07 13:06:09 -04:00
cmd1 := & model . Command {
CreatorId : user . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-04-07 13:06:09 -04:00
Trigger : "trigger1" ,
}
2017-09-06 18:12:54 -04:00
cmd1 , _ = th . App . CreateCommand ( cmd1 )
2017-04-07 13:06:09 -04:00
cmd2 := & model . Command {
2023-12-20 00:46:54 -05:00
CreatorId : GenerateTestID ( ) ,
2017-04-07 13:06:09 -04:00
TeamId : team . Id ,
URL : "http://nowhere.com/change" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodGet ,
2017-04-07 13:06:09 -04:00
Trigger : "trigger2" ,
Id : cmd1 . Id ,
Token : "tokenchange" ,
}
2020-05-31 10:57:04 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , client * model . Client4 ) {
2023-06-06 17:29:29 -04:00
rcmd , _ , err := client . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Equal ( t , cmd2 . Trigger , rcmd . Trigger , "Trigger should have updated" )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Equal ( t , cmd2 . Method , rcmd . Method , "Method should have updated" )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Equal ( t , cmd2 . URL , rcmd . URL , "URL should have updated" )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Equal ( t , cmd1 . CreatorId , rcmd . CreatorId , "CreatorId should have not updated" )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Equal ( t , cmd1 . Token , rcmd . Token , "Token should have not updated" )
2017-04-07 13:06:09 -04:00
2023-12-20 00:46:54 -05:00
cmd2 . Id = GenerateTestID ( )
2017-04-07 13:06:09 -04:00
2023-06-06 17:29:29 -04:00
rcmd , resp , err := client . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckNotFoundStatus ( t , resp )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
require . Nil ( t , rcmd , "should be empty" )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
cmd2 . Id = "junk"
2017-04-07 13:06:09 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = client . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckBadRequestStatus ( t , resp )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
cmd2 . Id = cmd1 . Id
2023-12-20 00:46:54 -05:00
cmd2 . TeamId = GenerateTestID ( )
2017-04-07 13:06:09 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = client . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckBadRequestStatus ( t , resp )
2017-04-07 13:06:09 -04:00
2020-05-31 10:57:04 -04:00
cmd2 . TeamId = team . Id
2017-04-07 13:06:09 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = th . Client . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckNotFoundStatus ( t , resp )
} )
2025-06-06 01:44:43 -04:00
_ , err := th . SystemAdminClient . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := th . SystemAdminClient . UpdateCommand ( context . Background ( ) , cmd2 )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-04-07 13:06:09 -04:00
CheckUnauthorizedStatus ( t , resp )
2025-11-13 06:12:30 -05:00
// Permission tests
th . LoginBasic ( t )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserCanUpdateTheirOwnCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own" ,
}
createdCmd , _ := th . App . CreateCommand ( cmd )
// Update the command
createdCmd . URL = "http://newurl.com"
updatedCmd , _ , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmd )
require . NoError ( t , err )
require . Equal ( t , "http://newurl.com" , updatedCmd . URL )
} )
t . Run ( "UserWithoutManageOthersCannotUpdateOthersCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other" ,
}
createdCmd , _ := th . App . CreateCommand ( cmd )
// Try to update the command
createdCmd . URL = "http://newurl.com"
_ , resp , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmd )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "UserWithManageOthersCanUpdateOthersCommand" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other2" ,
}
createdCmd , _ := th . App . CreateCommand ( cmd )
// Update the command
createdCmd . URL = "http://newurl.com"
updatedCmd , _ , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmd )
require . NoError ( t , err )
require . Equal ( t , "http://newurl.com" , updatedCmd . URL )
} )
t . Run ( "UserWithOnlyManageOwnCannotUpdateOthersCommand" , func ( t * testing . T ) {
// BasicUser should only have ManageOwn permission (already set up in the test)
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other3" ,
}
createdCmd , _ := th . App . CreateCommand ( cmd )
// Try to update the command
createdCmd . URL = "http://newurl.com"
_ , resp , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmd )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
} )
2026-03-06 07:39:48 -05:00
t . Run ( "CannotUpdateCommandToDuplicateCustomTrigger" , func ( t * testing . T ) {
cmdA := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com/a" ,
Method : model . CommandMethodPost ,
Trigger : "duplicate_custom_a" ,
}
createdCmdA , appErr := th . App . CreateCommand ( cmdA )
require . Nil ( t , appErr )
cmdB := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com/b" ,
Method : model . CommandMethodPost ,
Trigger : "duplicate_custom_b" ,
}
createdCmdB , appErr := th . App . CreateCommand ( cmdB )
require . Nil ( t , appErr )
createdCmdB . Trigger = createdCmdA . Trigger
_ , resp , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmdB )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
CheckErrorID ( t , err , "api.command.duplicate_trigger.app_error" )
} )
t . Run ( "CannotUpdateCommandToBuiltInTrigger" , func ( t * testing . T ) {
cmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com/c" ,
Method : model . CommandMethodPost ,
Trigger : "custom_for_builtin_collision" ,
}
createdCmd , appErr := th . App . CreateCommand ( cmd )
require . Nil ( t , appErr )
createdCmd . Trigger = "join"
_ , resp , err := th . Client . UpdateCommand ( context . Background ( ) , createdCmd )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
CheckErrorID ( t , err , "api.command.duplicate_trigger.app_error" )
} )
2017-04-07 13:06:09 -04:00
}
2020-01-29 11:56:21 -05:00
func TestMoveCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2020-01-29 11:56:21 -05:00
user := th . SystemAdminUser
team := th . BasicTeam
2025-11-12 07:00:51 -05:00
newTeam := th . CreateTeam ( t )
2020-01-29 11:56:21 -05:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
cmd1 := & model . Command {
CreatorId : user . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2020-01-29 11:56:21 -05:00
Trigger : "trigger1" ,
}
rcmd1 , _ := th . App . CreateCommand ( cmd1 )
2020-05-31 10:57:04 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , client * model . Client4 ) {
2023-06-06 17:29:29 -04:00
_ , err := client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd1 . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-01-29 11:56:21 -05:00
2020-05-31 10:57:04 -04:00
rcmd1 , _ = th . App . GetCommand ( rcmd1 . Id )
require . NotNil ( t , rcmd1 )
require . Equal ( t , newTeam . Id , rcmd1 . TeamId )
2020-01-29 11:56:21 -05:00
2023-06-06 17:29:29 -04:00
resp , err := client . MoveCommand ( context . Background ( ) , newTeam . Id , "bogus" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckBadRequestStatus ( t , resp )
2020-01-29 11:56:21 -05:00
2023-12-20 00:46:54 -05:00
resp , err = client . MoveCommand ( context . Background ( ) , GenerateTestID ( ) , rcmd1 . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckNotFoundStatus ( t , resp )
} )
2020-01-29 11:56:21 -05:00
cmd2 := & model . Command {
CreatorId : user . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2020-01-29 11:56:21 -05:00
Trigger : "trigger2" ,
}
rcmd2 , _ := th . App . CreateCommand ( cmd2 )
2023-06-06 17:29:29 -04:00
resp , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd2 . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-29 11:56:21 -05:00
CheckNotFoundStatus ( t , resp )
2025-06-06 01:44:43 -04:00
_ , err = th . SystemAdminClient . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
resp , err = th . SystemAdminClient . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd2 . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-29 11:56:21 -05:00
CheckUnauthorizedStatus ( t , resp )
2025-11-13 06:12:30 -05:00
// Set up for permission tests
th . LoginBasic ( t )
th . LinkUserToTeam ( t , th . BasicUser , newTeam )
th . LinkUserToTeam ( t , th . BasicUser2 , newTeam )
// Give BasicUser permission to manage their own commands on both teams
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithoutManageOthersPermissionCannotMoveOthersCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger3" ,
}
rcmd , _ := th . App . CreateCommand ( cmd )
// BasicUser should not be able to move BasicUser2's command
resp , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd . Id )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
// Verify the command was not moved
movedCmd , _ := th . App . GetCommand ( rcmd . Id )
require . Equal ( t , team . Id , movedCmd . TeamId )
} )
t . Run ( "UserWithManageOthersPermissionCanMoveOthersCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger4" ,
}
rcmd , _ := th . App . CreateCommand ( cmd )
// Give BasicUser the permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Now BasicUser should be able to move BasicUser2's command
_ , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd . Id )
require . NoError ( t , err )
// Verify the command was moved
movedCmd , _ := th . App . GetCommand ( rcmd . Id )
require . Equal ( t , newTeam . Id , movedCmd . TeamId )
} )
t . Run ( "CreatorCanMoveTheirOwnCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger5" ,
}
rcmd , _ := th . App . CreateCommand ( cmd )
// BasicUser should be able to move their own command
_ , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd . Id )
require . NoError ( t , err )
// Verify the command was moved
movedCmd , _ := th . App . GetCommand ( rcmd . Id )
require . Equal ( t , newTeam . Id , movedCmd . TeamId )
} )
t . Run ( "UserWithOnlyManageOwnCannotMoveOthersCommand" , func ( t * testing . T ) {
// BasicUser should only have ManageOwn permission (already set up in the test)
// Create a command owned by BasicUser2
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger6" ,
}
rcmd , _ := th . App . CreateCommand ( cmd )
// BasicUser should not be able to move BasicUser2's command
resp , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , rcmd . Id )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
// Verify the command was not moved
notMovedCmd , _ := th . App . GetCommand ( rcmd . Id )
require . Equal ( t , team . Id , notMovedCmd . TeamId )
} )
t . Run ( "CannotMoveCommandWhenCreatorHasNoPermissionToNewTeam" , func ( t * testing . T ) {
// Create a third team that the command creator (BasicUser2) is NOT a member of
thirdTeam := th . CreateTeam ( t )
th . LinkUserToTeam ( t , th . BasicUser , thirdTeam )
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser2
// Note: BasicUser2 is NOT a member of thirdTeam (only member of team and newTeam)
cmd := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger7" ,
}
rcmd , _ := th . App . CreateCommand ( cmd )
// BasicUser attempts to move BasicUser2's command to thirdTeam
// This should fail because BasicUser2 doesn't have permission to thirdTeam
resp , err := th . Client . MoveCommand ( context . Background ( ) , thirdTeam . Id , rcmd . Id )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
// Verify the command was not moved
notMovedCmd , _ := th . App . GetCommand ( rcmd . Id )
require . Equal ( t , team . Id , notMovedCmd . TeamId )
} )
2026-03-06 07:39:48 -05:00
t . Run ( "CannotMoveCommandToTeamWithDuplicateTrigger" , func ( t * testing . T ) {
trigger := "move_duplicate_trigger"
sourceCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com/source" ,
Method : model . CommandMethodPost ,
Trigger : trigger ,
}
sourceCreatedCmd , appErr := th . App . CreateCommand ( sourceCmd )
require . Nil ( t , appErr )
targetCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : newTeam . Id ,
URL : "http://nowhere.com/target" ,
Method : model . CommandMethodPost ,
Trigger : trigger ,
}
_ , appErr = th . App . CreateCommand ( targetCmd )
require . Nil ( t , appErr )
resp , err := th . Client . MoveCommand ( context . Background ( ) , newTeam . Id , sourceCreatedCmd . Id )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
CheckErrorID ( t , err , "api.command.duplicate_trigger.app_error" )
notMovedCmd , _ := th . App . GetCommand ( sourceCreatedCmd . Id )
require . Equal ( t , team . Id , notMovedCmd . TeamId )
} )
2020-01-29 11:56:21 -05:00
}
2017-04-10 09:27:10 -04:00
func TestDeleteCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2017-04-10 09:27:10 -04:00
user := th . SystemAdminUser
team := th . BasicTeam
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
2017-04-10 09:27:10 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
2017-04-10 09:27:10 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2017-04-10 09:27:10 -04:00
cmd1 := & model . Command {
CreatorId : user . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-04-10 09:27:10 -04:00
Trigger : "trigger1" ,
}
2020-05-31 10:57:04 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , client * model . Client4 ) {
cmd1 . Id = ""
2021-08-13 07:12:16 -04:00
rcmd1 , appErr := th . App . CreateCommand ( cmd1 )
require . Nil ( t , appErr )
2023-06-06 17:29:29 -04:00
_ , err := client . DeleteCommand ( context . Background ( ) , rcmd1 . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-10 09:27:10 -04:00
2020-05-31 10:57:04 -04:00
rcmd1 , _ = th . App . GetCommand ( rcmd1 . Id )
require . Nil ( t , rcmd1 )
2017-04-10 09:27:10 -04:00
2023-06-06 17:29:29 -04:00
resp , err := client . DeleteCommand ( context . Background ( ) , "junk" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckBadRequestStatus ( t , resp )
2017-04-10 09:27:10 -04:00
2023-12-20 00:46:54 -05:00
resp , err = client . DeleteCommand ( context . Background ( ) , GenerateTestID ( ) )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
CheckNotFoundStatus ( t , resp )
} )
2017-04-10 09:27:10 -04:00
cmd2 := & model . Command {
CreatorId : user . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-04-10 09:27:10 -04:00
Trigger : "trigger2" ,
}
2017-09-06 18:12:54 -04:00
rcmd2 , _ := th . App . CreateCommand ( cmd2 )
2017-04-10 09:27:10 -04:00
2023-06-06 17:29:29 -04:00
resp , err := th . Client . DeleteCommand ( context . Background ( ) , rcmd2 . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-29 11:56:21 -05:00
CheckNotFoundStatus ( t , resp )
2017-04-10 09:27:10 -04:00
2025-06-06 01:44:43 -04:00
_ , err = th . SystemAdminClient . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
resp , err = th . SystemAdminClient . DeleteCommand ( context . Background ( ) , rcmd2 . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-04-10 09:27:10 -04:00
CheckUnauthorizedStatus ( t , resp )
2025-11-13 06:12:30 -05:00
// Permission tests for ManageOwn vs ManageOthers
th . LoginBasic ( t )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithManageOwnCanDeleteOnlyOwnCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_delete" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_delete" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// Should be able to delete own command
_ , err := th . Client . DeleteCommand ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
// Verify the command was deleted
deletedCmd , _ := th . App . GetCommand ( createdCmdOwn . Id )
require . Nil ( t , deletedCmd )
// Should not be able to delete other user's command
resp , err := th . Client . DeleteCommand ( context . Background ( ) , createdCmdOther . Id )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
// Verify the command was not deleted
notDeletedCmd , _ := th . App . GetCommand ( createdCmdOther . Id )
require . NotNil ( t , notDeletedCmd )
} )
t . Run ( "UserWithManageOthersCanDeleteAnyCommand" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_delete2" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : team . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_delete2" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// Should be able to delete own command
_ , err := th . Client . DeleteCommand ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
// Verify the command was deleted
deletedCmd , _ := th . App . GetCommand ( createdCmdOwn . Id )
require . Nil ( t , deletedCmd )
// Should be able to delete other user's command
_ , err = th . Client . DeleteCommand ( context . Background ( ) , createdCmdOther . Id )
require . NoError ( t , err )
// Verify the command was deleted
deletedCmd , _ = th . App . GetCommand ( createdCmdOther . Id )
require . Nil ( t , deletedCmd )
} )
2017-04-10 09:27:10 -04:00
}
2017-04-03 08:12:50 -04:00
func TestListCommands ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2017-04-03 08:12:50 -04:00
2018-04-03 15:41:02 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2017-04-03 08:12:50 -04:00
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "custom_command" ,
2025-05-30 07:58:26 -04:00
}
2025-06-06 01:44:43 -04:00
_ , _ , rootErr := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
require . NoError ( t , rootErr )
2020-05-22 07:48:22 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , c * model . Client4 ) {
2023-06-06 17:29:29 -04:00
listCommands , _ , err := c . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-03 08:12:50 -04:00
foundEcho := false
foundCustom := false
for _ , command := range listCommands {
if command . Trigger == "echo" {
foundEcho = true
}
if command . Trigger == "custom_command" {
foundCustom = true
}
}
2019-10-24 09:32:31 -04:00
require . True ( t , foundEcho , "Couldn't find echo command" )
require . True ( t , foundCustom , "Should list the custom command" )
2020-05-22 07:48:22 -04:00
} , "ListSystemAndCustomCommands" )
2017-04-03 08:12:50 -04:00
2020-05-22 07:48:22 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , c * model . Client4 ) {
2023-06-06 17:29:29 -04:00
listCommands , _ , err := c . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-03 08:12:50 -04:00
2019-10-24 09:32:31 -04:00
require . Len ( t , listCommands , 1 , "Should list just one custom command" )
require . Equal ( t , listCommands [ 0 ] . Trigger , "custom_command" , "Wrong custom command trigger" )
2020-05-22 07:48:22 -04:00
} , "ListCustomOnlyCommands" )
2017-04-03 08:12:50 -04:00
t . Run ( "UserWithNoPermissionForCustomCommands" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-04-03 08:12:50 -04:00
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "RegularUserCanListOnlySystemCommands" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
listCommands , _ , err := client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-03 08:12:50 -04:00
foundEcho := false
foundCustom := false
for _ , command := range listCommands {
if command . Trigger == "echo" {
foundEcho = true
}
if command . Trigger == "custom_command" {
foundCustom = true
}
}
2019-10-24 09:32:31 -04:00
require . True ( t , foundEcho , "Couldn't find echo command" )
require . False ( t , foundCustom , "Should not list the custom command" )
2017-04-03 08:12:50 -04:00
} )
2019-07-04 08:52:47 -04:00
t . Run ( "NoMember" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2025-11-12 07:00:51 -05:00
user := th . CreateUser ( t )
2025-06-06 01:44:43 -04:00
_ , _ , err = client . Login ( context . Background ( ) , user . Email , user . Password )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckForbiddenStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "NotLoggedIn" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckUnauthorizedStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckUnauthorizedStatus ( t , resp )
} )
2025-11-13 06:12:30 -05:00
// Permission tests for ManageOwn vs ManageOthers
th . LoginBasic ( t )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithManageOwnCanListOnlyOwnCustomCommands" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_list" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_list" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// List custom commands only
listCommands , _ , err := th . Client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
require . NoError ( t , err )
foundOwn := false
foundOther := false
for _ , command := range listCommands {
if command . Id == createdCmdOwn . Id {
foundOwn = true
}
if command . Id == createdCmdOther . Id {
foundOther = true
}
}
require . True ( t , foundOwn , "Should list own command" )
require . False ( t , foundOther , "Should not list other user's command" )
// List all commands (system + custom)
listCommandsAll , _ , err := th . Client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
require . NoError ( t , err )
foundOwn = false
foundOther = false
foundSystem := false
for _ , command := range listCommandsAll {
if command . Id == createdCmdOwn . Id {
foundOwn = true
}
if command . Id == createdCmdOther . Id {
foundOther = true
}
if command . Trigger == "echo" {
foundSystem = true
}
}
require . True ( t , foundOwn , "Should list own command" )
require . False ( t , foundOther , "Should not list other user's command" )
require . True ( t , foundSystem , "Should list system commands" )
} )
t . Run ( "UserWithManageOthersCanListAllCustomCommands" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_list2" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_list2" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// List custom commands only
listCommands , _ , err := th . Client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , true )
require . NoError ( t , err )
foundOwn := false
foundOther := false
for _ , command := range listCommands {
if command . Id == createdCmdOwn . Id {
foundOwn = true
}
if command . Id == createdCmdOther . Id {
foundOther = true
}
}
require . True ( t , foundOwn , "Should list own command" )
require . True ( t , foundOther , "Should list other user's command" )
// List all commands (system + custom)
listCommandsAll , _ , err := th . Client . ListCommands ( context . Background ( ) , th . BasicTeam . Id , false )
require . NoError ( t , err )
foundOwn = false
foundOther = false
for _ , command := range listCommandsAll {
if command . Id == createdCmdOwn . Id {
foundOwn = true
}
if command . Id == createdCmdOther . Id {
foundOther = true
}
}
require . True ( t , foundOwn , "Should list own command" )
require . True ( t , foundOther , "Should list other user's command" )
} )
2017-04-03 08:12:50 -04:00
}
2017-04-04 00:20:04 -04:00
func TestListAutocompleteCommands ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2017-04-04 00:20:04 -04:00
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "custom_command" ,
2025-05-30 07:58:26 -04:00
}
2017-04-04 00:20:04 -04:00
2023-06-06 17:29:29 -04:00
_ , _ , err := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-04 00:20:04 -04:00
t . Run ( "ListAutocompleteCommandsOnly" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
listCommands , _ , err := th . SystemAdminClient . ListAutocompleteCommands ( context . Background ( ) , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-04 00:20:04 -04:00
foundEcho := false
foundCustom := false
for _ , command := range listCommands {
if command . Trigger == "echo" {
foundEcho = true
}
if command . Trigger == "custom_command" {
foundCustom = true
}
}
2019-10-24 09:32:31 -04:00
require . True ( t , foundEcho , "Couldn't find echo command" )
require . False ( t , foundCustom , "Should not list the custom command" )
2017-04-04 00:20:04 -04:00
} )
t . Run ( "RegularUserCanListOnlySystemCommands" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
listCommands , _ , err := client . ListAutocompleteCommands ( context . Background ( ) , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-04 00:20:04 -04:00
foundEcho := false
foundCustom := false
for _ , command := range listCommands {
if command . Trigger == "echo" {
foundEcho = true
}
if command . Trigger == "custom_command" {
foundCustom = true
}
}
2019-10-24 09:32:31 -04:00
require . True ( t , foundEcho , "Couldn't find echo command" )
require . False ( t , foundCustom , "Should not list the custom command" )
2017-04-04 00:20:04 -04:00
} )
2019-07-04 08:52:47 -04:00
t . Run ( "NoMember" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2025-11-12 07:00:51 -05:00
user := th . CreateUser ( t )
2025-06-06 01:44:43 -04:00
_ , _ , err = client . Login ( context . Background ( ) , user . Email , user . Password )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListAutocompleteCommands ( context . Background ( ) , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "NotLoggedIn" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListAutocompleteCommands ( context . Background ( ) , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2019-07-04 08:52:47 -04:00
CheckUnauthorizedStatus ( t , resp )
} )
2017-04-04 00:20:04 -04:00
}
2017-04-16 16:49:57 -04:00
2020-05-21 04:24:56 -04:00
func TestListCommandAutocompleteSuggestions ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2020-05-21 04:24:56 -04:00
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "custom_command" ,
2025-05-30 07:58:26 -04:00
}
2020-05-21 04:24:56 -04:00
2023-06-06 17:29:29 -04:00
_ , _ , err := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-21 04:24:56 -04:00
t . Run ( "ListAutocompleteSuggestionsOnly" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
suggestions , _ , err := th . SystemAdminClient . ListCommandAutocompleteSuggestions ( context . Background ( ) , "/" , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-21 04:24:56 -04:00
foundEcho := false
foundShrug := false
foundCustom := false
for _ , command := range suggestions {
if command . Suggestion == "echo" {
foundEcho = true
}
if command . Suggestion == "shrug" {
foundShrug = true
}
if command . Suggestion == "custom_command" {
foundCustom = true
}
}
require . True ( t , foundEcho , "Couldn't find echo command" )
require . True ( t , foundShrug , "Couldn't find shrug command" )
require . False ( t , foundCustom , "Should not list the custom command" )
} )
t . Run ( "ListAutocompleteSuggestionsOnlyWithInput" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
suggestions , _ , err := th . SystemAdminClient . ListCommandAutocompleteSuggestions ( context . Background ( ) , "/e" , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-21 04:24:56 -04:00
foundEcho := false
foundShrug := false
for _ , command := range suggestions {
if command . Suggestion == "echo" {
foundEcho = true
}
if command . Suggestion == "shrug" {
foundShrug = true
}
}
require . True ( t , foundEcho , "Couldn't find echo command" )
require . False ( t , foundShrug , "Should not list the shrug command" )
} )
t . Run ( "RegularUserCanListOnlySystemCommands" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
suggestions , _ , err := client . ListCommandAutocompleteSuggestions ( context . Background ( ) , "/" , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-21 04:24:56 -04:00
foundEcho := false
foundCustom := false
for _ , suggestion := range suggestions {
if suggestion . Suggestion == "echo" {
foundEcho = true
}
if suggestion . Suggestion == "custom_command" {
foundCustom = true
}
}
require . True ( t , foundEcho , "Couldn't find echo command" )
require . False ( t , foundCustom , "Should not list the custom command" )
} )
t . Run ( "NoMember" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2025-11-12 07:00:51 -05:00
user := th . CreateUser ( t )
2025-06-06 01:44:43 -04:00
_ , _ , err = client . Login ( context . Background ( ) , user . Email , user . Password )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListCommandAutocompleteSuggestions ( context . Background ( ) , "/" , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-21 04:24:56 -04:00
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "NotLoggedIn" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ListCommandAutocompleteSuggestions ( context . Background ( ) , "/" , th . BasicTeam . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-21 04:24:56 -04:00
CheckUnauthorizedStatus ( t , resp )
} )
}
2020-01-24 09:32:56 -05:00
func TestGetCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2020-01-24 09:32:56 -05:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "roger" ,
2025-05-30 07:58:26 -04:00
}
2025-06-06 01:44:43 -04:00
newCmd , _ , rootErr := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
require . NoError ( t , rootErr )
2020-05-31 10:57:04 -04:00
th . TestForSystemAdminAndLocal ( t , func ( t * testing . T , client * model . Client4 ) {
t . Run ( "ValidId" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
cmd , _ , err := client . GetCommandById ( context . Background ( ) , newCmd . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2020-05-31 10:57:04 -04:00
require . Equal ( t , newCmd . Id , cmd . Id )
require . Equal ( t , newCmd . CreatorId , cmd . CreatorId )
require . Equal ( t , newCmd . TeamId , cmd . TeamId )
require . Equal ( t , newCmd . URL , cmd . URL )
require . Equal ( t , newCmd . Method , cmd . Method )
require . Equal ( t , newCmd . Trigger , cmd . Trigger )
} )
t . Run ( "InvalidId" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
_ , _ , err := client . GetCommandById ( context . Background ( ) , strings . Repeat ( "z" , len ( newCmd . Id ) ) )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-05-31 10:57:04 -04:00
} )
2020-01-24 09:32:56 -05:00
} )
t . Run ( "UserWithNoPermissionForCustomCommands" , func ( t * testing . T ) {
2023-06-06 17:29:29 -04:00
_ , resp , err := th . Client . GetCommandById ( context . Background ( ) , newCmd . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-24 09:32:56 -05:00
CheckNotFoundStatus ( t , resp )
} )
t . Run ( "NoMember" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := th . Client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2025-11-12 07:00:51 -05:00
user := th . CreateUser ( t )
2025-06-06 01:44:43 -04:00
_ , _ , err = th . Client . Login ( context . Background ( ) , user . Email , user . Password )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := th . Client . GetCommandById ( context . Background ( ) , newCmd . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-24 09:32:56 -05:00
CheckNotFoundStatus ( t , resp )
} )
t . Run ( "NotLoggedIn" , func ( t * testing . T ) {
2025-06-06 01:44:43 -04:00
_ , err := th . Client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2023-06-06 17:29:29 -04:00
_ , resp , err := th . Client . GetCommandById ( context . Background ( ) , newCmd . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-24 09:32:56 -05:00
CheckUnauthorizedStatus ( t , resp )
} )
2025-11-13 06:12:30 -05:00
// Permission tests for ManageOwn vs ManageOthers
th . LoginBasic ( t )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithManageOwnCanGetOnlyOwnCommand" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_get" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_get" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// Should be able to get own command
cmd , _ , err := th . Client . GetCommandById ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
require . Equal ( t , createdCmdOwn . Id , cmd . Id )
// Should not be able to get other user's command
_ , resp , err := th . Client . GetCommandById ( context . Background ( ) , createdCmdOther . Id )
require . Error ( t , err )
CheckNotFoundStatus ( t , resp )
} )
t . Run ( "UserWithManageOthersCanGetAnyCommand" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_get2" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_get2" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// Should be able to get own command
cmd , _ , err := th . Client . GetCommandById ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
require . Equal ( t , createdCmdOwn . Id , cmd . Id )
// Should be able to get other user's command
cmd , _ , err = th . Client . GetCommandById ( context . Background ( ) , createdCmdOther . Id )
require . NoError ( t , err )
require . Equal ( t , createdCmdOther . Id , cmd . Id )
} )
2020-01-24 09:32:56 -05:00
}
2017-04-16 16:49:57 -04:00
func TestRegenToken ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2017-04-16 16:49:57 -04:00
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
2017-04-16 16:49:57 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
2017-04-16 16:49:57 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2017-04-16 16:49:57 -04:00
newCmd := & model . Command {
2025-11-13 06:12:30 -05:00
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger" ,
2025-05-30 07:58:26 -04:00
}
2017-04-16 16:49:57 -04:00
2023-06-06 17:29:29 -04:00
createdCmd , resp , err := th . SystemAdminClient . CreateCommand ( context . Background ( ) , newCmd )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-04-16 16:49:57 -04:00
CheckCreatedStatus ( t , resp )
2023-06-06 17:29:29 -04:00
token , _ , err := th . SystemAdminClient . RegenCommandToken ( context . Background ( ) , createdCmd . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2019-10-24 09:32:31 -04:00
require . NotEqual ( t , createdCmd . Token , token , "should update the token" )
2017-04-16 16:49:57 -04:00
2023-06-06 17:29:29 -04:00
token , resp , err = client . RegenCommandToken ( context . Background ( ) , createdCmd . Id )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2020-01-29 11:56:21 -05:00
CheckNotFoundStatus ( t , resp )
2019-10-24 09:32:31 -04:00
require . Empty ( t , token , "should not return the token" )
2025-11-13 06:12:30 -05:00
// Permission tests for ManageOwn vs ManageOthers
th . LoginBasic ( t )
// Give BasicUser permission to manage their own commands
th . AddPermissionToRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOwnSlashCommands . Id , model . TeamUserRoleId )
t . Run ( "UserWithManageOwnCanRegenOnlyOwnCommandToken" , func ( t * testing . T ) {
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_regen" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
oldToken := createdCmdOwn . Token
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_regen" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
// Should be able to regenerate own command token
newToken , _ , err := th . Client . RegenCommandToken ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
require . NotEqual ( t , oldToken , newToken )
// Should not be able to regenerate other user's command token
_ , resp , err := th . Client . RegenCommandToken ( context . Background ( ) , createdCmdOther . Id )
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
} )
t . Run ( "UserWithManageOthersCanRegenAnyCommandToken" , func ( t * testing . T ) {
// Give BasicUser permission to manage others' commands
th . AddPermissionToRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
defer th . RemovePermissionFromRole ( t , model . PermissionManageOthersSlashCommands . Id , model . TeamUserRoleId )
// Create a command owned by BasicUser
cmdOwn := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_own_regen2" ,
}
createdCmdOwn , _ := th . App . CreateCommand ( cmdOwn )
oldTokenOwn := createdCmdOwn . Token
// Create a command owned by BasicUser2
cmdOther := & model . Command {
CreatorId : th . BasicUser2 . Id ,
TeamId : th . BasicTeam . Id ,
URL : "http://nowhere.com" ,
Method : model . CommandMethodPost ,
Trigger : "trigger_other_regen2" ,
}
createdCmdOther , _ := th . App . CreateCommand ( cmdOther )
oldTokenOther := createdCmdOther . Token
// Should be able to regenerate own command token
newToken , _ , err := th . Client . RegenCommandToken ( context . Background ( ) , createdCmdOwn . Id )
require . NoError ( t , err )
require . NotEqual ( t , oldTokenOwn , newToken )
// Should be able to regenerate other user's command token
newToken , _ , err = th . Client . RegenCommandToken ( context . Background ( ) , createdCmdOther . Id )
require . NoError ( t , err )
require . NotEqual ( t , oldTokenOther , newToken )
} )
2017-04-27 09:41:52 -04:00
}
2018-05-14 13:24:22 -04:00
func TestExecuteInvalidCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2017-04-27 09:41:52 -04:00
channel := th . BasicChannel
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2017-04-27 09:41:52 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2017-04-27 09:41:52 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2018-05-14 13:24:22 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . AllowedUntrustedInternalConnections = "127.0.0.0/8" } )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2018-05-14 13:24:22 -04:00
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
rc := & model . CommandResponse { }
2017-04-16 16:49:57 -04:00
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( rc ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-05-14 13:24:22 -04:00
} ) )
defer ts . Close ( )
getCmd := & model . Command {
2017-04-27 09:41:52 -04:00
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodGet ,
2018-05-14 13:24:22 -04:00
Trigger : "getcommand" ,
2017-04-27 09:41:52 -04:00
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( getCmd )
require . Nil ( t , appErr , "failed to create get command" )
2017-04-27 09:41:52 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckBadRequestStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "/" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckBadRequestStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "getcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckBadRequestStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "/junk" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckNotFoundStatus ( t , resp )
2025-11-12 07:00:51 -05:00
otherUser := th . CreateUser ( t )
2025-06-06 01:44:43 -04:00
_ , _ , err = client . Login ( context . Background ( ) , otherUser . Email , otherUser . Password )
require . NoError ( t , err )
2018-05-14 13:24:22 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "/getcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckForbiddenStatus ( t , resp )
2025-06-06 01:44:43 -04:00
_ , err = client . Logout ( context . Background ( ) )
require . NoError ( t , err )
2018-05-14 13:24:22 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "/getcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2018-05-14 13:24:22 -04:00
CheckUnauthorizedStatus ( t , resp )
2023-06-06 17:29:29 -04:00
_ , _ , err = th . SystemAdminClient . ExecuteCommand ( context . Background ( ) , channel . Id , "/getcommand" )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2018-05-14 13:24:22 -04:00
}
2017-04-27 09:41:52 -04:00
2018-05-14 13:24:22 -04:00
func TestExecuteGetCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2018-05-14 13:24:22 -04:00
channel := th . BasicChannel
2017-04-27 09:41:52 -04:00
2018-05-14 13:24:22 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2018-05-14 13:24:22 -04:00
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2018-05-14 13:24:22 -04:00
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . AllowedUntrustedInternalConnections = "127.0.0.0/8" } )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2018-05-14 13:24:22 -04:00
token := model . NewId ( )
expectedCommandResponse := & model . CommandResponse {
Text : "test get command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-05-14 13:24:22 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2017-04-27 09:41:52 -04:00
}
2018-05-14 13:24:22 -04:00
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
require . Equal ( t , http . MethodGet , r . Method )
2017-09-28 12:08:16 -04:00
2018-05-14 13:24:22 -04:00
values , err := url . ParseQuery ( r . URL . RawQuery )
require . NoError ( t , err )
2017-09-28 12:08:16 -04:00
2018-05-14 13:24:22 -04:00
require . Equal ( t , token , values . Get ( "token" ) )
require . Equal ( t , th . BasicTeam . Name , values . Get ( "team_domain" ) )
2018-09-11 15:58:57 -04:00
require . Equal ( t , "ourCommand" , values . Get ( "cmd" ) )
2017-08-30 16:29:32 -04:00
2018-05-14 13:24:22 -04:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-05-14 13:24:22 -04:00
} ) )
defer ts . Close ( )
2017-08-30 16:29:32 -04:00
2017-04-27 09:41:52 -04:00
getCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
2018-09-11 15:58:57 -04:00
URL : ts . URL + "/?cmd=ourCommand" ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodGet ,
2017-04-27 09:41:52 -04:00
Trigger : "getcommand" ,
2018-05-14 13:24:22 -04:00
Token : token ,
2017-04-27 09:41:52 -04:00
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( getCmd )
require . Nil ( t , appErr , "failed to create get command" )
2017-04-27 09:41:52 -04:00
2023-06-06 17:29:29 -04:00
commandResponse , _ , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "/getcommand" )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2018-11-19 15:27:17 -05:00
assert . True ( t , len ( commandResponse . TriggerId ) == 26 )
2017-04-27 09:41:52 -04:00
2018-11-19 15:27:17 -05:00
expectedCommandResponse . TriggerId = commandResponse . TriggerId
2018-05-14 13:24:22 -04:00
require . Equal ( t , expectedCommandResponse , commandResponse )
}
func TestExecutePostCommand ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2018-05-14 13:24:22 -04:00
channel := th . BasicChannel
2017-04-27 09:41:52 -04:00
2018-05-14 13:24:22 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2018-05-14 13:24:22 -04:00
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2018-05-14 13:24:22 -04:00
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . AllowedUntrustedInternalConnections = "127.0.0.0/8" } )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2018-05-14 13:24:22 -04:00
token := model . NewId ( )
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-05-14 13:24:22 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2017-04-27 09:41:52 -04:00
}
2018-05-14 13:24:22 -04:00
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
require . Equal ( t , http . MethodPost , r . Method )
2017-04-27 09:41:52 -04:00
2025-06-06 01:44:43 -04:00
err := r . ParseForm ( )
require . NoError ( t , err )
2017-04-27 09:41:52 -04:00
2018-05-14 13:24:22 -04:00
require . Equal ( t , token , r . FormValue ( "token" ) )
require . Equal ( t , th . BasicTeam . Name , r . FormValue ( "team_domain" ) )
2017-04-27 09:41:52 -04:00
2018-05-14 13:24:22 -04:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-05-14 13:24:22 -04:00
} ) )
defer ts . Close ( )
2017-04-27 09:41:52 -04:00
2018-07-06 09:07:42 -04:00
postCmd := & model . Command {
2018-05-14 13:24:22 -04:00
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2018-05-14 13:24:22 -04:00
Trigger : "postcommand" ,
Token : token ,
}
2017-04-27 09:41:52 -04:00
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create get command" )
2017-04-27 09:41:52 -04:00
2023-06-06 17:29:29 -04:00
commandResponse , _ , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2018-11-19 15:27:17 -05:00
assert . True ( t , len ( commandResponse . TriggerId ) == 26 )
2017-04-27 09:41:52 -04:00
2018-11-19 15:27:17 -05:00
expectedCommandResponse . TriggerId = commandResponse . TriggerId
2018-05-14 13:24:22 -04:00
require . Equal ( t , expectedCommandResponse , commandResponse )
2017-04-16 16:49:57 -04:00
}
2017-10-04 11:12:13 -04:00
func TestExecuteCommandAgainstChannelOnAnotherTeam ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2021-08-13 07:12:16 -04:00
client := th . Client
2017-10-04 11:12:13 -04:00
channel := th . BasicChannel
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2017-10-04 11:12:13 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2017-10-04 11:12:13 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2018-07-06 09:07:42 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) {
2019-07-17 10:04:09 -04:00
* cfg . ServiceSettings . AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
2018-07-06 09:07:42 -04:00
} )
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-07-06 09:07:42 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2018-07-06 09:07:42 -04:00
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-07-06 09:07:42 -04:00
} ) )
defer ts . Close ( )
2017-10-04 11:12:13 -04:00
// create a slash command on some other team where we have permission to do so
2025-11-12 07:00:51 -05:00
team2 := th . CreateTeam ( t )
2017-10-04 11:12:13 -04:00
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team2 . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-10-04 11:12:13 -04:00
Trigger : "postcommand" ,
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create post command" )
2017-10-04 11:12:13 -04:00
// the execute command endpoint will always search for the command by trigger and team id, inferring team id from the
// channel id, so there is no way to use that slash command on a channel that belongs to some other team
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-10-04 11:12:13 -04:00
CheckNotFoundStatus ( t , resp )
}
2017-10-17 13:21:12 -04:00
func TestExecuteCommandAgainstChannelUserIsNotIn ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2017-10-17 13:21:12 -04:00
client := th . Client
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2017-10-17 13:21:12 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2017-10-17 13:21:12 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2018-07-06 09:07:42 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) {
2019-07-17 10:04:09 -04:00
* cfg . ServiceSettings . AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
2018-07-06 09:07:42 -04:00
} )
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-07-06 09:07:42 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2018-07-06 09:07:42 -04:00
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-07-06 09:07:42 -04:00
} ) )
defer ts . Close ( )
2017-10-17 13:21:12 -04:00
// create a slash command on some other team where we have permission to do so
2025-11-12 07:00:51 -05:00
team2 := th . CreateTeam ( t )
2017-10-17 13:21:12 -04:00
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team2 . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-10-17 13:21:12 -04:00
Trigger : "postcommand" ,
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create post command" )
2017-10-17 13:21:12 -04:00
// make a channel on that team, ensuring that our test user isn't in it
2025-11-12 07:00:51 -05:00
channel2 := th . CreateChannelWithClientAndTeam ( t , client , model . ChannelTypeOpen , team2 . Id )
2023-06-06 17:29:29 -04:00
_ , err := th . Client . RemoveUserFromChannel ( context . Background ( ) , channel2 . Id , th . BasicUser . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err , "Failed to remove user from channel" )
2017-10-17 13:21:12 -04:00
// we should not be able to run the slash command in channel2, because we aren't in it
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommandWithTeam ( context . Background ( ) , channel2 . Id , team2 . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-10-17 13:21:12 -04:00
CheckForbiddenStatus ( t , resp )
}
func TestExecuteCommandInDirectMessageChannel ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2017-10-17 13:21:12 -04:00
client := th . Client
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2017-10-17 13:21:12 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2017-10-17 13:21:12 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2018-07-06 09:07:42 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) {
2019-07-17 10:04:09 -04:00
* cfg . ServiceSettings . AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
2018-07-06 09:07:42 -04:00
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2017-10-17 13:21:12 -04:00
2018-07-06 09:07:42 -04:00
// create a team that the user isn't a part of
2025-11-12 07:00:51 -05:00
team2 := th . CreateTeam ( t )
2018-07-06 09:07:42 -04:00
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-07-06 09:07:42 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2018-07-06 09:07:42 -04:00
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
require . Equal ( t , http . MethodPost , r . Method )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-07-06 09:07:42 -04:00
} ) )
defer ts . Close ( )
// create a slash command on some other team where we have permission to do so
2017-10-17 13:21:12 -04:00
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team2 . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-10-17 13:21:12 -04:00
Trigger : "postcommand" ,
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create post command" )
2017-10-17 13:21:12 -04:00
// make a direct message channel
2023-06-06 17:29:29 -04:00
dmChannel , response , err := client . CreateDirectChannel ( context . Background ( ) , th . BasicUser . Id , th . BasicUser2 . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-10-17 13:21:12 -04:00
CheckCreatedStatus ( t , response )
// we should be able to run the slash command in the DM channel
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommandWithTeam ( context . Background ( ) , dmChannel . Id , team2 . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-10-17 13:21:12 -04:00
CheckOKStatus ( t , resp )
// but we can't run the slash command in the DM channel if we sub in some other team's id
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommandWithTeam ( context . Background ( ) , dmChannel . Id , th . BasicTeam . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-10-17 13:21:12 -04:00
CheckNotFoundStatus ( t , resp )
}
func TestExecuteCommandInTeamUserIsNotOn ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2017-10-17 13:21:12 -04:00
client := th . Client
2017-10-18 18:36:43 -04:00
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2017-10-17 13:21:12 -04:00
defer func ( ) {
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2017-10-17 13:21:12 -04:00
} ( )
2017-10-18 18:36:43 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
2018-07-06 09:07:42 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) {
2019-07-17 10:04:09 -04:00
* cfg . ServiceSettings . AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
2018-07-06 09:07:42 -04:00
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2017-10-17 13:21:12 -04:00
// create a team that the user isn't a part of
2025-11-12 07:00:51 -05:00
team2 := th . CreateTeam ( t )
2017-10-17 13:21:12 -04:00
2018-07-06 09:07:42 -04:00
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
2021-07-12 14:05:36 -04:00
ResponseType : model . CommandResponseTypeInChannel ,
2018-07-06 09:07:42 -04:00
Type : "custom_test" ,
2022-07-05 02:46:50 -04:00
Props : map [ string ] any { "someprop" : "somevalue" } ,
2018-07-06 09:07:42 -04:00
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
require . Equal ( t , http . MethodPost , r . Method )
2025-06-06 01:44:43 -04:00
err := r . ParseForm ( )
require . NoError ( t , err )
2018-07-06 09:07:42 -04:00
require . Equal ( t , team2 . Name , r . FormValue ( "team_domain" ) )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-07-26 04:11:02 -04:00
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
2022-07-28 11:05:03 -04:00
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
2021-07-26 04:11:02 -04:00
}
2018-07-06 09:07:42 -04:00
} ) )
defer ts . Close ( )
2017-10-17 13:21:12 -04:00
// create a slash command on that team
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : team2 . Id ,
2018-07-06 09:07:42 -04:00
URL : ts . URL ,
2021-07-12 14:05:36 -04:00
Method : model . CommandMethodPost ,
2017-10-17 13:21:12 -04:00
Trigger : "postcommand" ,
}
2021-08-13 07:12:16 -04:00
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create post command" )
2017-10-17 13:21:12 -04:00
// make a direct message channel
2023-06-06 17:29:29 -04:00
dmChannel , response , err := client . CreateDirectChannel ( context . Background ( ) , th . BasicUser . Id , th . BasicUser2 . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-10-17 13:21:12 -04:00
CheckCreatedStatus ( t , response )
// we should be able to run the slash command in the DM channel
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommandWithTeam ( context . Background ( ) , dmChannel . Id , team2 . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err )
2017-10-17 13:21:12 -04:00
CheckOKStatus ( t , resp )
// if the user is removed from the team, they should NOT be able to run the slash command in the DM channel
2023-06-06 17:29:29 -04:00
_ , err = th . Client . RemoveTeamMember ( context . Background ( ) , team2 . Id , th . BasicUser . Id )
2021-08-13 07:12:16 -04:00
require . NoError ( t , err , "Failed to remove user from team" )
2019-10-24 09:32:31 -04:00
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommandWithTeam ( context . Background ( ) , dmChannel . Id , team2 . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-10-17 13:21:12 -04:00
CheckForbiddenStatus ( t , resp )
// if we omit the team id from the request, the slash command will fail because this is a DM channel, and the
// team id can't be inherited from the channel
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommand ( context . Background ( ) , dmChannel . Id , "/postcommand" )
2021-08-13 07:12:16 -04:00
require . Error ( t , err )
2017-10-17 13:21:12 -04:00
CheckForbiddenStatus ( t , resp )
}
2023-04-18 15:54:20 -04:00
func TestExecuteCommandReadOnly ( t * testing . T ) {
2025-05-30 07:58:26 -04:00
mainHelper . Parallel ( t )
2025-11-12 07:00:51 -05:00
th := Setup ( t ) . InitBasic ( t )
2023-04-18 15:54:20 -04:00
client := th . Client
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
2026-03-25 12:06:34 -04:00
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
2023-04-18 15:54:20 -04:00
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
2023-04-18 15:54:20 -04:00
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
* cfg . ServiceSettings . AllowedUntrustedInternalConnections = "localhost,127.0.0.1"
} )
2026-03-25 12:06:34 -04:00
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
2023-04-18 15:54:20 -04:00
expectedCommandResponse := & model . CommandResponse {
Text : "test post command response" ,
ResponseType : model . CommandResponseTypeInChannel ,
Type : "custom_test" ,
Props : map [ string ] any { "someprop" : "somevalue" } ,
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
require . Equal ( t , http . MethodPost , r . Method )
2025-06-06 01:44:43 -04:00
err := r . ParseForm ( )
require . NoError ( t , err )
2023-04-18 15:54:20 -04:00
require . Equal ( t , th . BasicTeam . Name , r . FormValue ( "team_domain" ) )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
} ) )
defer ts . Close ( )
// create a slash command on that team
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : ts . URL ,
Method : model . CommandMethodPost ,
Trigger : "postcommand" ,
}
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create post command" )
// Confirm that the command works when the channel is not read only
2023-06-06 17:29:29 -04:00
_ , resp , err := client . ExecuteCommandWithTeam ( context . Background ( ) , th . BasicChannel . Id , th . BasicChannel . TeamId , "/postcommand" )
2023-04-18 15:54:20 -04:00
require . NoError ( t , err )
CheckOKStatus ( t , resp )
// Enable Enterprise features
th . App . Srv ( ) . SetLicense ( model . NewTestLicense ( ) )
2025-06-06 01:44:43 -04:00
err = th . App . SetPhase2PermissionsMigrationStatus ( true )
require . NoError ( t , err )
2023-04-18 15:54:20 -04:00
_ , appErr = th . App . PatchChannelModerationsForChannel (
2023-10-06 16:43:21 -04:00
th . Context ,
2023-04-18 15:54:20 -04:00
th . BasicChannel ,
[ ] * model . ChannelModerationPatch { {
Name : & model . PermissionCreatePost . Id ,
Roles : & model . ChannelModeratedRolesPatch {
2024-08-05 23:45:00 -04:00
Guests : model . NewPointer ( false ) ,
Members : model . NewPointer ( false ) ,
2023-04-18 15:54:20 -04:00
} ,
} } )
require . Nil ( t , appErr )
// Confirm that the command fails when the channel is read only
2023-06-06 17:29:29 -04:00
_ , resp , err = client . ExecuteCommandWithTeam ( context . Background ( ) , th . BasicChannel . Id , th . BasicChannel . TeamId , "/postcommand" )
2023-04-18 15:54:20 -04:00
require . Error ( t , err )
CheckForbiddenStatus ( t , resp )
2025-01-30 10:20:36 -05:00
// Confirm that the command works when the channel is not read only - use different channel
_ , resp , err = client . ExecuteCommandWithTeam ( context . Background ( ) , th . BasicChannel2 . Id , th . BasicChannel2 . TeamId , "/postcommand" )
require . NoError ( t , err )
CheckOKStatus ( t , resp )
appErr = th . App . DeleteChannel (
th . Context ,
th . BasicChannel2 ,
th . SystemAdminUser . Id ,
)
require . Nil ( t , appErr , "failed to delete channel" )
// Confirm that the command fails when the channel is archived
_ , resp , err = client . ExecuteCommandWithTeam ( context . Background ( ) , th . BasicChannel2 . Id , th . BasicChannel2 . TeamId , "/postcommand" )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
2023-04-18 15:54:20 -04:00
}
2026-03-25 12:06:34 -04:00
func TestExecuteCommandResponseURLUsesSiteURL ( t * testing . T ) {
mainHelper . Parallel ( t )
th := Setup ( t ) . InitBasic ( t )
client := th . Client
channel := th . BasicChannel
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . AllowedUntrustedInternalConnections = "127.0.0.0/8" } )
// Set a SiteURL that differs from the test client's Host header (localhost).
// This verifies that response_url uses the configured SiteURL, not the Host header.
// Before the fix (MM-67142), response_url would contain "localhost" and fail this check.
expectedSiteURL := "http://mattermost.example.com"
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = expectedSiteURL } )
var receivedResponseURL string
expectedCommandResponse := & model . CommandResponse {
Text : "test response_url command response" ,
ResponseType : model . CommandResponseTypeInChannel ,
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
err := r . ParseForm ( )
require . NoError ( t , err )
// Capture the response_url sent by the server
receivedResponseURL = r . FormValue ( "response_url" )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
if err := json . NewEncoder ( w ) . Encode ( expectedCommandResponse ) ; err != nil {
th . TestLogger . Warn ( "Error while writing response" , mlog . Err ( err ) )
}
} ) )
defer ts . Close ( )
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : ts . URL ,
Method : model . CommandMethodPost ,
Trigger : "testrespurl" ,
}
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create command" )
_ , _ , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "/testrespurl" )
require . NoError ( t , err )
// Verify response_url starts with the configured SiteURL, not the Host header
require . True ( t , strings . HasPrefix ( receivedResponseURL , expectedSiteURL ) ,
"response_url should start with configured SiteURL %q, but got %q" , expectedSiteURL , receivedResponseURL )
// Verify warning is logged when Host header differs from SiteURL
require . NoError ( t , th . TestLogger . Flush ( ) )
testlib . AssertLog ( t , th . LogBuffer , mlog . LvlWarn . Name , "Request hostname differs from configured SiteURL. The configured SiteURL will be used for the slash command response URL." )
}
func TestExecuteCustomCommandRequiresSiteURL ( t * testing . T ) {
mainHelper . Parallel ( t )
th := Setup ( t ) . InitBasic ( t )
client := th . Client
channel := th . BasicChannel
enableCommands := * th . App . Config ( ) . ServiceSettings . EnableCommands
allowedInternalConnections := * th . App . Config ( ) . ServiceSettings . AllowedUntrustedInternalConnections
siteURL := * th . App . Config ( ) . ServiceSettings . SiteURL
defer func ( ) {
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . EnableCommands = & enableCommands } )
th . App . UpdateConfig ( func ( cfg * model . Config ) {
cfg . ServiceSettings . AllowedUntrustedInternalConnections = & allowedInternalConnections
} )
th . App . UpdateConfig ( func ( cfg * model . Config ) { cfg . ServiceSettings . SiteURL = & siteURL } )
} ( )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . EnableCommands = true } )
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . AllowedUntrustedInternalConnections = "127.0.0.0/8" } )
// Set SiteURL to a valid value first so we can create the command
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "http://localhost:8065" } )
// Create a custom command
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
fmt . Fprintln ( w , ` { "text": "response"} ` )
} ) )
defer ts . Close ( )
postCmd := & model . Command {
CreatorId : th . BasicUser . Id ,
TeamId : th . BasicTeam . Id ,
URL : ts . URL ,
Method : model . CommandMethodPost ,
Trigger : "customcmd" ,
}
_ , appErr := th . App . CreateCommand ( postCmd )
require . Nil ( t , appErr , "failed to create command" )
// Now set SiteURL to empty
th . App . UpdateConfig ( func ( cfg * model . Config ) { * cfg . ServiceSettings . SiteURL = "" } )
// Try to execute the custom command - should fail because SiteURL is required for custom commands
_ , resp , err := client . ExecuteCommand ( context . Background ( ) , channel . Id , "/customcmd" )
require . Error ( t , err )
CheckBadRequestStatus ( t , resp )
CheckErrorID ( t , err , "api.command.execute_command.site_url_required.app_error" )
// Built-in commands should still work without SiteURL
_ , _ , err = client . ExecuteCommand ( context . Background ( ) , channel . Id , "/echo test" )
require . NoError ( t , err )
}