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