github.com/vnforks/kid@v5.11.1+incompatible/app/plugin_api_test.go (about)

     1  // Copyright (c) 2017-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  	"fmt"
    10  	"image"
    11  	"image/color"
    12  	"image/png"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/mattermost/mattermost-server/model"
    21  	"github.com/mattermost/mattermost-server/plugin"
    22  	"github.com/mattermost/mattermost-server/services/mailservice"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) {
    28  	pluginDir, err := ioutil.TempDir("", "")
    29  	require.NoError(t, err)
    30  	webappPluginDir, err := ioutil.TempDir("", "")
    31  	require.NoError(t, err)
    32  	defer os.RemoveAll(pluginDir)
    33  	defer os.RemoveAll(webappPluginDir)
    34  
    35  	env, err := plugin.NewEnvironment(app.NewPluginAPI, pluginDir, webappPluginDir, app.Log)
    36  	require.NoError(t, err)
    37  
    38  	backend := filepath.Join(pluginDir, pluginId, "backend.exe")
    39  	compileGo(t, pluginCode, backend)
    40  
    41  	ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600)
    42  	manifest, activated, reterr := env.Activate(pluginId)
    43  	require.Nil(t, reterr)
    44  	require.NotNil(t, manifest)
    45  	require.True(t, activated)
    46  
    47  	app.SetPluginsEnvironment(env)
    48  }
    49  
    50  func TestPluginAPIGetUsers(t *testing.T) {
    51  	th := Setup(t)
    52  	defer th.TearDown()
    53  	api := th.SetupPluginAPI()
    54  
    55  	user1, err := th.App.CreateUser(&model.User{
    56  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
    57  		Password: "password",
    58  		Username: "user1" + model.NewId(),
    59  	})
    60  	require.Nil(t, err)
    61  	defer th.App.PermanentDeleteUser(user1)
    62  
    63  	user2, err := th.App.CreateUser(&model.User{
    64  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
    65  		Password: "password",
    66  		Username: "user2" + model.NewId(),
    67  	})
    68  	require.Nil(t, err)
    69  	defer th.App.PermanentDeleteUser(user2)
    70  
    71  	user3, err := th.App.CreateUser(&model.User{
    72  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
    73  		Password: "password",
    74  		Username: "user3" + model.NewId(),
    75  	})
    76  	require.Nil(t, err)
    77  	defer th.App.PermanentDeleteUser(user3)
    78  
    79  	user4, err := th.App.CreateUser(&model.User{
    80  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
    81  		Password: "password",
    82  		Username: "user4" + model.NewId(),
    83  	})
    84  	require.Nil(t, err)
    85  	defer th.App.PermanentDeleteUser(user4)
    86  
    87  	testCases := []struct {
    88  		Description   string
    89  		Page          int
    90  		PerPage       int
    91  		ExpectedUsers []*model.User
    92  	}{
    93  		{
    94  			"page 0, perPage 0",
    95  			0,
    96  			0,
    97  			[]*model.User{},
    98  		},
    99  		{
   100  			"page 0, perPage 10",
   101  			0,
   102  			10,
   103  			[]*model.User{user1, user2, user3, user4},
   104  		},
   105  		{
   106  			"page 0, perPage 2",
   107  			0,
   108  			2,
   109  			[]*model.User{user1, user2},
   110  		},
   111  		{
   112  			"page 1, perPage 2",
   113  			1,
   114  			2,
   115  			[]*model.User{user3, user4},
   116  		},
   117  		{
   118  			"page 10, perPage 10",
   119  			10,
   120  			10,
   121  			[]*model.User{},
   122  		},
   123  	}
   124  
   125  	for _, testCase := range testCases {
   126  		t.Run(testCase.Description, func(t *testing.T) {
   127  			users, err := api.GetUsers(&model.UserGetOptions{
   128  				Page:    testCase.Page,
   129  				PerPage: testCase.PerPage,
   130  			})
   131  			assert.Nil(t, err)
   132  			assert.Equal(t, testCase.ExpectedUsers, users)
   133  		})
   134  	}
   135  }
   136  
   137  func TestPluginAPIGetUsersInTeam(t *testing.T) {
   138  	th := Setup(t).InitBasic()
   139  	defer th.TearDown()
   140  	api := th.SetupPluginAPI()
   141  
   142  	team1 := th.CreateTeam()
   143  	team2 := th.CreateTeam()
   144  
   145  	user1, err := th.App.CreateUser(&model.User{
   146  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   147  		Password: "password",
   148  		Username: "user1" + model.NewId(),
   149  	})
   150  	require.Nil(t, err)
   151  	defer th.App.PermanentDeleteUser(user1)
   152  
   153  	user2, err := th.App.CreateUser(&model.User{
   154  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   155  		Password: "password",
   156  		Username: "user2" + model.NewId(),
   157  	})
   158  	require.Nil(t, err)
   159  	defer th.App.PermanentDeleteUser(user2)
   160  
   161  	user3, err := th.App.CreateUser(&model.User{
   162  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   163  		Password: "password",
   164  		Username: "user3" + model.NewId(),
   165  	})
   166  	require.Nil(t, err)
   167  	defer th.App.PermanentDeleteUser(user3)
   168  
   169  	user4, err := th.App.CreateUser(&model.User{
   170  		Email:    strings.ToLower(model.NewId()) + "success+test@example.com",
   171  		Password: "password",
   172  		Username: "user4" + model.NewId(),
   173  	})
   174  	require.Nil(t, err)
   175  	defer th.App.PermanentDeleteUser(user4)
   176  
   177  	// Add all users to team 1
   178  	_, _, err = th.App.joinUserToTeam(team1, user1)
   179  	require.Nil(t, err)
   180  	_, _, err = th.App.joinUserToTeam(team1, user2)
   181  	require.Nil(t, err)
   182  	_, _, err = th.App.joinUserToTeam(team1, user3)
   183  	require.Nil(t, err)
   184  	_, _, err = th.App.joinUserToTeam(team1, user4)
   185  	require.Nil(t, err)
   186  
   187  	// Add only user3 and user4 to team 2
   188  	_, _, err = th.App.joinUserToTeam(team2, user3)
   189  	require.Nil(t, err)
   190  	_, _, err = th.App.joinUserToTeam(team2, user4)
   191  	require.Nil(t, err)
   192  
   193  	testCases := []struct {
   194  		Description   string
   195  		TeamId        string
   196  		Page          int
   197  		PerPage       int
   198  		ExpectedUsers []*model.User
   199  	}{
   200  		{
   201  			"unknown team",
   202  			model.NewId(),
   203  			0,
   204  			0,
   205  			[]*model.User{},
   206  		},
   207  		{
   208  			"team 1, page 0, perPage 10",
   209  			team1.Id,
   210  			0,
   211  			10,
   212  			[]*model.User{user1, user2, user3, user4},
   213  		},
   214  		{
   215  			"team 1, page 0, perPage 2",
   216  			team1.Id,
   217  			0,
   218  			2,
   219  			[]*model.User{user1, user2},
   220  		},
   221  		{
   222  			"team 1, page 1, perPage 2",
   223  			team1.Id,
   224  			1,
   225  			2,
   226  			[]*model.User{user3, user4},
   227  		},
   228  		{
   229  			"team 2, page 0, perPage 10",
   230  			team2.Id,
   231  			0,
   232  			10,
   233  			[]*model.User{user3, user4},
   234  		},
   235  	}
   236  
   237  	for _, testCase := range testCases {
   238  		t.Run(testCase.Description, func(t *testing.T) {
   239  			users, err := api.GetUsersInTeam(testCase.TeamId, testCase.Page, testCase.PerPage)
   240  			assert.Nil(t, err)
   241  			assert.Equal(t, testCase.ExpectedUsers, users)
   242  		})
   243  	}
   244  }
   245  
   246  func TestPluginAPIUpdateUserStatus(t *testing.T) {
   247  	th := Setup(t).InitBasic()
   248  	defer th.TearDown()
   249  	api := th.SetupPluginAPI()
   250  
   251  	statuses := []string{model.STATUS_ONLINE, model.STATUS_AWAY, model.STATUS_DND, model.STATUS_OFFLINE}
   252  
   253  	for _, s := range statuses {
   254  		status, err := api.UpdateUserStatus(th.BasicUser.Id, s)
   255  		require.Nil(t, err)
   256  		require.NotNil(t, status)
   257  		assert.Equal(t, s, status.Status)
   258  	}
   259  
   260  	status, err := api.UpdateUserStatus(th.BasicUser.Id, "notrealstatus")
   261  	assert.NotNil(t, err)
   262  	assert.Nil(t, status)
   263  }
   264  
   265  func TestPluginAPIGetFile(t *testing.T) {
   266  	th := Setup(t).InitBasic()
   267  	defer th.TearDown()
   268  	api := th.SetupPluginAPI()
   269  
   270  	// check a valid file first
   271  	uploadTime := time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local)
   272  	filename := "testGetFile"
   273  	fileData := []byte("Hello World")
   274  	info, err := th.App.DoUploadFile(uploadTime, th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, filename, fileData)
   275  	require.Nil(t, err)
   276  	defer func() {
   277  		<-th.App.Srv.Store.FileInfo().PermanentDelete(info.Id)
   278  		th.App.RemoveFile(info.Path)
   279  	}()
   280  
   281  	data, err1 := api.GetFile(info.Id)
   282  	require.Nil(t, err1)
   283  	assert.Equal(t, data, fileData)
   284  
   285  	// then checking invalid file
   286  	data, err = api.GetFile("../fake/testingApi")
   287  	require.NotNil(t, err)
   288  	require.Nil(t, data)
   289  }
   290  func TestPluginAPISavePluginConfig(t *testing.T) {
   291  	th := Setup(t).InitBasic()
   292  	defer th.TearDown()
   293  
   294  	manifest := &model.Manifest{
   295  		Id: "pluginid",
   296  		SettingsSchema: &model.PluginSettingsSchema{
   297  			Settings: []*model.PluginSetting{
   298  				{Key: "MyStringSetting", Type: "text"},
   299  				{Key: "MyIntSetting", Type: "text"},
   300  				{Key: "MyBoolSetting", Type: "bool"},
   301  			},
   302  		},
   303  	}
   304  
   305  	api := NewPluginAPI(th.App, manifest)
   306  
   307  	pluginConfigJsonString := `{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`
   308  
   309  	var pluginConfig map[string]interface{}
   310  	if err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	if err := api.SavePluginConfig(pluginConfig); err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	type Configuration struct {
   319  		MyStringSetting string
   320  		MyIntSetting    int
   321  		MyBoolSetting   bool
   322  	}
   323  
   324  	savedConfiguration := new(Configuration)
   325  	if err := api.LoadPluginConfiguration(savedConfiguration); err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	expectedConfiguration := new(Configuration)
   330  	if err := json.Unmarshal([]byte(pluginConfigJsonString), &expectedConfiguration); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  
   334  	assert.Equal(t, expectedConfiguration, savedConfiguration)
   335  }
   336  
   337  func TestPluginAPIGetPluginConfig(t *testing.T) {
   338  	th := Setup(t).InitBasic()
   339  	defer th.TearDown()
   340  
   341  	manifest := &model.Manifest{
   342  		Id: "pluginid",
   343  		SettingsSchema: &model.PluginSettingsSchema{
   344  			Settings: []*model.PluginSetting{
   345  				{Key: "MyStringSetting", Type: "text"},
   346  				{Key: "MyIntSetting", Type: "text"},
   347  				{Key: "MyBoolSetting", Type: "bool"},
   348  			},
   349  		},
   350  	}
   351  
   352  	api := NewPluginAPI(th.App, manifest)
   353  
   354  	pluginConfigJsonString := `{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`
   355  	var pluginConfig map[string]interface{}
   356  
   357  	if err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig); err != nil {
   358  		t.Fatal(err)
   359  	}
   360  	th.App.UpdateConfig(func(cfg *model.Config) {
   361  		cfg.PluginSettings.Plugins["pluginid"] = pluginConfig
   362  	})
   363  
   364  	savedPluginConfig := api.GetPluginConfig()
   365  	assert.Equal(t, pluginConfig, savedPluginConfig)
   366  }
   367  
   368  func TestPluginAPILoadPluginConfiguration(t *testing.T) {
   369  	th := Setup(t).InitBasic()
   370  	defer th.TearDown()
   371  
   372  	var pluginJson map[string]interface{}
   373  	if err := json.Unmarshal([]byte(`{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`), &pluginJson); err != nil {
   374  		t.Fatal(err)
   375  	}
   376  	th.App.UpdateConfig(func(cfg *model.Config) {
   377  		cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
   378  	})
   379  	setupPluginApiTest(t,
   380  		`
   381  		package main
   382  
   383  		import (
   384  			"github.com/mattermost/mattermost-server/plugin"
   385  			"github.com/mattermost/mattermost-server/model"
   386  			"fmt"
   387  		)
   388  
   389  		type configuration struct {
   390  			MyStringSetting string
   391  			MyIntSetting int
   392  			MyBoolSetting bool
   393  		}
   394  
   395  		type MyPlugin struct {
   396  			plugin.MattermostPlugin
   397  
   398  			configuration configuration
   399  		}
   400  
   401  		func (p *MyPlugin) OnConfigurationChange() error {
   402  			if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil {
   403  				return err
   404  			}
   405  
   406  			return nil
   407  		}
   408  
   409  		func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   410  			return nil, fmt.Sprintf("%v%v%v", p.configuration.MyStringSetting, p.configuration.MyIntSetting, p.configuration.MyBoolSetting)
   411  		}
   412  
   413  		func main() {
   414  			plugin.ClientMain(&MyPlugin{})
   415  		}
   416  	`,
   417  		`{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": {
   418  		"settings": [
   419  			{
   420  				"key": "MyStringSetting",
   421  				"type": "text"
   422  			},
   423  			{
   424  				"key": "MyIntSetting",
   425  				"type": "text"
   426  			},
   427  			{
   428  				"key": "MyBoolSetting",
   429  				"type": "bool"
   430  			}
   431  		]
   432  	}}`, "testloadpluginconfig", th.App)
   433  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testloadpluginconfig")
   434  	assert.NoError(t, err)
   435  	_, ret := hooks.MessageWillBePosted(nil, nil)
   436  	assert.Equal(t, "str32true", ret)
   437  }
   438  
   439  func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) {
   440  	th := Setup(t).InitBasic()
   441  	defer th.TearDown()
   442  
   443  	var pluginJson map[string]interface{}
   444  	if err := json.Unmarshal([]byte(`{"mystringsetting": "override"}`), &pluginJson); err != nil {
   445  		t.Fatal(err)
   446  	}
   447  	th.App.UpdateConfig(func(cfg *model.Config) {
   448  		cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
   449  	})
   450  	setupPluginApiTest(t,
   451  		`
   452  		package main
   453  
   454  		import (
   455  			"github.com/mattermost/mattermost-server/plugin"
   456  			"github.com/mattermost/mattermost-server/model"
   457  			"fmt"
   458  		)
   459  
   460  		type configuration struct {
   461  			MyStringSetting string
   462  			MyIntSetting int
   463  			MyBoolSetting bool
   464  		}
   465  
   466  		type MyPlugin struct {
   467  			plugin.MattermostPlugin
   468  
   469  			configuration configuration
   470  		}
   471  
   472  		func (p *MyPlugin) OnConfigurationChange() error {
   473  			if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil {
   474  				return err
   475  			}
   476  
   477  			return nil
   478  		}
   479  
   480  		func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   481  			return nil, fmt.Sprintf("%v%v%v", p.configuration.MyStringSetting, p.configuration.MyIntSetting, p.configuration.MyBoolSetting)
   482  		}
   483  
   484  		func main() {
   485  			plugin.ClientMain(&MyPlugin{})
   486  		}
   487  	`,
   488  		`{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": {
   489  		"settings": [
   490  			{
   491  				"key": "MyStringSetting",
   492  				"type": "text",
   493  				"default": "notthis"
   494  			},
   495  			{
   496  				"key": "MyIntSetting",
   497  				"type": "text",
   498  				"default": 35
   499  			},
   500  			{
   501  				"key": "MyBoolSetting",
   502  				"type": "bool",
   503  				"default": true
   504  			}
   505  		]
   506  	}}`, "testloadpluginconfig", th.App)
   507  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testloadpluginconfig")
   508  	assert.NoError(t, err)
   509  	_, ret := hooks.MessageWillBePosted(nil, nil)
   510  	assert.Equal(t, "override35true", ret)
   511  }
   512  
   513  func TestPluginAPIGetBundlePath(t *testing.T) {
   514  	th := Setup(t).InitBasic()
   515  	defer th.TearDown()
   516  
   517  	setupPluginApiTest(t,
   518  		`
   519  		package main
   520  
   521  		import (
   522  			"github.com/mattermost/mattermost-server/plugin"
   523  			"github.com/mattermost/mattermost-server/model"
   524  		)
   525  
   526  		type MyPlugin struct {
   527  			plugin.MattermostPlugin
   528  		}
   529  
   530  		func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   531  			bundlePath, err := p.API.GetBundlePath()
   532  			if err != nil {
   533  				return nil, err.Error() + "failed get bundle path"
   534  			}
   535  
   536  			return nil, bundlePath
   537  		}
   538  
   539  		func main() {
   540  			plugin.ClientMain(&MyPlugin{})
   541  		}
   542  		`, `{"id": "testplugin", "backend": {"executable": "backend.exe"}}`, "testplugin", th.App)
   543  
   544  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testplugin")
   545  	require.Nil(t, err)
   546  	require.NotNil(t, hooks)
   547  	bundlePath, err := filepath.Abs(filepath.Join(*th.App.Config().PluginSettings.Directory, "testplugin"))
   548  	require.Nil(t, err)
   549  
   550  	_, errString := hooks.MessageWillBePosted(nil, nil)
   551  	assert.Equal(t, bundlePath, errString)
   552  }
   553  
   554  func TestPluginAPIGetProfileImage(t *testing.T) {
   555  	th := Setup(t).InitBasic()
   556  	defer th.TearDown()
   557  	api := th.SetupPluginAPI()
   558  
   559  	// check existing user first
   560  	data, err := api.GetProfileImage(th.BasicUser.Id)
   561  	require.Nil(t, err)
   562  	require.NotEmpty(t, data)
   563  
   564  	// then unknown user
   565  	data, err = api.GetProfileImage(model.NewId())
   566  	require.NotNil(t, err)
   567  	require.Nil(t, data)
   568  }
   569  
   570  func TestPluginAPISetProfileImage(t *testing.T) {
   571  	th := Setup(t).InitBasic()
   572  	defer th.TearDown()
   573  	api := th.SetupPluginAPI()
   574  
   575  	// Create an 128 x 128 image
   576  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   577  	// Draw a red dot at (2, 3)
   578  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   579  	buf := new(bytes.Buffer)
   580  	err := png.Encode(buf, img)
   581  	require.Nil(t, err)
   582  	dataBytes := buf.Bytes()
   583  
   584  	// Set the user profile image
   585  	err = api.SetProfileImage(th.BasicUser.Id, dataBytes)
   586  	require.Nil(t, err)
   587  
   588  	// Get the user profile image to check
   589  	imageProfile, err := api.GetProfileImage(th.BasicUser.Id)
   590  	require.Nil(t, err)
   591  	require.NotEmpty(t, imageProfile)
   592  
   593  	colorful := color.NRGBA{255, 0, 0, 255}
   594  	byteReader := bytes.NewReader(imageProfile)
   595  	img2, _, err2 := image.Decode(byteReader)
   596  	require.Nil(t, err2)
   597  	require.Equal(t, img2.At(2, 3), colorful)
   598  }
   599  
   600  func TestPluginAPIGetPlugins(t *testing.T) {
   601  	th := Setup(t).InitBasic()
   602  	defer th.TearDown()
   603  	api := th.SetupPluginAPI()
   604  
   605  	pluginCode := `
   606      package main
   607  
   608      import (
   609        "github.com/mattermost/mattermost-server/plugin"
   610      )
   611  
   612      type MyPlugin struct {
   613        plugin.MattermostPlugin
   614      }
   615  
   616      func main() {
   617        plugin.ClientMain(&MyPlugin{})
   618      }
   619    `
   620  
   621  	pluginDir, err := ioutil.TempDir("", "")
   622  	require.NoError(t, err)
   623  	webappPluginDir, err := ioutil.TempDir("", "")
   624  	require.NoError(t, err)
   625  	defer os.RemoveAll(pluginDir)
   626  	defer os.RemoveAll(webappPluginDir)
   627  
   628  	env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log)
   629  	require.NoError(t, err)
   630  
   631  	pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
   632  	var pluginManifests []*model.Manifest
   633  	for _, pluginID := range pluginIDs {
   634  		backend := filepath.Join(pluginDir, pluginID, "backend.exe")
   635  		compileGo(t, pluginCode, backend)
   636  
   637  		ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
   638  		manifest, activated, reterr := env.Activate(pluginID)
   639  
   640  		require.Nil(t, reterr)
   641  		require.NotNil(t, manifest)
   642  		require.True(t, activated)
   643  		pluginManifests = append(pluginManifests, manifest)
   644  	}
   645  	th.App.SetPluginsEnvironment(env)
   646  
   647  	// Decativate the last one for testing
   648  	sucess := env.Deactivate(pluginIDs[len(pluginIDs)-1])
   649  	require.True(t, sucess)
   650  
   651  	// check existing user first
   652  	plugins, err := api.GetPlugins()
   653  	assert.Nil(t, err)
   654  	assert.NotEmpty(t, plugins)
   655  	assert.Equal(t, pluginManifests, plugins)
   656  }
   657  
   658  func TestPluginAPIGetTeamIcon(t *testing.T) {
   659  	th := Setup(t).InitBasic()
   660  	defer th.TearDown()
   661  	api := th.SetupPluginAPI()
   662  
   663  	// Create an 128 x 128 image
   664  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   665  	// Draw a red dot at (2, 3)
   666  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   667  	buf := new(bytes.Buffer)
   668  	err := png.Encode(buf, img)
   669  	require.Nil(t, err)
   670  	dataBytes := buf.Bytes()
   671  	fileReader := bytes.NewReader(dataBytes)
   672  
   673  	// Set the Team Icon
   674  	err = th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
   675  	require.Nil(t, err)
   676  
   677  	// Get the team icon to check
   678  	teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
   679  	require.Nil(t, err)
   680  	require.NotEmpty(t, teamIcon)
   681  
   682  	colorful := color.NRGBA{255, 0, 0, 255}
   683  	byteReader := bytes.NewReader(teamIcon)
   684  	img2, _, err2 := image.Decode(byteReader)
   685  	require.Nil(t, err2)
   686  	require.Equal(t, img2.At(2, 3), colorful)
   687  }
   688  
   689  func TestPluginAPISetTeamIcon(t *testing.T) {
   690  	th := Setup(t).InitBasic()
   691  	defer th.TearDown()
   692  	api := th.SetupPluginAPI()
   693  
   694  	// Create an 128 x 128 image
   695  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   696  	// Draw a red dot at (2, 3)
   697  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   698  	buf := new(bytes.Buffer)
   699  	err := png.Encode(buf, img)
   700  	require.Nil(t, err)
   701  	dataBytes := buf.Bytes()
   702  
   703  	// Set the user profile image
   704  	err = api.SetTeamIcon(th.BasicTeam.Id, dataBytes)
   705  	require.Nil(t, err)
   706  
   707  	// Get the user profile image to check
   708  	teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
   709  	require.Nil(t, err)
   710  	require.NotEmpty(t, teamIcon)
   711  
   712  	colorful := color.NRGBA{255, 0, 0, 255}
   713  	byteReader := bytes.NewReader(teamIcon)
   714  	img2, _, err2 := image.Decode(byteReader)
   715  	require.Nil(t, err2)
   716  	require.Equal(t, img2.At(2, 3), colorful)
   717  }
   718  
   719  func TestPluginAPISearchChannels(t *testing.T) {
   720  	th := Setup(t).InitBasic()
   721  	defer th.TearDown()
   722  	api := th.SetupPluginAPI()
   723  
   724  	t.Run("all fine", func(t *testing.T) {
   725  		channels, err := api.SearchChannels(th.BasicTeam.Id, th.BasicChannel.Name)
   726  		assert.Nil(t, err)
   727  		assert.Len(t, channels, 1)
   728  	})
   729  
   730  	t.Run("invalid team id", func(t *testing.T) {
   731  		channels, err := api.SearchChannels("invalidid", th.BasicChannel.Name)
   732  		assert.Nil(t, err)
   733  		assert.Empty(t, channels)
   734  	})
   735  }
   736  
   737  func TestPluginAPISearchPostsInTeam(t *testing.T) {
   738  	th := Setup(t).InitBasic()
   739  	defer th.TearDown()
   740  	api := th.SetupPluginAPI()
   741  
   742  	testCases := []struct {
   743  		description      string
   744  		teamId           string
   745  		params           []*model.SearchParams
   746  		expectedPostsLen int
   747  	}{
   748  		{
   749  			"nil params",
   750  			th.BasicTeam.Id,
   751  			nil,
   752  			0,
   753  		},
   754  		{
   755  			"empty params",
   756  			th.BasicTeam.Id,
   757  			[]*model.SearchParams{},
   758  			0,
   759  		},
   760  		{
   761  			"doesn't match any posts",
   762  			th.BasicTeam.Id,
   763  			model.ParseSearchParams("bad message", 0),
   764  			0,
   765  		},
   766  		{
   767  			"matched posts",
   768  			th.BasicTeam.Id,
   769  			model.ParseSearchParams(th.BasicPost.Message, 0),
   770  			1,
   771  		},
   772  	}
   773  
   774  	for _, testCase := range testCases {
   775  		t.Run(testCase.description, func(t *testing.T) {
   776  			posts, err := api.SearchPostsInTeam(testCase.teamId, testCase.params)
   777  			assert.Nil(t, err)
   778  			assert.Equal(t, testCase.expectedPostsLen, len(posts))
   779  		})
   780  	}
   781  }
   782  
   783  func TestPluginAPIGetChannelsForTeamForUser(t *testing.T) {
   784  	th := Setup(t).InitBasic()
   785  	defer th.TearDown()
   786  	api := th.SetupPluginAPI()
   787  
   788  	t.Run("all fine", func(t *testing.T) {
   789  		channels, err := api.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false)
   790  		assert.Nil(t, err)
   791  		assert.Len(t, channels, 3)
   792  	})
   793  
   794  	t.Run("invalid team id", func(t *testing.T) {
   795  		channels, err := api.GetChannelsForTeamForUser("invalidid", th.BasicUser.Id, false)
   796  		assert.NotNil(t, err)
   797  		assert.Empty(t, channels)
   798  	})
   799  }
   800  
   801  func TestPluginAPIRemoveTeamIcon(t *testing.T) {
   802  	th := Setup(t).InitBasic()
   803  	defer th.TearDown()
   804  	api := th.SetupPluginAPI()
   805  
   806  	// Create an 128 x 128 image
   807  	img := image.NewRGBA(image.Rect(0, 0, 128, 128))
   808  
   809  	// Draw a red dot at (2, 3)
   810  	img.Set(2, 3, color.RGBA{255, 0, 0, 255})
   811  	buf := new(bytes.Buffer)
   812  	err1 := png.Encode(buf, img)
   813  	require.Nil(t, err1)
   814  	dataBytes := buf.Bytes()
   815  	fileReader := bytes.NewReader(dataBytes)
   816  
   817  	// Set the Team Icon
   818  	err := th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
   819  	require.Nil(t, err)
   820  	err = api.RemoveTeamIcon(th.BasicTeam.Id)
   821  	require.Nil(t, err)
   822  }
   823  
   824  func TestPluginAPIUpdateUserActive(t *testing.T) {
   825  	th := Setup(t).InitBasic()
   826  	defer th.TearDown()
   827  	api := th.SetupPluginAPI()
   828  
   829  	err := api.UpdateUserActive(th.BasicUser.Id, true)
   830  	require.Nil(t, err)
   831  	user, err := api.GetUser(th.BasicUser.Id)
   832  	require.Nil(t, err)
   833  	require.Equal(t, int64(0), user.DeleteAt)
   834  
   835  	err = api.UpdateUserActive(th.BasicUser.Id, false)
   836  	require.Nil(t, err)
   837  	user, err = api.GetUser(th.BasicUser.Id)
   838  	require.Nil(t, err)
   839  	require.NotNil(t, user)
   840  	require.NotEqual(t, int64(0), user.DeleteAt)
   841  
   842  	err = api.UpdateUserActive(th.BasicUser.Id, true)
   843  	require.Nil(t, err)
   844  	err = api.UpdateUserActive(th.BasicUser.Id, true)
   845  	require.Nil(t, err)
   846  	user, err = api.GetUser(th.BasicUser.Id)
   847  	require.Nil(t, err)
   848  	require.Equal(t, int64(0), user.DeleteAt)
   849  }
   850  
   851  func TestPluginAPIGetDirectChannel(t *testing.T) {
   852  	th := Setup(t).InitBasic()
   853  	defer th.TearDown()
   854  	api := th.SetupPluginAPI()
   855  
   856  	dm1, err := api.GetDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
   857  	require.Nil(t, err)
   858  	require.NotEmpty(t, dm1)
   859  
   860  	dm2, err := api.GetDirectChannel(th.BasicUser.Id, th.BasicUser.Id)
   861  	require.Nil(t, err)
   862  	require.NotEmpty(t, dm2)
   863  
   864  	dm3, err := api.GetDirectChannel(th.BasicUser.Id, model.NewId())
   865  	require.NotNil(t, err)
   866  	require.Empty(t, dm3)
   867  }
   868  
   869  func TestPluginAPISendMail(t *testing.T) {
   870  	th := Setup(t).InitBasic()
   871  	defer th.TearDown()
   872  	api := th.SetupPluginAPI()
   873  
   874  	to := th.BasicUser.Email
   875  	subject := "testing plugin api sending email"
   876  	body := "this is a test."
   877  
   878  	err := api.SendMail(to, subject, body)
   879  	require.Nil(t, err)
   880  
   881  	// Check if we received the email
   882  	var resultsMailbox mailservice.JSONMessageHeaderInbucket
   883  	errMail := mailservice.RetryInbucket(5, func() error {
   884  		var err error
   885  		resultsMailbox, err = mailservice.GetMailBox(to)
   886  		return err
   887  	})
   888  	require.Nil(t, errMail)
   889  	require.NotZero(t, len(resultsMailbox))
   890  	require.True(t, strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], to))
   891  
   892  	resultsEmail, err1 := mailservice.GetMessageFromMailbox(to, resultsMailbox[len(resultsMailbox)-1].ID)
   893  	require.Nil(t, err1)
   894  	require.Equal(t, resultsEmail.Subject, subject)
   895  	require.Equal(t, resultsEmail.Body.Text, body)
   896  
   897  }
   898  
   899  func TestPluginAPI_SearchTeams(t *testing.T) {
   900  	th := Setup(t).InitBasic()
   901  	defer th.TearDown()
   902  
   903  	api := th.SetupPluginAPI()
   904  
   905  	t.Run("all fine", func(t *testing.T) {
   906  		teams, err := api.SearchTeams(th.BasicTeam.Name)
   907  		assert.Nil(t, err)
   908  		assert.Len(t, teams, 1)
   909  
   910  		teams, err = api.SearchTeams(th.BasicTeam.DisplayName)
   911  		assert.Nil(t, err)
   912  		assert.Len(t, teams, 1)
   913  
   914  		teams, err = api.SearchTeams(th.BasicTeam.Name[:3])
   915  		assert.Nil(t, err)
   916  		assert.Len(t, teams, 1)
   917  	})
   918  
   919  	t.Run("invalid team name", func(t *testing.T) {
   920  		teams, err := api.SearchTeams("not found")
   921  		assert.Nil(t, err)
   922  		assert.Empty(t, teams)
   923  	})
   924  }
   925  
   926  func TestPluginBots(t *testing.T) {
   927  	th := Setup(t).InitBasic()
   928  	defer th.TearDown()
   929  
   930  	setupPluginApiTest(t,
   931  		`
   932  		package main
   933  
   934  		import (
   935  			"github.com/mattermost/mattermost-server/plugin"
   936  			"github.com/mattermost/mattermost-server/model"
   937  		)
   938  
   939  		type MyPlugin struct {
   940  			plugin.MattermostPlugin
   941  		}
   942  
   943  		func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   944  			createdBot, err := p.API.CreateBot(&model.Bot{
   945  				Username: "bot",
   946  				Description: "a plugin bot",
   947  			})
   948  			if err != nil {
   949  				return nil, err.Error() + "failed to create bot"
   950  			}
   951  
   952  			fetchedBot, err := p.API.GetBot(createdBot.UserId, false)
   953  			if err != nil {
   954  				return nil, err.Error() + "failed to get bot"
   955  			}
   956  			if fetchedBot.Description != "a plugin bot" {
   957  				return nil, "GetBot did not return the expected bot Description"
   958  			}
   959  			if fetchedBot.OwnerId != "testpluginbots" {
   960  				return nil, "GetBot did not return the expected bot OwnerId"
   961  			}
   962  
   963  			updatedDescription := createdBot.Description + ", updated"
   964  			patchedBot, err := p.API.PatchBot(createdBot.UserId, &model.BotPatch{
   965  				Description: &updatedDescription,
   966  			})
   967  			if err != nil {
   968  				return nil, err.Error() + "failed to patch bot"
   969  			}
   970  
   971  			fetchedBot, err = p.API.GetBot(patchedBot.UserId, false)
   972  			if err != nil {
   973  				return nil, err.Error() + "failed to get bot"
   974  			}
   975  
   976  			if fetchedBot.UserId != patchedBot.UserId {
   977  				return nil, "GetBot did not return the expected bot"
   978  			}
   979  			if fetchedBot.Description != "a plugin bot, updated" {
   980  				return nil, "GetBot did not return the updated bot Description"
   981  			}
   982  
   983  			fetchedBots, err := p.API.GetBots(&model.BotGetOptions{
   984  				Page: 0,
   985  				PerPage: 1,
   986  				OwnerId: "",
   987  				IncludeDeleted: false,
   988  			})
   989  			if err != nil {
   990  				return nil, err.Error() + "failed to get bots"
   991  			}
   992  
   993  			if len(fetchedBots) != 1 {
   994  				return nil, "GetBots did not return a single bot"
   995  			}
   996  			if fetchedBot.UserId != fetchedBots[0].UserId {
   997  				return nil, "GetBots did not return the expected bot"
   998  			}
   999  
  1000  			_, err = p.API.UpdateBotActive(fetchedBot.UserId, false)
  1001  			if err != nil {
  1002  				return nil, err.Error() + "failed to disable bot"
  1003  			}
  1004  
  1005  			fetchedBot, err = p.API.GetBot(patchedBot.UserId, false)
  1006  			if err == nil {
  1007  				return nil, "expected not to find disabled bot"
  1008  			}
  1009  
  1010  			_, err = p.API.UpdateBotActive(fetchedBot.UserId, true)
  1011  			if err != nil {
  1012  				return nil, err.Error() + "failed to disable bot"
  1013  			}
  1014  
  1015  			fetchedBot, err = p.API.GetBot(patchedBot.UserId, false)
  1016  			if err != nil {
  1017  				return nil, err.Error() + "failed to get bot after enabling"
  1018  			}
  1019  			if fetchedBot.UserId != patchedBot.UserId {
  1020  				return nil, "GetBot did not return the expected bot after enabling"
  1021  			}
  1022  
  1023  			err = p.API.PermanentDeleteBot(patchedBot.UserId)
  1024  			if err != nil {
  1025  				return nil, err.Error() + "failed to delete bot"
  1026  			}
  1027  
  1028  			_, err = p.API.GetBot(patchedBot.UserId, false)
  1029  			if err == nil {
  1030  				return nil, err.Error() + "found bot after permanently deleting"
  1031  			}
  1032  
  1033  			createdBotWithOverriddenCreator, err := p.API.CreateBot(&model.Bot{
  1034  				Username: "bot",
  1035  				Description: "a plugin bot",
  1036  				OwnerId: "abc123",
  1037  			})
  1038  			if err != nil {
  1039  				return nil, err.Error() + "failed to create bot with overridden creator"
  1040  			}
  1041  
  1042  			fetchedBot, err = p.API.GetBot(createdBotWithOverriddenCreator.UserId, false)
  1043  			if err != nil {
  1044  				return nil, err.Error() + "failed to get bot"
  1045  			}
  1046  			if fetchedBot.Description != "a plugin bot" {
  1047  				return nil, "GetBot did not return the expected bot Description"
  1048  			}
  1049  			if fetchedBot.OwnerId != "abc123" {
  1050  				return nil, "GetBot did not return the expected bot OwnerId"
  1051  			}
  1052  
  1053  			return nil, ""
  1054  		}
  1055  
  1056  		func main() {
  1057  			plugin.ClientMain(&MyPlugin{})
  1058  		}
  1059  		`,
  1060  		`{"id": "testpluginbots", "backend": {"executable": "backend.exe"}}`,
  1061  		"testpluginbots",
  1062  		th.App,
  1063  	)
  1064  
  1065  	hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testpluginbots")
  1066  	assert.NoError(t, err)
  1067  	_, errString := hooks.MessageWillBePosted(nil, nil)
  1068  	assert.Empty(t, errString)
  1069  }
  1070  
  1071  func TestPluginAPI_GetTeamMembersForUser(t *testing.T) {
  1072  	th := Setup(t).InitBasic()
  1073  	defer th.TearDown()
  1074  	api := th.SetupPluginAPI()
  1075  
  1076  	userId := th.BasicUser.Id
  1077  	teamMembers, err := api.GetTeamMembersForUser(userId, 0, 10)
  1078  	assert.Nil(t, err)
  1079  	assert.Equal(t, len(teamMembers), 1)
  1080  	assert.Equal(t, teamMembers[0].TeamId, th.BasicTeam.Id)
  1081  	assert.Equal(t, teamMembers[0].UserId, th.BasicUser.Id)
  1082  }
  1083  
  1084  func TestPluginAPI_GetChannelMembersForUser(t *testing.T) {
  1085  	th := Setup(t).InitBasic()
  1086  	defer th.TearDown()
  1087  	api := th.SetupPluginAPI()
  1088  
  1089  	userId := th.BasicUser.Id
  1090  	teamId := th.BasicTeam.Id
  1091  	channelMembers, err := api.GetChannelMembersForUser(teamId, userId, 0, 10)
  1092  
  1093  	assert.Nil(t, err)
  1094  	assert.Equal(t, len(channelMembers), 3)
  1095  	assert.Equal(t, channelMembers[0].UserId, th.BasicUser.Id)
  1096  }