github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/plugin_api_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"image"
    12  	"image/color"
    13  	"image/png"
    14  	"io/ioutil"
    15  	"net/http"
    16  	"net/http/httptest"
    17  	"os"
    18  	"path"
    19  	"path/filepath"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/mattermost/mattermost-server/v5/einterfaces/mocks"
    25  	"github.com/mattermost/mattermost-server/v5/model"
    26  	"github.com/mattermost/mattermost-server/v5/plugin"
    27  	"github.com/mattermost/mattermost-server/v5/utils"
    28  	"github.com/mattermost/mattermost-server/v5/utils/fileutils"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/mock"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func getDefaultPluginSettingsSchema() string {
    36  	ret, _ := json.Marshal(model.PluginSettingsSchema{
    37  		Settings: []*model.PluginSetting{
    38  			{Key: "BasicChannelName", Type: "text"},
    39  			{Key: "BasicChannelId", Type: "text"},
    40  			{Key: "BasicTeamDisplayName", Type: "text"},
    41  			{Key: "BasicTeamName", Type: "text"},
    42  			{Key: "BasicTeamId", Type: "text"},
    43  			{Key: "BasicUserEmail", Type: "text"},
    44  			{Key: "BasicUserId", Type: "text"},
    45  			{Key: "BasicUser2Email", Type: "text"},
    46  			{Key: "BasicUser2Id", Type: "text"},
    47  			{Key: "BasicPostMessage", Type: "text"},
    48  		},
    49  	})
    50  	return string(ret)
    51  }
    52  
    53  func setDefaultPluginConfig(th *TestHelper, pluginId string) {
    54  	th.App.UpdateConfig(func(cfg *model.Config) {
    55  		cfg.PluginSettings.Plugins[pluginId] = map[string]interface{}{
    56  			"BasicChannelName":     th.BasicChannel.Name,
    57  			"BasicChannelId":       th.BasicChannel.Id,
    58  			"BasicTeamName":        th.BasicTeam.Name,
    59  			"BasicTeamId":          th.BasicTeam.Id,
    60  			"BasicTeamDisplayName": th.BasicTeam.DisplayName,
    61  			"BasicUserEmail":       th.BasicUser.Email,
    62  			"BasicUserId":          th.BasicUser.Id,
    63  			"BasicUser2Email":      th.BasicUser2.Email,
    64  			"BasicUser2Id":         th.BasicUser2.Id,
    65  			"BasicPostMessage":     th.BasicPost.Message,
    66  		}
    67  	})
    68  }
    69  
    70  func setupMultiPluginApiTest(t *testing.T, pluginCodes []string, pluginManifests []string, pluginIds []string, app *App) string {
    71  	pluginDir, err := ioutil.TempDir("", "")
    72  	require.NoError(t, err)
    73  	webappPluginDir, err := ioutil.TempDir("", "")
    74  	require.NoError(t, err)
    75  	defer os.RemoveAll(pluginDir)
    76  	defer os.RemoveAll(webappPluginDir)
    77  
    78  	env, err := plugin.NewEnvironment(app.NewPluginAPI, pluginDir, webappPluginDir, app.Log(), nil)
    79  	require.NoError(t, err)
    80  
    81  	require.Equal(t, len(pluginCodes), len(pluginIds))
    82  	require.Equal(t, len(pluginManifests), len(pluginIds))
    83  
    84  	for i, pluginId := range pluginIds {
    85  		backend := filepath.Join(pluginDir, pluginId, "backend.exe")
    86  		utils.CompileGo(t, pluginCodes[i], backend)
    87  
    88  		ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifests[i]), 0600)
    89  		manifest, activated, reterr := env.Activate(pluginId)
    90  		require.Nil(t, reterr)
    91  		require.NotNil(t, manifest)
    92  		require.True(t, activated)
    93  	}
    94  
    95  	app.SetPluginsEnvironment(env)
    96  
    97  	return pluginDir
    98  }
    99  
   100  func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) string {
   101  	return setupMultiPluginApiTest(t, []string{pluginCode}, []string{pluginManifest}, []string{pluginId}, app)
   102  }
   103  
   104  func TestPublicFilesPathConfiguration(t *testing.T) {
   105  	th := Setup(t)
   106  	defer th.TearDown()
   107  
   108  	pluginID := "com.mattermost.sample"
   109  
   110  	pluginDir := setupPluginApiTest(t,
   111  		`
   112  		package main
   113  
   114  		import (
   115  			"github.com/mattermost/mattermost-server/v5/plugin"
   116  		)
   117  
   118  		type MyPlugin struct {
   119  			plugin.MattermostPlugin
   120  		}
   121  
   122  		func main() {
   123  			plugin.ClientMain(&MyPlugin{})
   124  		}
   125  	`,
   126  		`{"id": "com.mattermost.sample", "server": {"executable": "backend.exe"}, "settings_schema": {"settings": []}}`, pluginID, th.App)
   127  
   128  	publicFilesFolderInTest := filepath.Join(pluginDir, pluginID, "public")
   129  	publicFilesPath, err := th.App.GetPluginsEnvironment().PublicFilesPath(pluginID)
   130  	assert.NoError(t, err)
   131  	assert.Equal(t, publicFilesPath, publicFilesFolderInTest)
   132  }
   133  
   134  func TestPluginAPIGetUserPreferences(t *testing.T) {
   135  	th := Setup(t)
   136  	defer th.TearDown()
   137  	api := th.SetupPluginAPI()
   138  
   139  	user1, err := th.App.CreateUser(&model.User{
   140  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   141  		Password: "password",
   142  		Username: "user1" + model.NewId(),
   143  	})
   144  	require.Nil(t, err)
   145  	defer th.App.PermanentDeleteUser(user1)
   146  
   147  	preferences, err := api.GetPreferencesForUser(user1.Id)
   148  	require.Nil(t, err)
   149  	assert.Equal(t, 1, len(preferences))
   150  
   151  	assert.Equal(t, user1.Id, preferences[0].UserId)
   152  	assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category)
   153  	assert.Equal(t, user1.Id, preferences[0].Name)
   154  	assert.Equal(t, "0", preferences[0].Value)
   155  }
   156  
   157  func TestPluginAPIDeleteUserPreferences(t *testing.T) {
   158  	th := Setup(t)
   159  	defer th.TearDown()
   160  	api := th.SetupPluginAPI()
   161  
   162  	user1, err := th.App.CreateUser(&model.User{
   163  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   164  		Password: "password",
   165  		Username: "user1" + model.NewId(),
   166  	})
   167  	require.Nil(t, err)
   168  	defer th.App.PermanentDeleteUser(user1)
   169  
   170  	preferences, err := api.GetPreferencesForUser(user1.Id)
   171  	require.Nil(t, err)
   172  	assert.Equal(t, 1, len(preferences))
   173  
   174  	err = api.DeletePreferencesForUser(user1.Id, preferences)
   175  	require.Nil(t, err)
   176  	preferences, err = api.GetPreferencesForUser(user1.Id)
   177  	require.Nil(t, err)
   178  	assert.Equal(t, 0, len(preferences))
   179  
   180  	user2, err := th.App.CreateUser(&model.User{
   181  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   182  		Password: "password",
   183  		Username: "user2" + model.NewId(),
   184  	})
   185  	require.Nil(t, err)
   186  	defer th.App.PermanentDeleteUser(user2)
   187  
   188  	preference := model.Preference{
   189  		Name:     user2.Id,
   190  		UserId:   user2.Id,
   191  		Category: model.PREFERENCE_CATEGORY_THEME,
   192  		Value:    `{"color": "#ff0000", "color2": "#faf"}`,
   193  	}
   194  	err = api.UpdatePreferencesForUser(user2.Id, []model.Preference{preference})
   195  	require.Nil(t, err)
   196  
   197  	preferences, err = api.GetPreferencesForUser(user2.Id)
   198  	require.Nil(t, err)
   199  	assert.Equal(t, 2, len(preferences))
   200  
   201  	err = api.DeletePreferencesForUser(user2.Id, []model.Preference{preference})
   202  	require.Nil(t, err)
   203  	preferences, err = api.GetPreferencesForUser(user2.Id)
   204  	require.Nil(t, err)
   205  	assert.Equal(t, 1, len(preferences))
   206  	assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category)
   207  }
   208  
   209  func TestPluginAPIUpdateUserPreferences(t *testing.T) {
   210  	th := Setup(t)
   211  	defer th.TearDown()
   212  	api := th.SetupPluginAPI()
   213  
   214  	user1, err := th.App.CreateUser(&model.User{
   215  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   216  		Password: "password",
   217  		Username: "user1" + model.NewId(),
   218  	})
   219  	require.Nil(t, err)
   220  	defer th.App.PermanentDeleteUser(user1)
   221  
   222  	preferences, err := api.GetPreferencesForUser(user1.Id)
   223  	require.Nil(t, err)
   224  	assert.Equal(t, 1, len(preferences))
   225  	assert.Equal(t, user1.Id, preferences[0].UserId)
   226  	assert.Equal(t, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, preferences[0].Category)
   227  	assert.Equal(t, user1.Id, preferences[0].Name)
   228  	assert.Equal(t, "0", preferences[0].Value)
   229  
   230  	preference := model.Preference{
   231  		Name:     user1.Id,
   232  		UserId:   user1.Id,
   233  		Category: model.PREFERENCE_CATEGORY_THEME,
   234  		Value:    `{"color": "#ff0000", "color2": "#faf"}`,
   235  	}
   236  
   237  	err = api.UpdatePreferencesForUser(user1.Id, []model.Preference{preference})
   238  	require.Nil(t, err)
   239  
   240  	preferences, err = api.GetPreferencesForUser(user1.Id)
   241  	require.Nil(t, err)
   242  
   243  	assert.Equal(t, 2, len(preferences))
   244  	expectedCategories := []string{model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, model.PREFERENCE_CATEGORY_THEME}
   245  	for _, pref := range preferences {
   246  		assert.Contains(t, expectedCategories, pref.Category)
   247  		assert.Equal(t, user1.Id, pref.UserId)
   248  		assert.Equal(t, user1.Id, pref.Name)
   249  		if pref.Category == model.PREFERENCE_CATEGORY_TUTORIAL_STEPS {
   250  			assert.Equal(t, "0", pref.Value)
   251  		} else {
   252  			newTheme, _ := json.Marshal(map[string]string{"color": "#ff0000", "color2": "#faf"})
   253  			assert.Equal(t, string(newTheme), pref.Value)
   254  		}
   255  	}
   256  }
   257  
   258  func TestPluginAPIGetUsers(t *testing.T) {
   259  	th := Setup(t)
   260  	defer th.TearDown()
   261  	api := th.SetupPluginAPI()
   262  
   263  	user1, err := th.App.CreateUser(&model.User{
   264  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   265  		Password: "password",
   266  		Username: "user1" + model.NewId(),
   267  	})
   268  	require.Nil(t, err)
   269  	defer th.App.PermanentDeleteUser(user1)
   270  
   271  	user2, err := th.App.CreateUser(&model.User{
   272  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   273  		Password: "password",
   274  		Username: "user2" + model.NewId(),
   275  	})
   276  	require.Nil(t, err)
   277  	defer th.App.PermanentDeleteUser(user2)
   278  
   279  	user3, err := th.App.CreateUser(&model.User{
   280  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   281  		Password: "password",
   282  		Username: "user3" + model.NewId(),
   283  	})
   284  	require.Nil(t, err)
   285  	defer th.App.PermanentDeleteUser(user3)
   286  
   287  	user4, err := th.App.CreateUser(&model.User{
   288  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   289  		Password: "password",
   290  		Username: "user4" + model.NewId(),
   291  	})
   292  	require.Nil(t, err)
   293  	defer th.App.PermanentDeleteUser(user4)
   294  
   295  	testCases := []struct {
   296  		Description   string
   297  		Page          int
   298  		PerPage       int
   299  		ExpectedUsers []*model.User
   300  	}{
   301  		{
   302  			"page 0, perPage 0",
   303  			0,
   304  			0,
   305  			[]*model.User{},
   306  		},
   307  		{
   308  			"page 0, perPage 10",
   309  			0,
   310  			10,
   311  			[]*model.User{user1, user2, user3, user4},
   312  		},
   313  		{
   314  			"page 0, perPage 2",
   315  			0,
   316  			2,
   317  			[]*model.User{user1, user2},
   318  		},
   319  		{
   320  			"page 1, perPage 2",
   321  			1,
   322  			2,
   323  			[]*model.User{user3, user4},
   324  		},
   325  		{
   326  			"page 10, perPage 10",
   327  			10,
   328  			10,
   329  			[]*model.User{},
   330  		},
   331  	}
   332  
   333  	for _, testCase := range testCases {
   334  		t.Run(testCase.Description, func(t *testing.T) {
   335  			users, err := api.GetUsers(&model.UserGetOptions{
   336  				Page:    testCase.Page,
   337  				PerPage: testCase.PerPage,
   338  			})
   339  			assert.Nil(t, err)
   340  			assert.Equal(t, testCase.ExpectedUsers, users)
   341  		})
   342  	}
   343  }
   344  
   345  func TestPluginAPIGetUsersInTeam(t *testing.T) {
   346  	th := Setup(t)
   347  	defer th.TearDown()
   348  	api := th.SetupPluginAPI()
   349  
   350  	team1 := th.CreateTeam()
   351  	team2 := th.CreateTeam()
   352  
   353  	user1, err := th.App.CreateUser(&model.User{
   354  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   355  		Password: "password",
   356  		Username: "user1" + model.NewId(),
   357  	})
   358  	require.Nil(t, err)
   359  	defer th.App.PermanentDeleteUser(user1)
   360  
   361  	user2, err := th.App.CreateUser(&model.User{
   362  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   363  		Password: "password",
   364  		Username: "user2" + model.NewId(),
   365  	})
   366  	require.Nil(t, err)
   367  	defer th.App.PermanentDeleteUser(user2)
   368  
   369  	user3, err := th.App.CreateUser(&model.User{
   370  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   371  		Password: "password",
   372  		Username: "user3" + model.NewId(),
   373  	})
   374  	require.Nil(t, err)
   375  	defer th.App.PermanentDeleteUser(user3)
   376  
   377  	user4, err := th.App.CreateUser(&model.User{
   378  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   379  		Password: "password",
   380  		Username: "user4" + model.NewId(),
   381  	})
   382  	require.Nil(t, err)
   383  	defer th.App.PermanentDeleteUser(user4)
   384  
   385  	// Add all users to team 1
   386  	_, _, err = th.App.joinUserToTeam(team1, user1)
   387  	require.Nil(t, err)
   388  	_, _, err = th.App.joinUserToTeam(team1, user2)
   389  	require.Nil(t, err)
   390  	_, _, err = th.App.joinUserToTeam(team1, user3)
   391  	require.Nil(t, err)
   392  	_, _, err = th.App.joinUserToTeam(team1, user4)
   393  	require.Nil(t, err)
   394  
   395  	// Add only user3 and user4 to team 2
   396  	_, _, err = th.App.joinUserToTeam(team2, user3)
   397  	require.Nil(t, err)
   398  	_, _, err = th.App.joinUserToTeam(team2, user4)
   399  	require.Nil(t, err)
   400  
   401  	testCases := []struct {
   402  		Description   string
   403  		TeamId        string
   404  		Page          int
   405  		PerPage       int
   406  		ExpectedUsers []*model.User
   407  	}{
   408  		{
   409  			"unknown team",
   410  			model.NewId(),
   411  			0,
   412  			0,
   413  			[]*model.User{},
   414  		},
   415  		{
   416  			"team 1, page 0, perPage 10",
   417  			team1.Id,
   418  			0,
   419  			10,
   420  			[]*model.User{user1, user2, user3, user4},
   421  		},
   422  		{
   423  			"team 1, page 0, perPage 2",
   424  			team1.Id,
   425  			0,
   426  			2,
   427  			[]*model.User{user1, user2},
   428  		},
   429  		{
   430  			"team 1, page 1, perPage 2",
   431  			team1.Id,
   432  			1,
   433  			2,
   434  			[]*model.User{user3, user4},
   435  		},
   436  		{
   437  			"team 2, page 0, perPage 10",
   438  			team2.Id,
   439  			0,
   440  			10,
   441  			[]*model.User{user3, user4},
   442  		},
   443  	}
   444  
   445  	for _, testCase := range testCases {
   446  		t.Run(testCase.Description, func(t *testing.T) {
   447  			users, err := api.GetUsersInTeam(testCase.TeamId, testCase.Page, testCase.PerPage)
   448  			assert.Nil(t, err)
   449  			assert.Equal(t, testCase.ExpectedUsers, users)
   450  		})
   451  	}
   452  }
   453  
   454  func TestPluginAPIGetFile(t *testing.T) {
   455  	th := Setup(t).InitBasic()
   456  	defer th.TearDown()
   457  	api := th.SetupPluginAPI()
   458  
   459  	// check a valid file first
   460  	uploadTime := time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local)
   461  	filename := "testGetFile"
   462  	fileData := []byte("Hello World")
   463  	info, err := th.App.DoUploadFile(uploadTime, th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, filename, fileData)
   464  	require.Nil(t, err)
   465  	defer func() {
   466  		th.App.Srv().Store.FileInfo().PermanentDelete(info.Id)
   467  		th.App.RemoveFile(info.Path)
   468  	}()
   469  
   470  	data, err1 := api.GetFile(info.Id)
   471  	require.Nil(t, err1)
   472  	assert.Equal(t, data, fileData)
   473  
   474  	// then checking invalid file
   475  	data, err = api.GetFile("../fake/testingApi")
   476  	require.NotNil(t, err)
   477  	require.Nil(t, data)
   478  }
   479  
   480  func TestPluginAPIGetFileInfos(t *testing.T) {
   481  	th := Setup(t).InitBasic()
   482  	defer th.TearDown()
   483  	api := th.SetupPluginAPI()
   484  
   485  	fileInfo1, err := th.App.DoUploadFile(
   486  		time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC),
   487  		th.BasicTeam.Id,
   488  		th.BasicChannel.Id,
   489  		th.BasicUser.Id,
   490  		"testFile1",
   491  		[]byte("testfile1 Content"),
   492  	)
   493  	require.Nil(t, err)
   494  	defer func() {
   495  		th.App.Srv().Store.FileInfo().PermanentDelete(fileInfo1.Id)
   496  		th.App.RemoveFile(fileInfo1.Path)
   497  	}()
   498  
   499  	fileInfo2, err := th.App.DoUploadFile(
   500  		time.Date(2020, 1, 2, 1, 1, 1, 1, time.UTC),
   501  		th.BasicTeam.Id,
   502  		th.BasicChannel.Id,
   503  		th.BasicUser2.Id,
   504  		"testFile2",
   505  		[]byte("testfile2 Content"),
   506  	)
   507  	require.Nil(t, err)
   508  	defer func() {
   509  		th.App.Srv().Store.FileInfo().PermanentDelete(fileInfo2.Id)
   510  		th.App.RemoveFile(fileInfo2.Path)
   511  	}()
   512  
   513  	fileInfo3, err := th.App.DoUploadFile(
   514  		time.Date(2020, 1, 3, 1, 1, 1, 1, time.UTC),
   515  		th.BasicTeam.Id,
   516  		th.BasicChannel.Id,
   517  		th.BasicUser.Id,
   518  		"testFile3",
   519  		[]byte("testfile3 Content"),
   520  	)
   521  	require.Nil(t, err)
   522  	defer func() {
   523  		th.App.Srv().Store.FileInfo().PermanentDelete(fileInfo3.Id)
   524  		th.App.RemoveFile(fileInfo3.Path)
   525  	}()
   526  
   527  	_, err = api.CreatePost(&model.Post{
   528  		Message:   "testFile1",
   529  		UserId:    th.BasicUser.Id,
   530  		ChannelId: th.BasicChannel.Id,
   531  		FileIds:   model.StringArray{fileInfo1.Id},
   532  	})
   533  	require.Nil(t, err)
   534  
   535  	_, err = api.CreatePost(&model.Post{
   536  		Message:   "testFile2",
   537  		UserId:    th.BasicUser2.Id,
   538  		ChannelId: th.BasicChannel.Id,
   539  		FileIds:   model.StringArray{fileInfo2.Id},
   540  	})
   541  	require.Nil(t, err)
   542  
   543  	t.Run("get file infos with no options 2nd page of 1 per page", func(t *testing.T) {
   544  		fileInfos, err := api.GetFileInfos(1, 1, nil)
   545  		require.Nil(t, err)
   546  		require.Len(t, fileInfos, 1)
   547  	})
   548  	t.Run("get file infos filtered by user", func(t *testing.T) {
   549  		fileInfos, err := api.GetFileInfos(0, 5, &model.GetFileInfosOptions{
   550  			UserIds: []string{th.BasicUser.Id},
   551  		})
   552  		require.Nil(t, err)
   553  		require.Len(t, fileInfos, 2)
   554  	})
   555  	t.Run("get file infos filtered by channel ordered by created at descending", func(t *testing.T) {
   556  		fileInfos, err := api.GetFileInfos(0, 5, &model.GetFileInfosOptions{
   557  			ChannelIds:     []string{th.BasicChannel.Id},
   558  			SortBy:         model.FILEINFO_SORT_BY_CREATED,
   559  			SortDescending: true,
   560  		})
   561  		require.Nil(t, err)
   562  		require.Len(t, fileInfos, 2)
   563  		require.Equal(t, fileInfos[0].Id, fileInfo2.Id)
   564  		require.Equal(t, fileInfos[1].Id, fileInfo1.Id)
   565  	})
   566  }
   567  
   568  func TestPluginAPISavePluginConfig(t *testing.T) {
   569  	th := Setup(t)
   570  	defer th.TearDown()
   571  
   572  	manifest := &model.Manifest{
   573  		Id: "pluginid",
   574  		SettingsSchema: &model.PluginSettingsSchema{
   575  			Settings: []*model.PluginSetting{
   576  				{Key: "MyStringSetting", Type: "text"},
   577  				{Key: "MyIntSetting", Type: "text"},
   578  				{Key: "MyBoolSetting", Type: "bool"},
   579  			},
   580  		},
   581  	}
   582  
   583  	api := NewPluginAPI(th.App, manifest)
   584  
   585  	pluginConfigJsonString := `{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`
   586  
   587  	var pluginConfig map[string]interface{}
   588  	err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig)
   589  	require.NoError(t, err)
   590  
   591  	appErr := api.SavePluginConfig(pluginConfig)
   592  	require.Nil(t, appErr)
   593  
   594  	type Configuration struct {
   595  		MyStringSetting string
   596  		MyIntSetting    int
   597  		MyBoolSetting   bool
   598  	}
   599  
   600  	savedConfiguration := new(Configuration)
   601  	err = api.LoadPluginConfiguration(savedConfiguration)
   602  	require.NoError(t, err)
   603  
   604  	expectedConfiguration := new(Configuration)
   605  	err = json.Unmarshal([]byte(pluginConfigJsonString), &expectedConfiguration)
   606  	require.NoError(t, err)
   607  
   608  	assert.Equal(t, expectedConfiguration, savedConfiguration)
   609  }
   610  
   611  func TestPluginAPIGetPluginConfig(t *testing.T) {
   612  	th := Setup(t)
   613  	defer th.TearDown()
   614  
   615  	manifest := &model.Manifest{
   616  		Id: "pluginid",
   617  		SettingsSchema: &model.PluginSettingsSchema{
   618  			Settings: []*model.PluginSetting{
   619  				{Key: "MyStringSetting", Type: "text"},
   620  				{Key: "MyIntSetting", Type: "text"},
   621  				{Key: "MyBoolSetting", Type: "bool"},
   622  			},
   623  		},
   624  	}
   625  
   626  	api := NewPluginAPI(th.App, manifest)
   627  
   628  	pluginConfigJsonString := `{"mystringsetting": "str", "myintsetting": 32, "myboolsetting": true}`
   629  	var pluginConfig map[string]interface{}
   630  
   631  	err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig)
   632  	require.NoError(t, err)
   633  
   634  	th.App.UpdateConfig(func(cfg *model.Config) {
   635  		cfg.PluginSettings.Plugins["pluginid"] = pluginConfig
   636  	})
   637  
   638  	savedPluginConfig := api.GetPluginConfig()
   639  	assert.Equal(t, pluginConfig, savedPluginConfig)
   640  }
   641  
   642  func TestPluginAPILoadPluginConfiguration(t *testing.T) {
   643  	th := Setup(t)
   644  	defer th.TearDown()
   645  
   646  	var pluginJson map[string]interface{}
   647  	err := json.Unmarshal([]byte(`{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`), &pluginJson)
   648  	require.NoError(t, err)
   649  
   650  	th.App.UpdateConfig(func(cfg *model.Config) {
   651  		cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
   652  	})
   653  
   654  	testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
   655  	require.True(t, found, "Cannot find tests folder")
   656  	fullPath := path.Join(testFolder, "manual.test_load_configuration_plugin", "main.go")
   657  
   658  	err = pluginAPIHookTest(t, th, fullPath, "testloadpluginconfig", `{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": {
   659  		"settings": [
   660  			{
   661  				"key": "MyStringSetting",
   662  				"type": "text"
   663  			},
   664  			{
   665  				"key": "MyIntSetting",
   666  				"type": "text"
   667  			},
   668  			{
   669  				"key": "MyBoolSetting",
   670  				"type": "bool"
   671  			}
   672  		]
   673  	}}`)
   674  	require.NoError(t, err)
   675  
   676  }
   677  
   678  func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) {
   679  	th := Setup(t)
   680  	defer th.TearDown()
   681  
   682  	var pluginJson map[string]interface{}
   683  	err := json.Unmarshal([]byte(`{"mystringsetting": "override"}`), &pluginJson)
   684  	require.NoError(t, err)
   685  
   686  	th.App.UpdateConfig(func(cfg *model.Config) {
   687  		cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
   688  	})
   689  
   690  	testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
   691  	require.True(t, found, "Cannot find tests folder")
   692  	fullPath := path.Join(testFolder, "manual.test_load_configuration_defaults_plugin", "main.go")
   693  
   694  	err = pluginAPIHookTest(t, th, fullPath, "testloadpluginconfig", `{
   695  		"settings": [
   696  			{
   697  				"key": "MyStringSetting",
   698  				"type": "text",
   699  				"default": "notthis"
   700  			},
   701  			{
   702  				"key": "MyIntSetting",
   703  				"type": "text",
   704  				"default": 35
   705  			},
   706  			{
   707  				"key": "MyBoolSetting",
   708  				"type": "bool",
   709  				"default": true
   710  			}
   711  		]
   712  	}`)
   713  
   714  	require.NoError(t, err)
   715  
   716  }
   717  
   718  func TestPluginAPIGetPlugins(t *testing.T) {
   719  	th := Setup(t)
   720  	defer th.TearDown()
   721  	api := th.SetupPluginAPI()
   722  
   723  	pluginCode := `
   724      package main
   725  
   726      import (
   727        "github.com/mattermost/mattermost-server/v5/plugin"
   728      )
   729  
   730      type MyPlugin struct {
   731        plugin.MattermostPlugin
   732      }
   733  
   734      func main() {
   735        plugin.ClientMain(&MyPlugin{})
   736      }
   737    `
   738  
   739  	pluginDir, err := ioutil.TempDir("", "")
   740  	require.NoError(t, err)
   741  	webappPluginDir, err := ioutil.TempDir("", "")
   742  	require.NoError(t, err)
   743  	defer os.RemoveAll(pluginDir)
   744  	defer os.RemoveAll(webappPluginDir)
   745  
   746  	env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log(), nil)
   747  	require.NoError(t, err)
   748  
   749  	pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
   750  	var pluginManifests []*model.Manifest
   751  	for _, pluginID := range pluginIDs {
   752  		backend := filepath.Join(pluginDir, pluginID, "backend.exe")
   753  		utils.CompileGo(t, pluginCode, backend)
   754  
   755  		ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
   756  		manifest, activated, reterr := env.Activate(pluginID)
   757  
   758  		require.Nil(t, reterr)
   759  		require.NotNil(t, manifest)
   760  		require.True(t, activated)
   761  		pluginManifests = append(pluginManifests, manifest)
   762  	}
   763  	th.App.SetPluginsEnvironment(env)
   764  
   765  	// Deactivate the last one for testing
   766  	success := env.Deactivate(pluginIDs[len(pluginIDs)-1])
   767  	require.True(t, success)
   768  
   769  	// check existing user first
   770  	plugins, err := api.GetPlugins()
   771  	assert.Nil(t, err)
   772  	assert.NotEmpty(t, plugins)
   773  	assert.Equal(t, pluginManifests, plugins)
   774  }
   775  
   776  func TestPluginAPIInstallPlugin(t *testing.T) {
   777  	th := Setup(t)
   778  	defer th.TearDown()
   779  	api := th.SetupPluginAPI()
   780  
   781  	path, _ := fileutils.FindDir("tests")
   782  	tarData, err := ioutil.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
   783  	require.NoError(t, err)
   784  
   785  	_, err = api.InstallPlugin(bytes.NewReader(tarData), true)
   786  	assert.NotNil(t, err, "should not allow upload if upload disabled")
   787  	assert.Equal(t, err.Error(), "installPlugin: Plugins and/or plugin uploads have been disabled., ")
   788  
   789  	th.App.UpdateConfig(func(cfg *model.Config) {
   790  		*cfg.PluginSettings.Enable = true
   791  		*cfg.PluginSettings.EnableUploads = true
   792  	})
   793  
   794  	manifest, err := api.InstallPlugin(bytes.NewReader(tarData), true)
   795  	defer os.RemoveAll("plugins/testplugin")
   796  	require.Nil(t, err)
   797  	assert.Equal(t, "testplugin", manifest.Id)
   798  
   799  	// Successfully installed
   800  	pluginsResp, err := api.GetPlugins()
   801  	require.Nil(t, err)
   802  
   803  	found := false
   804  	for _, m := range pluginsResp {
   805  		if m.Id == manifest.Id {
   806  			found = true
   807  		}
   808  	}
   809  
   810  	assert.True(t, found)
   811  }
   812  
   813  func TestInstallPlugin(t *testing.T) {
   814  	// TODO(ilgooz): remove this setup func to use existent setupPluginApiTest().
   815  	// following setupTest() func is a modified version of setupPluginApiTest().
   816  	// we need a modified version of setupPluginApiTest() because it wasn't possible to use it directly here
   817  	// since it removes plugin dirs right after it returns, does not update App configs with the plugin
   818  	// dirs and this behavior tends to break this test as a result.
   819  	setupTest := func(t *testing.T, pluginCode string, pluginManifest string, pluginID string, app *App) (func(), string) {
   820  		pluginDir, err := ioutil.TempDir("", "")
   821  		require.NoError(t, err)
   822  		webappPluginDir, err := ioutil.TempDir("", "")
   823  		require.NoError(t, err)
   824  
   825  		app.UpdateConfig(func(cfg *model.Config) {
   826  			*cfg.PluginSettings.Directory = pluginDir
   827  			*cfg.PluginSettings.ClientDirectory = webappPluginDir
   828  		})
   829  
   830  		env, err := plugin.NewEnvironment(app.NewPluginAPI, pluginDir, webappPluginDir, app.Log(), nil)
   831  		require.NoError(t, err)
   832  
   833  		app.SetPluginsEnvironment(env)
   834  
   835  		backend := filepath.Join(pluginDir, pluginID, "backend.exe")
   836  		utils.CompileGo(t, pluginCode, backend)
   837  
   838  		ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
   839  		manifest, activated, reterr := env.Activate(pluginID)
   840  		require.Nil(t, reterr)
   841  		require.NotNil(t, manifest)
   842  		require.True(t, activated)
   843  
   844  		return func() {
   845  			os.RemoveAll(pluginDir)
   846  			os.RemoveAll(webappPluginDir)
   847  		}, pluginDir
   848  	}
   849  
   850  	th := Setup(t)
   851  	defer th.TearDown()
   852  
   853  	// start an http server to serve plugin's tarball to the test.
   854  	path, _ := fileutils.FindDir("tests")
   855  	ts := httptest.NewServer(http.FileServer(http.Dir(path)))
   856  	defer ts.Close()
   857  
   858  	th.App.UpdateConfig(func(cfg *model.Config) {
   859  		*cfg.PluginSettings.Enable = true
   860  		*cfg.PluginSettings.EnableUploads = true
   861  		cfg.PluginSettings.Plugins["testinstallplugin"] = map[string]interface{}{
   862  			"DownloadURL": ts.URL + "/testplugin.tar.gz",
   863  		}
   864  	})
   865  
   866  	tearDown, _ := setupTest(t,
   867  		`
   868  		package main
   869  
   870  		import (
   871  			"net/http"
   872  
   873  			"github.com/pkg/errors"
   874  
   875  			"github.com/mattermost/mattermost-server/v5/plugin"
   876  		)
   877  
   878  		type configuration struct {
   879  			DownloadURL string
   880  		}
   881  
   882  		type Plugin struct {
   883  			plugin.MattermostPlugin
   884  
   885  			configuration configuration
   886  		}
   887  
   888  		func (p *Plugin) OnConfigurationChange() error {
   889  			if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil {
   890  				return err
   891  			}
   892  			return nil
   893  		}
   894  
   895  		func (p *Plugin) OnActivate() error {
   896  			resp, err := http.Get(p.configuration.DownloadURL)
   897  			if err != nil {
   898  				return err
   899  			}
   900  			defer resp.Body.Close()
   901  			_, aerr := p.API.InstallPlugin(resp.Body, true)
   902  			if aerr != nil {
   903  				return errors.Wrap(aerr, "cannot install plugin")
   904  			}
   905  			return nil
   906  		}
   907  
   908  		func main() {
   909  			plugin.ClientMain(&Plugin{})
   910  		}
   911  
   912  	`,
   913  		`{"id": "testinstallplugin", "backend": {"executable": "backend.exe"}, "settings_schema": {
   914  		"settings": [
   915  			{
   916  				"key": "DownloadURL",
   917  				"type": "text"
   918  			}
   919  		]
   920  	}}`, "testinstallplugin", th.App)
   921  	defer tearDown()
   922  
   923  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testinstallplugin")
   924  	require.NoError(t, err)
   925  
   926  	err = hooks.OnActivate()
   927  	require.NoError(t, err)
   928  
   929  	plugins, aerr := th.App.GetPlugins()
   930  	require.Nil(t, aerr)
   931  	require.Len(t, plugins.Inactive, 1)
   932  	require.Equal(t, "testplugin", plugins.Inactive[0].Id)
   933  }
   934  
   935  func TestPluginAPIGetTeamIcon(t *testing.T) {
   936  	th := Setup(t).InitBasic()
   937  	defer th.TearDown()
   938  	api := th.SetupPluginAPI()
   939  
   940  	// Create an 128 x 128 image
   941  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   942  	// Draw a red dot at (2, 3)
   943  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   944  	buf := new(bytes.Buffer)
   945  	err := png.Encode(buf, img)
   946  	require.Nil(t, err)
   947  	dataBytes := buf.Bytes()
   948  	fileReader := bytes.NewReader(dataBytes)
   949  
   950  	// Set the Team Icon
   951  	err = th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
   952  	require.Nil(t, err)
   953  
   954  	// Get the team icon to check
   955  	teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
   956  	require.Nil(t, err)
   957  	require.NotEmpty(t, teamIcon)
   958  
   959  	colorful := color.NRGBA{255, 0, 0, 255}
   960  	byteReader := bytes.NewReader(teamIcon)
   961  	img2, _, err2 := image.Decode(byteReader)
   962  	require.Nil(t, err2)
   963  	require.Equal(t, img2.At(2, 3), colorful)
   964  }
   965  
   966  func TestPluginAPISetTeamIcon(t *testing.T) {
   967  	th := Setup(t).InitBasic()
   968  	defer th.TearDown()
   969  	api := th.SetupPluginAPI()
   970  
   971  	// Create an 128 x 128 image
   972  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   973  	// Draw a red dot at (2, 3)
   974  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   975  	buf := new(bytes.Buffer)
   976  	err := png.Encode(buf, img)
   977  	require.Nil(t, err)
   978  	dataBytes := buf.Bytes()
   979  
   980  	// Set the user profile image
   981  	err = api.SetTeamIcon(th.BasicTeam.Id, dataBytes)
   982  	require.Nil(t, err)
   983  
   984  	// Get the user profile image to check
   985  	teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
   986  	require.Nil(t, err)
   987  	require.NotEmpty(t, teamIcon)
   988  
   989  	colorful := color.NRGBA{255, 0, 0, 255}
   990  	byteReader := bytes.NewReader(teamIcon)
   991  	img2, _, err2 := image.Decode(byteReader)
   992  	require.Nil(t, err2)
   993  	require.Equal(t, img2.At(2, 3), colorful)
   994  }
   995  
   996  func TestPluginAPIRemoveTeamIcon(t *testing.T) {
   997  	th := Setup(t).InitBasic()
   998  	defer th.TearDown()
   999  	api := th.SetupPluginAPI()
  1000  
  1001  	// Create an 128 x 128 image
  1002  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
  1003  
  1004  	// Draw a red dot at (2, 3)
  1005  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
  1006  	buf := new(bytes.Buffer)
  1007  	err1 := png.Encode(buf, img)
  1008  	require.Nil(t, err1)
  1009  	dataBytes := buf.Bytes()
  1010  	fileReader := bytes.NewReader(dataBytes)
  1011  
  1012  	// Set the Team Icon
  1013  	err := th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
  1014  	require.Nil(t, err)
  1015  	err = api.RemoveTeamIcon(th.BasicTeam.Id)
  1016  	require.Nil(t, err)
  1017  }
  1018  
  1019  func pluginAPIHookTest(t *testing.T, th *TestHelper, fileName string, id string, settingsSchema string) error {
  1020  	data, err := ioutil.ReadFile(fileName)
  1021  	if err != nil {
  1022  		return err
  1023  	}
  1024  	code := string(data)
  1025  	schema := `{"settings": [ ]	}`
  1026  	if settingsSchema != "" {
  1027  		schema = settingsSchema
  1028  	}
  1029  	setupPluginApiTest(t, code,
  1030  		fmt.Sprintf(`{"id": "%v", "backend": {"executable": "backend.exe"}, "settings_schema": %v}`, id, schema),
  1031  		id, th.App)
  1032  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin(id)
  1033  	require.NoError(t, err)
  1034  	require.NotNil(t, hooks)
  1035  	_, ret := hooks.MessageWillBePosted(nil, nil)
  1036  	if ret != "OK" {
  1037  		return errors.New(ret)
  1038  	}
  1039  	return nil
  1040  }
  1041  
  1042  // This is a meta-test function. It does the following:
  1043  // 1. Scans "tests/plugin_tests" folder
  1044  // 2. For each folder - compiles the main.go inside and executes it, validating it's result
  1045  // 3. If folder starts with "manual." it is skipped ("manual." tests executed in other part of this file)
  1046  // 4. Before compiling the main.go file is passed through templating and the following values are available in the template: BasicUser, BasicUser2, BasicChannel, BasicTeam, BasicPost
  1047  // 5. Successfully running test should return nil, "OK". Any other returned string is considered and error
  1048  
  1049  func TestBasicAPIPlugins(t *testing.T) {
  1050  	defaultSchema := getDefaultPluginSettingsSchema()
  1051  	testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
  1052  	require.True(t, found, "Cannot read find app folder")
  1053  	dirs, err := ioutil.ReadDir(testFolder)
  1054  	require.NoError(t, err, "Cannot read test folder %v", testFolder)
  1055  	for _, dir := range dirs {
  1056  		d := dir.Name()
  1057  		if dir.IsDir() && !strings.HasPrefix(d, "manual.") {
  1058  			t.Run(d, func(t *testing.T) {
  1059  				mainPath := path.Join(testFolder, d, "main.go")
  1060  				_, err := os.Stat(mainPath)
  1061  				require.NoError(t, err, "Cannot find plugin main file at %v", mainPath)
  1062  				th := Setup(t).InitBasic()
  1063  				defer th.TearDown()
  1064  				setDefaultPluginConfig(th, dir.Name())
  1065  				err = pluginAPIHookTest(t, th, mainPath, dir.Name(), defaultSchema)
  1066  				require.NoError(t, err)
  1067  			})
  1068  		}
  1069  	}
  1070  }
  1071  
  1072  func TestPluginAPIKVCompareAndSet(t *testing.T) {
  1073  	th := Setup(t)
  1074  	defer th.TearDown()
  1075  	api := th.SetupPluginAPI()
  1076  
  1077  	testCases := []struct {
  1078  		Description   string
  1079  		ExpectedValue []byte
  1080  	}{
  1081  		{
  1082  			Description:   "Testing non-nil, non-empty value",
  1083  			ExpectedValue: []byte("value1"),
  1084  		},
  1085  		{
  1086  			Description:   "Testing empty value",
  1087  			ExpectedValue: []byte(""),
  1088  		},
  1089  	}
  1090  
  1091  	for i, testCase := range testCases {
  1092  		t.Run(testCase.Description, func(t *testing.T) {
  1093  			expectedKey := fmt.Sprintf("Key%d", i)
  1094  			expectedValueEmpty := []byte("")
  1095  			expectedValue1 := testCase.ExpectedValue
  1096  			expectedValue2 := []byte("value2")
  1097  			expectedValue3 := []byte("value3")
  1098  
  1099  			// Attempt update using an incorrect old value
  1100  			updated, err := api.KVCompareAndSet(expectedKey, expectedValue2, expectedValue1)
  1101  			require.Nil(t, err)
  1102  			require.False(t, updated)
  1103  
  1104  			// Make sure no key is already created
  1105  			value, err := api.KVGet(expectedKey)
  1106  			require.Nil(t, err)
  1107  			require.Nil(t, value)
  1108  
  1109  			// Insert using nil old value
  1110  			updated, err = api.KVCompareAndSet(expectedKey, nil, expectedValue1)
  1111  			require.Nil(t, err)
  1112  			require.True(t, updated)
  1113  
  1114  			// Get inserted value
  1115  			value, err = api.KVGet(expectedKey)
  1116  			require.Nil(t, err)
  1117  			require.Equal(t, expectedValue1, value)
  1118  
  1119  			// Attempt to insert again using nil old value
  1120  			updated, err = api.KVCompareAndSet(expectedKey, nil, expectedValue2)
  1121  			require.Nil(t, err)
  1122  			require.False(t, updated)
  1123  
  1124  			// Get old value to assert nothing has changed
  1125  			value, err = api.KVGet(expectedKey)
  1126  			require.Nil(t, err)
  1127  			require.Equal(t, expectedValue1, value)
  1128  
  1129  			// Update using correct old value
  1130  			updated, err = api.KVCompareAndSet(expectedKey, expectedValue1, expectedValue2)
  1131  			require.Nil(t, err)
  1132  			require.True(t, updated)
  1133  
  1134  			value, err = api.KVGet(expectedKey)
  1135  			require.Nil(t, err)
  1136  			require.Equal(t, expectedValue2, value)
  1137  
  1138  			// Update using incorrect old value
  1139  			updated, err = api.KVCompareAndSet(expectedKey, []byte("incorrect"), expectedValue3)
  1140  			require.Nil(t, err)
  1141  			require.False(t, updated)
  1142  
  1143  			value, err = api.KVGet(expectedKey)
  1144  			require.Nil(t, err)
  1145  			require.Equal(t, expectedValue2, value)
  1146  
  1147  			// Update using nil old value
  1148  			updated, err = api.KVCompareAndSet(expectedKey, nil, expectedValue3)
  1149  			require.Nil(t, err)
  1150  			require.False(t, updated)
  1151  
  1152  			value, err = api.KVGet(expectedKey)
  1153  			require.Nil(t, err)
  1154  			require.Equal(t, expectedValue2, value)
  1155  
  1156  			// Update using empty old value
  1157  			updated, err = api.KVCompareAndSet(expectedKey, expectedValueEmpty, expectedValue3)
  1158  			require.Nil(t, err)
  1159  			require.False(t, updated)
  1160  
  1161  			value, err = api.KVGet(expectedKey)
  1162  			require.Nil(t, err)
  1163  			require.Equal(t, expectedValue2, value)
  1164  		})
  1165  	}
  1166  }
  1167  
  1168  func TestPluginAPIKVCompareAndDelete(t *testing.T) {
  1169  	th := Setup(t)
  1170  	defer th.TearDown()
  1171  	api := th.SetupPluginAPI()
  1172  
  1173  	testCases := []struct {
  1174  		Description   string
  1175  		ExpectedValue []byte
  1176  	}{
  1177  		{
  1178  			Description:   "Testing non-nil, non-empty value",
  1179  			ExpectedValue: []byte("value1"),
  1180  		},
  1181  		{
  1182  			Description:   "Testing empty value",
  1183  			ExpectedValue: []byte(""),
  1184  		},
  1185  	}
  1186  
  1187  	for i, testCase := range testCases {
  1188  		t.Run(testCase.Description, func(t *testing.T) {
  1189  			expectedKey := fmt.Sprintf("Key%d", i)
  1190  			expectedValue1 := testCase.ExpectedValue
  1191  			expectedValue2 := []byte("value2")
  1192  
  1193  			// Set the value
  1194  			err := api.KVSet(expectedKey, expectedValue1)
  1195  			require.Nil(t, err)
  1196  
  1197  			// Attempt delete using an incorrect old value
  1198  			deleted, err := api.KVCompareAndDelete(expectedKey, expectedValue2)
  1199  			require.Nil(t, err)
  1200  			require.False(t, deleted)
  1201  
  1202  			// Make sure the value is still there
  1203  			value, err := api.KVGet(expectedKey)
  1204  			require.Nil(t, err)
  1205  			require.Equal(t, expectedValue1, value)
  1206  
  1207  			// Attempt delete using the proper value
  1208  			deleted, err = api.KVCompareAndDelete(expectedKey, expectedValue1)
  1209  			require.Nil(t, err)
  1210  			require.True(t, deleted)
  1211  
  1212  			// Verify it's deleted
  1213  			value, err = api.KVGet(expectedKey)
  1214  			require.Nil(t, err)
  1215  			require.Nil(t, value)
  1216  		})
  1217  	}
  1218  }
  1219  
  1220  func TestPluginCreateBot(t *testing.T) {
  1221  	th := Setup(t)
  1222  	defer th.TearDown()
  1223  	api := th.SetupPluginAPI()
  1224  
  1225  	bot, err := api.CreateBot(&model.Bot{
  1226  		Username:    model.NewRandomString(10),
  1227  		DisplayName: "bot",
  1228  		Description: "bot",
  1229  	})
  1230  	require.Nil(t, err)
  1231  
  1232  	_, err = api.CreateBot(&model.Bot{
  1233  		Username:    model.NewRandomString(10),
  1234  		OwnerId:     bot.UserId,
  1235  		DisplayName: "bot2",
  1236  		Description: "bot2",
  1237  	})
  1238  	require.NotNil(t, err)
  1239  
  1240  }
  1241  
  1242  func TestPluginCreatePostWithUploadedFile(t *testing.T) {
  1243  	th := Setup(t).InitBasic()
  1244  	defer th.TearDown()
  1245  	api := th.SetupPluginAPI()
  1246  
  1247  	data := []byte("Hello World")
  1248  	channelId := th.BasicChannel.Id
  1249  	filename := "testGetFile"
  1250  	fileInfo, err := api.UploadFile(data, channelId, filename)
  1251  	require.Nil(t, err)
  1252  	defer func() {
  1253  		th.App.Srv().Store.FileInfo().PermanentDelete(fileInfo.Id)
  1254  		th.App.RemoveFile(fileInfo.Path)
  1255  	}()
  1256  
  1257  	actualData, err := api.GetFile(fileInfo.Id)
  1258  	require.Nil(t, err)
  1259  	assert.Equal(t, data, actualData)
  1260  
  1261  	userId := th.BasicUser.Id
  1262  	post, err := api.CreatePost(&model.Post{
  1263  		Message:   "test",
  1264  		UserId:    userId,
  1265  		ChannelId: channelId,
  1266  		FileIds:   model.StringArray{fileInfo.Id},
  1267  	})
  1268  	require.Nil(t, err)
  1269  	assert.Equal(t, model.StringArray{fileInfo.Id}, post.FileIds)
  1270  
  1271  	actualPost, err := api.GetPost(post.Id)
  1272  	require.Nil(t, err)
  1273  	assert.Equal(t, model.StringArray{fileInfo.Id}, actualPost.FileIds)
  1274  }
  1275  
  1276  func TestPluginAPIGetConfig(t *testing.T) {
  1277  	th := Setup(t)
  1278  	defer th.TearDown()
  1279  	api := th.SetupPluginAPI()
  1280  
  1281  	config := api.GetConfig()
  1282  	if config.LdapSettings.BindPassword != nil && len(*config.LdapSettings.BindPassword) > 0 {
  1283  		assert.Equal(t, *config.LdapSettings.BindPassword, model.FAKE_SETTING)
  1284  	}
  1285  
  1286  	assert.Equal(t, *config.FileSettings.PublicLinkSalt, model.FAKE_SETTING)
  1287  
  1288  	if len(*config.FileSettings.AmazonS3SecretAccessKey) > 0 {
  1289  		assert.Equal(t, *config.FileSettings.AmazonS3SecretAccessKey, model.FAKE_SETTING)
  1290  	}
  1291  
  1292  	if config.EmailSettings.SMTPPassword != nil && len(*config.EmailSettings.SMTPPassword) > 0 {
  1293  		assert.Equal(t, *config.EmailSettings.SMTPPassword, model.FAKE_SETTING)
  1294  	}
  1295  
  1296  	if len(*config.GitLabSettings.Secret) > 0 {
  1297  		assert.Equal(t, *config.GitLabSettings.Secret, model.FAKE_SETTING)
  1298  	}
  1299  
  1300  	assert.Equal(t, *config.SqlSettings.DataSource, model.FAKE_SETTING)
  1301  	assert.Equal(t, *config.SqlSettings.AtRestEncryptKey, model.FAKE_SETTING)
  1302  	assert.Equal(t, *config.ElasticsearchSettings.Password, model.FAKE_SETTING)
  1303  
  1304  	for i := range config.SqlSettings.DataSourceReplicas {
  1305  		assert.Equal(t, config.SqlSettings.DataSourceReplicas[i], model.FAKE_SETTING)
  1306  	}
  1307  
  1308  	for i := range config.SqlSettings.DataSourceSearchReplicas {
  1309  		assert.Equal(t, config.SqlSettings.DataSourceSearchReplicas[i], model.FAKE_SETTING)
  1310  	}
  1311  }
  1312  
  1313  func TestPluginAPIGetUnsanitizedConfig(t *testing.T) {
  1314  	th := Setup(t)
  1315  	defer th.TearDown()
  1316  	api := th.SetupPluginAPI()
  1317  
  1318  	config := api.GetUnsanitizedConfig()
  1319  	if config.LdapSettings.BindPassword != nil && len(*config.LdapSettings.BindPassword) > 0 {
  1320  		assert.NotEqual(t, *config.LdapSettings.BindPassword, model.FAKE_SETTING)
  1321  	}
  1322  
  1323  	assert.NotEqual(t, *config.FileSettings.PublicLinkSalt, model.FAKE_SETTING)
  1324  
  1325  	if len(*config.FileSettings.AmazonS3SecretAccessKey) > 0 {
  1326  		assert.NotEqual(t, *config.FileSettings.AmazonS3SecretAccessKey, model.FAKE_SETTING)
  1327  	}
  1328  
  1329  	if config.EmailSettings.SMTPPassword != nil && len(*config.EmailSettings.SMTPPassword) > 0 {
  1330  		assert.NotEqual(t, *config.EmailSettings.SMTPPassword, model.FAKE_SETTING)
  1331  	}
  1332  
  1333  	if len(*config.GitLabSettings.Secret) > 0 {
  1334  		assert.NotEqual(t, *config.GitLabSettings.Secret, model.FAKE_SETTING)
  1335  	}
  1336  
  1337  	assert.NotEqual(t, *config.SqlSettings.DataSource, model.FAKE_SETTING)
  1338  	assert.NotEqual(t, *config.SqlSettings.AtRestEncryptKey, model.FAKE_SETTING)
  1339  	assert.NotEqual(t, *config.ElasticsearchSettings.Password, model.FAKE_SETTING)
  1340  
  1341  	for i := range config.SqlSettings.DataSourceReplicas {
  1342  		assert.NotEqual(t, config.SqlSettings.DataSourceReplicas[i], model.FAKE_SETTING)
  1343  	}
  1344  
  1345  	for i := range config.SqlSettings.DataSourceSearchReplicas {
  1346  		assert.NotEqual(t, config.SqlSettings.DataSourceSearchReplicas[i], model.FAKE_SETTING)
  1347  	}
  1348  }
  1349  
  1350  func TestPluginAddUserToChannel(t *testing.T) {
  1351  	th := Setup(t).InitBasic()
  1352  	defer th.TearDown()
  1353  	api := th.SetupPluginAPI()
  1354  
  1355  	member, err := api.AddUserToChannel(th.BasicChannel.Id, th.BasicUser.Id, th.BasicUser2.Id)
  1356  	require.Nil(t, err)
  1357  	require.NotNil(t, member)
  1358  	require.Equal(t, th.BasicChannel.Id, member.ChannelId)
  1359  	require.Equal(t, th.BasicUser.Id, member.UserId)
  1360  }
  1361  
  1362  func TestInterpluginPluginHTTP(t *testing.T) {
  1363  	th := Setup(t)
  1364  	defer th.TearDown()
  1365  
  1366  	setupMultiPluginApiTest(t,
  1367  		[]string{`
  1368  		package main
  1369  
  1370  		import (
  1371  			"github.com/mattermost/mattermost-server/v5/plugin"
  1372  			"bytes"
  1373  			"net/http"
  1374  		)
  1375  
  1376  		type MyPlugin struct {
  1377  			plugin.MattermostPlugin
  1378  		}
  1379  
  1380  		func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
  1381  			if r.URL.Path != "/api/v2/test" {
  1382  				return
  1383  			}
  1384  
  1385  			if r.URL.Query().Get("abc") != "xyz" {
  1386  				return
  1387  			}
  1388  
  1389  			buf := bytes.Buffer{}
  1390  			buf.ReadFrom(r.Body)
  1391  			resp := "we got:" + buf.String()
  1392  			w.WriteHeader(598)
  1393  			w.Write([]byte(resp))
  1394  		}
  1395  
  1396  		func main() {
  1397  			plugin.ClientMain(&MyPlugin{})
  1398  		}
  1399  		`,
  1400  			`
  1401  		package main
  1402  
  1403  		import (
  1404  			"github.com/mattermost/mattermost-server/v5/plugin"
  1405  			"github.com/mattermost/mattermost-server/v5/model"
  1406  			"bytes"
  1407  			"net/http"
  1408  			"io/ioutil"
  1409  		)
  1410  
  1411  		type MyPlugin struct {
  1412  			plugin.MattermostPlugin
  1413  		}
  1414  
  1415  		func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
  1416  			buf := bytes.Buffer{}
  1417  			buf.WriteString("This is the request")
  1418  			req, err := http.NewRequest("GET", "/testplugininterserver/api/v2/test?abc=xyz", &buf)
  1419  			if err != nil {
  1420  				return nil, err.Error()
  1421  			}
  1422  			req.Header.Add("Mattermost-User-Id", "userid")
  1423  			resp := p.API.PluginHTTP(req)
  1424  			if resp == nil {
  1425  				return nil, "Nil resp"
  1426  			}
  1427  			if resp.Body == nil {
  1428  				return nil, "Nil body"
  1429  			}
  1430  			respbody, err := ioutil.ReadAll(resp.Body)
  1431  			if err != nil {
  1432  				return nil, err.Error()
  1433  			}
  1434  			if resp.StatusCode != 598 {
  1435  				return nil, "wrong status " + string(respbody)
  1436  			}
  1437  			return nil, string(respbody)
  1438  		}
  1439  
  1440  		func main() {
  1441  			plugin.ClientMain(&MyPlugin{})
  1442  		}
  1443  		`,
  1444  		},
  1445  		[]string{
  1446  			`{"id": "testplugininterserver", "backend": {"executable": "backend.exe"}}`,
  1447  			`{"id": "testplugininterclient", "backend": {"executable": "backend.exe"}}`,
  1448  		},
  1449  		[]string{
  1450  			"testplugininterserver",
  1451  			"testplugininterclient",
  1452  		},
  1453  		th.App,
  1454  	)
  1455  
  1456  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testplugininterclient")
  1457  	require.NoError(t, err)
  1458  	_, ret := hooks.MessageWillBePosted(nil, nil)
  1459  	assert.Equal(t, "we got:This is the request", ret)
  1460  }
  1461  
  1462  func TestApiMetrics(t *testing.T) {
  1463  	th := Setup(t)
  1464  	defer th.TearDown()
  1465  
  1466  	t.Run("", func(t *testing.T) {
  1467  		metricsMock := &mocks.MetricsInterface{}
  1468  
  1469  		pluginDir, err := ioutil.TempDir("", "")
  1470  		require.NoError(t, err)
  1471  		webappPluginDir, err := ioutil.TempDir("", "")
  1472  		require.NoError(t, err)
  1473  		defer os.RemoveAll(pluginDir)
  1474  		defer os.RemoveAll(webappPluginDir)
  1475  
  1476  		env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log(), metricsMock)
  1477  		require.NoError(t, err)
  1478  
  1479  		th.App.SetPluginsEnvironment(env)
  1480  
  1481  		pluginId := model.NewId()
  1482  		backend := filepath.Join(pluginDir, pluginId, "backend.exe")
  1483  		code :=
  1484  			`
  1485  	package main
  1486  
  1487  	import (
  1488  		"github.com/mattermost/mattermost-server/v5/model"
  1489  		"github.com/mattermost/mattermost-server/v5/plugin"
  1490  	)
  1491  
  1492  	type MyPlugin struct {
  1493  		plugin.MattermostPlugin
  1494  	}
  1495  
  1496  	func (p *MyPlugin) UserHasBeenCreated(c *plugin.Context, user *model.User) {
  1497  		user.Nickname = "plugin-callback-success"
  1498  		p.API.UpdateUser(user)
  1499  	}
  1500  
  1501  	func main() {
  1502  		plugin.ClientMain(&MyPlugin{})
  1503  	}
  1504  `
  1505  		utils.CompileGo(t, code, backend)
  1506  		ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(`{"id": "`+pluginId+`", "backend": {"executable": "backend.exe"}}`), 0600)
  1507  
  1508  		// Don't care about these mocks
  1509  		metricsMock.On("ObservePluginHookDuration", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
  1510  		metricsMock.On("ObservePluginMultiHookIterationDuration", mock.Anything, mock.Anything, mock.Anything).Return()
  1511  		metricsMock.On("ObservePluginMultiHookDuration", mock.Anything).Return()
  1512  
  1513  		// Setup mocks
  1514  		metricsMock.On("ObservePluginApiDuration", pluginId, "UpdateUser", true, mock.Anything).Return()
  1515  
  1516  		_, _, activationErr := env.Activate(pluginId)
  1517  		require.NoError(t, activationErr)
  1518  
  1519  		require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginId))
  1520  
  1521  		user1 := &model.User{
  1522  			Email:       model.NewId() + "success+test@example.com",
  1523  			Nickname:    "Darth Vader1",
  1524  			Username:    "vader" + model.NewId(),
  1525  			Password:    "passwd1",
  1526  			AuthService: "",
  1527  		}
  1528  		_, appErr := th.App.CreateUser(user1)
  1529  		require.Nil(t, appErr)
  1530  		time.Sleep(1 * time.Second)
  1531  		user1, appErr = th.App.GetUser(user1.Id)
  1532  		require.Nil(t, appErr)
  1533  		require.Equal(t, "plugin-callback-success", user1.Nickname)
  1534  
  1535  		// Disable plugin
  1536  		require.True(t, th.App.GetPluginsEnvironment().Deactivate(pluginId))
  1537  		require.False(t, th.App.GetPluginsEnvironment().IsActive(pluginId))
  1538  
  1539  		metricsMock.AssertExpectations(t)
  1540  	})
  1541  }
  1542  
  1543  func TestPluginAPIGetPostsForChannel(t *testing.T) {
  1544  	require := require.New(t)
  1545  
  1546  	th := Setup(t).InitBasic()
  1547  	defer th.TearDown()
  1548  	api := th.SetupPluginAPI()
  1549  
  1550  	numPosts := 10
  1551  
  1552  	// GetPostsForChannel returns posts ordered with the most recent first, so we
  1553  	// need to invert the expected slice, the oldest post being BasicPost
  1554  	expectedPosts := make([]*model.Post, numPosts)
  1555  	expectedPosts[numPosts-1] = th.BasicPost
  1556  	for i := numPosts - 2; i >= 0; i-- {
  1557  		expectedPosts[i] = th.CreatePost(th.BasicChannel)
  1558  	}
  1559  	// CreatePost does not add Metadata, but initializes the structure. GetPostsForChannel
  1560  	// returns nil for an empty Metadata, so we need to match that behaviour
  1561  	for _, post := range expectedPosts {
  1562  		post.Metadata = nil
  1563  	}
  1564  
  1565  	postList, err := api.GetPostsForChannel(th.BasicChannel.Id, 0, 0)
  1566  	require.Nil(err)
  1567  	require.Nil(postList.ToSlice())
  1568  
  1569  	postList, err = api.GetPostsForChannel(th.BasicChannel.Id, 0, numPosts/2)
  1570  	require.Nil(err)
  1571  	require.Equal(expectedPosts[:numPosts/2], postList.ToSlice())
  1572  
  1573  	postList, err = api.GetPostsForChannel(th.BasicChannel.Id, 1, numPosts/2)
  1574  	require.Nil(err)
  1575  	require.Equal(expectedPosts[numPosts/2:], postList.ToSlice())
  1576  
  1577  	postList, err = api.GetPostsForChannel(th.BasicChannel.Id, 2, numPosts/2)
  1578  	require.Nil(err)
  1579  	require.Nil(postList.ToSlice())
  1580  
  1581  	postList, err = api.GetPostsForChannel(th.BasicChannel.Id, 0, numPosts+1)
  1582  	require.Nil(err)
  1583  	require.Equal(expectedPosts, postList.ToSlice())
  1584  }
  1585  
  1586  func TestPluginHTTPConnHijack(t *testing.T) {
  1587  	th := Setup(t)
  1588  	defer th.TearDown()
  1589  
  1590  	testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
  1591  	require.True(t, found, "Cannot find tests folder")
  1592  	fullPath := path.Join(testFolder, "manual.test_http_hijack_plugin", "main.go")
  1593  
  1594  	pluginCode, err := ioutil.ReadFile(fullPath)
  1595  	require.NoError(t, err)
  1596  	require.NotEmpty(t, pluginCode)
  1597  
  1598  	tearDown, ids, errors := SetAppEnvironmentWithPlugins(t, []string{string(pluginCode)}, th.App, th.App.NewPluginAPI)
  1599  	defer tearDown()
  1600  	require.NoError(t, errors[0])
  1601  	require.Len(t, ids, 1)
  1602  
  1603  	pluginID := ids[0]
  1604  	require.NotEmpty(t, pluginID)
  1605  
  1606  	reqURL := fmt.Sprintf("http://localhost:%d/plugins/%s", th.Server.ListenAddr.Port, pluginID)
  1607  	req, err := http.NewRequest("GET", reqURL, nil)
  1608  	require.NoError(t, err)
  1609  
  1610  	client := &http.Client{}
  1611  	resp, err := client.Do(req)
  1612  	require.NoError(t, err)
  1613  
  1614  	defer resp.Body.Close()
  1615  
  1616  	body, err := ioutil.ReadAll(resp.Body)
  1617  	require.NoError(t, err)
  1618  	require.Equal(t, "OK", string(body))
  1619  }
  1620  
  1621  func TestPluginHTTPUpgradeWebSocket(t *testing.T) {
  1622  	th := Setup(t)
  1623  	defer th.TearDown()
  1624  
  1625  	testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
  1626  	require.True(t, found, "Cannot find tests folder")
  1627  	fullPath := path.Join(testFolder, "manual.test_http_upgrade_websocket_plugin", "main.go")
  1628  
  1629  	pluginCode, err := ioutil.ReadFile(fullPath)
  1630  	require.NoError(t, err)
  1631  	require.NotEmpty(t, pluginCode)
  1632  
  1633  	tearDown, ids, errors := SetAppEnvironmentWithPlugins(t, []string{string(pluginCode)}, th.App, th.App.NewPluginAPI)
  1634  	defer tearDown()
  1635  	require.NoError(t, errors[0])
  1636  	require.Len(t, ids, 1)
  1637  
  1638  	pluginID := ids[0]
  1639  	require.NotEmpty(t, pluginID)
  1640  
  1641  	reqURL := fmt.Sprintf("ws://localhost:%d/plugins/%s", th.Server.ListenAddr.Port, pluginID)
  1642  	wsc, err := model.NewWebSocketClient(reqURL, "")
  1643  	require.Nil(t, err)
  1644  	require.NotNil(t, wsc)
  1645  
  1646  	wsc.Listen()
  1647  	defer wsc.Close()
  1648  
  1649  	resp := <-wsc.ResponseChannel
  1650  	require.Equal(t, resp.Status, model.STATUS_OK)
  1651  
  1652  	for i := 0; i < 10; i++ {
  1653  		wsc.SendMessage("custom_action", map[string]interface{}{"value": i})
  1654  		var resp *model.WebSocketResponse
  1655  		select {
  1656  		case resp = <-wsc.ResponseChannel:
  1657  		case <-time.After(1 * time.Second):
  1658  		}
  1659  		require.NotNil(t, resp)
  1660  		require.Equal(t, resp.Status, model.STATUS_OK)
  1661  		require.Equal(t, "custom_action", resp.Data["action"])
  1662  		require.Equal(t, float64(i), resp.Data["value"])
  1663  	}
  1664  }
  1665  
  1666  func TestPluginExecuteSlashCommand(t *testing.T) {
  1667  	th := Setup(t).InitBasic()
  1668  	defer th.TearDown()
  1669  	api := th.SetupPluginAPI()
  1670  
  1671  	newUser := th.CreateUser()
  1672  	th.LinkUserToTeam(newUser, th.BasicTeam)
  1673  
  1674  	t.Run("run invite command", func(t *testing.T) {
  1675  		_, err := api.ExecuteSlashCommand(&model.CommandArgs{
  1676  			Command:   "/invite @" + newUser.Username,
  1677  			TeamId:    th.BasicTeam.Id,
  1678  			UserId:    th.BasicUser.Id,
  1679  			ChannelId: th.BasicChannel.Id,
  1680  		})
  1681  		require.NoError(t, err)
  1682  		_, err2 := th.App.GetChannelMember(th.BasicChannel.Id, newUser.Id)
  1683  		require.Nil(t, err2)
  1684  	})
  1685  }
  1686  
  1687  func TestPluginAPISearchPostsInTeamByUser(t *testing.T) {
  1688  	th := Setup(t).InitBasic()
  1689  	defer th.TearDown()
  1690  	api := th.SetupPluginAPI()
  1691  
  1692  	basicPostText := &th.BasicPost.Message
  1693  	unknwonTerm := "Unknown Message"
  1694  
  1695  	testCases := []struct {
  1696  		description      string
  1697  		teamId           string
  1698  		userId           string
  1699  		params           model.SearchParameter
  1700  		expectedPostsLen int
  1701  	}{
  1702  		{
  1703  			"empty params",
  1704  			th.BasicTeam.Id,
  1705  			th.BasicUser.Id,
  1706  			model.SearchParameter{},
  1707  			0,
  1708  		},
  1709  		{
  1710  			"doesn't match any posts",
  1711  			th.BasicTeam.Id,
  1712  			th.BasicUser.Id,
  1713  			model.SearchParameter{Terms: &unknwonTerm},
  1714  			0,
  1715  		},
  1716  		{
  1717  			"matched posts",
  1718  			th.BasicTeam.Id,
  1719  			th.BasicUser.Id,
  1720  			model.SearchParameter{Terms: basicPostText},
  1721  			1,
  1722  		},
  1723  	}
  1724  
  1725  	for _, testCase := range testCases {
  1726  		t.Run(testCase.description, func(t *testing.T) {
  1727  			searchResults, err := api.SearchPostsInTeamForUser(testCase.teamId, testCase.userId, testCase.params)
  1728  			assert.Nil(t, err)
  1729  			assert.Equal(t, testCase.expectedPostsLen, len(searchResults.Posts))
  1730  		})
  1731  	}
  1732  }
  1733  
  1734  func TestPluginAPICreateCommandAndListCommands(t *testing.T) {
  1735  	th := Setup(t).InitBasic()
  1736  	defer th.TearDown()
  1737  	api := th.SetupPluginAPI()
  1738  
  1739  	foundCommand := func(listXCommand func(teamId string) ([]*model.Command, error)) bool {
  1740  		cmds, appErr := listXCommand(th.BasicTeam.Id)
  1741  		require.Nil(t, appErr)
  1742  
  1743  		for _, cmd := range cmds {
  1744  			if cmd.Trigger == "testcmd" {
  1745  				return true
  1746  			}
  1747  		}
  1748  		return false
  1749  	}
  1750  
  1751  	require.False(t, foundCommand(api.ListCommands))
  1752  
  1753  	cmd := &model.Command{
  1754  		TeamId:  th.BasicTeam.Id,
  1755  		Trigger: "testcmd",
  1756  		Method:  "G",
  1757  		URL:     "http://test.com/testcmd",
  1758  	}
  1759  
  1760  	cmd, appErr := api.CreateCommand(cmd)
  1761  	require.Nil(t, appErr)
  1762  
  1763  	newCmd, appErr := api.GetCommand(cmd.Id)
  1764  	require.Nil(t, appErr)
  1765  	require.Equal(t, "pluginid", newCmd.PluginId)
  1766  	require.Equal(t, "", newCmd.CreatorId)
  1767  	require.True(t, foundCommand(api.ListCommands))
  1768  	require.True(t, foundCommand(api.ListCustomCommands))
  1769  	require.False(t, foundCommand(api.ListPluginCommands))
  1770  }
  1771  
  1772  func TestPluginAPIUpdateCommand(t *testing.T) {
  1773  	th := Setup(t).InitBasic()
  1774  	defer th.TearDown()
  1775  	api := th.SetupPluginAPI()
  1776  
  1777  	cmd := &model.Command{
  1778  		TeamId:  th.BasicTeam.Id,
  1779  		Trigger: "testcmd",
  1780  		Method:  "G",
  1781  		URL:     "http://test.com/testcmd",
  1782  	}
  1783  
  1784  	cmd, appErr := api.CreateCommand(cmd)
  1785  	require.Nil(t, appErr)
  1786  
  1787  	newCmd, appErr := api.GetCommand(cmd.Id)
  1788  	require.Nil(t, appErr)
  1789  	require.Equal(t, "pluginid", newCmd.PluginId)
  1790  	require.Equal(t, "", newCmd.CreatorId)
  1791  
  1792  	newCmd.Trigger = "NewTrigger"
  1793  	newCmd.PluginId = "CannotChangeMe"
  1794  	newCmd2, appErr := api.UpdateCommand(newCmd.Id, newCmd)
  1795  	require.Nil(t, appErr)
  1796  	require.Equal(t, "pluginid", newCmd2.PluginId)
  1797  	require.Equal(t, "newtrigger", newCmd2.Trigger)
  1798  
  1799  	team1 := th.CreateTeam()
  1800  
  1801  	newCmd2.PluginId = "CannotChangeMe"
  1802  	newCmd2.Trigger = "anotherNewTrigger"
  1803  	newCmd2.TeamId = team1.Id
  1804  	newCmd3, appErr := api.UpdateCommand(newCmd2.Id, newCmd2)
  1805  	require.Nil(t, appErr)
  1806  	require.Equal(t, "pluginid", newCmd3.PluginId)
  1807  	require.Equal(t, "anothernewtrigger", newCmd3.Trigger)
  1808  	require.Equal(t, team1.Id, newCmd3.TeamId)
  1809  
  1810  	newCmd3.Trigger = "anotherNewTriggerAgain"
  1811  	newCmd3.TeamId = ""
  1812  	newCmd4, appErr := api.UpdateCommand(newCmd2.Id, newCmd2)
  1813  	require.Nil(t, appErr)
  1814  	require.Equal(t, "anothernewtriggeragain", newCmd4.Trigger)
  1815  	require.Equal(t, team1.Id, newCmd4.TeamId)
  1816  
  1817  }