github.com/nhannv/mattermost-server@v5.11.1+incompatible/app/plugin_hooks_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  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/pkg/errors"
    20  
    21  	"github.com/mattermost/mattermost-server/model"
    22  	"github.com/mattermost/mattermost-server/plugin"
    23  	"github.com/mattermost/mattermost-server/plugin/plugintest"
    24  	"github.com/mattermost/mattermost-server/plugin/plugintest/mock"
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  func compileGo(t *testing.T, sourceCode, outputPath string) {
    30  	dir, err := ioutil.TempDir(".", "")
    31  	require.NoError(t, err)
    32  	defer os.RemoveAll(dir)
    33  	require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte(sourceCode), 0600))
    34  	cmd := exec.Command("go", "build", "-o", outputPath, "main.go")
    35  	cmd.Dir = dir
    36  	cmd.Stdout = os.Stdout
    37  	cmd.Stderr = os.Stderr
    38  	require.NoError(t, cmd.Run(), "failed to compile go")
    39  }
    40  
    41  func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, apiFunc func(*model.Manifest) plugin.API) (func(), []string, []error) {
    42  	pluginDir, err := ioutil.TempDir("", "")
    43  	require.NoError(t, err)
    44  	webappPluginDir, err := ioutil.TempDir("", "")
    45  	require.NoError(t, err)
    46  
    47  	env, err := plugin.NewEnvironment(apiFunc, pluginDir, webappPluginDir, app.Log)
    48  	require.NoError(t, err)
    49  
    50  	app.SetPluginsEnvironment(env)
    51  	pluginIds := []string{}
    52  	activationErrors := []error{}
    53  	for _, code := range pluginCode {
    54  		pluginId := model.NewId()
    55  		backend := filepath.Join(pluginDir, pluginId, "backend.exe")
    56  		compileGo(t, code, backend)
    57  
    58  		ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(`{"id": "`+pluginId+`", "backend": {"executable": "backend.exe"}}`), 0600)
    59  		_, _, activationErr := env.Activate(pluginId)
    60  		pluginIds = append(pluginIds, pluginId)
    61  		activationErrors = append(activationErrors, activationErr)
    62  	}
    63  
    64  	return func() {
    65  		os.RemoveAll(pluginDir)
    66  		os.RemoveAll(webappPluginDir)
    67  	}, pluginIds, activationErrors
    68  }
    69  
    70  func TestHookMessageWillBePosted(t *testing.T) {
    71  	t.Run("rejected", func(t *testing.T) {
    72  		th := Setup(t).InitBasic()
    73  		defer th.TearDown()
    74  
    75  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
    76  			`
    77  			package main
    78  
    79  			import (
    80  				"github.com/mattermost/mattermost-server/plugin"
    81  				"github.com/mattermost/mattermost-server/model"
    82  			)
    83  
    84  			type MyPlugin struct {
    85  				plugin.MattermostPlugin
    86  			}
    87  
    88  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
    89  				return nil, "rejected"
    90  			}
    91  
    92  			func main() {
    93  				plugin.ClientMain(&MyPlugin{})
    94  			}
    95  			`,
    96  		}, th.App, th.App.NewPluginAPI)
    97  		defer tearDown()
    98  
    99  		post := &model.Post{
   100  			UserId:    th.BasicUser.Id,
   101  			ChannelId: th.BasicChannel.Id,
   102  			Message:   "message_",
   103  			CreateAt:  model.GetMillis() - 10000,
   104  		}
   105  		_, err := th.App.CreatePost(post, th.BasicChannel, false)
   106  		if assert.NotNil(t, err) {
   107  			assert.Equal(t, "Post rejected by plugin. rejected", err.Message)
   108  		}
   109  	})
   110  
   111  	t.Run("rejected, returned post ignored", func(t *testing.T) {
   112  		th := Setup(t).InitBasic()
   113  		defer th.TearDown()
   114  
   115  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   116  			`
   117  			package main
   118  
   119  			import (
   120  				"github.com/mattermost/mattermost-server/plugin"
   121  				"github.com/mattermost/mattermost-server/model"
   122  			)
   123  
   124  			type MyPlugin struct {
   125  				plugin.MattermostPlugin
   126  			}
   127  
   128  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   129  				post.Message = "ignored"
   130  				return post, "rejected"
   131  			}
   132  
   133  			func main() {
   134  				plugin.ClientMain(&MyPlugin{})
   135  			}
   136  			`,
   137  		}, th.App, th.App.NewPluginAPI)
   138  		defer tearDown()
   139  
   140  		post := &model.Post{
   141  			UserId:    th.BasicUser.Id,
   142  			ChannelId: th.BasicChannel.Id,
   143  			Message:   "message_",
   144  			CreateAt:  model.GetMillis() - 10000,
   145  		}
   146  		_, err := th.App.CreatePost(post, th.BasicChannel, false)
   147  		if assert.NotNil(t, err) {
   148  			assert.Equal(t, "Post rejected by plugin. rejected", err.Message)
   149  		}
   150  	})
   151  
   152  	t.Run("allowed", func(t *testing.T) {
   153  		th := Setup(t).InitBasic()
   154  		defer th.TearDown()
   155  
   156  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   157  			`
   158  			package main
   159  
   160  			import (
   161  				"github.com/mattermost/mattermost-server/plugin"
   162  				"github.com/mattermost/mattermost-server/model"
   163  			)
   164  
   165  			type MyPlugin struct {
   166  				plugin.MattermostPlugin
   167  			}
   168  
   169  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   170  				return nil, ""
   171  			}
   172  
   173  			func main() {
   174  				plugin.ClientMain(&MyPlugin{})
   175  			}
   176  			`,
   177  		}, th.App, th.App.NewPluginAPI)
   178  		defer tearDown()
   179  
   180  		post := &model.Post{
   181  			UserId:    th.BasicUser.Id,
   182  			ChannelId: th.BasicChannel.Id,
   183  			Message:   "message",
   184  			CreateAt:  model.GetMillis() - 10000,
   185  		}
   186  		post, err := th.App.CreatePost(post, th.BasicChannel, false)
   187  		if err != nil {
   188  			t.Fatal(err)
   189  		}
   190  		assert.Equal(t, "message", post.Message)
   191  		if result := <-th.App.Srv.Store.Post().GetSingle(post.Id); result.Err != nil {
   192  			t.Fatal(err)
   193  		} else {
   194  			assert.Equal(t, "message", result.Data.(*model.Post).Message)
   195  		}
   196  	})
   197  
   198  	t.Run("updated", func(t *testing.T) {
   199  		th := Setup(t).InitBasic()
   200  		defer th.TearDown()
   201  
   202  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   203  			`
   204  			package main
   205  
   206  			import (
   207  				"github.com/mattermost/mattermost-server/plugin"
   208  				"github.com/mattermost/mattermost-server/model"
   209  			)
   210  
   211  			type MyPlugin struct {
   212  				plugin.MattermostPlugin
   213  			}
   214  
   215  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   216  				post.Message = post.Message + "_fromplugin"
   217  				return post, ""
   218  			}
   219  
   220  			func main() {
   221  				plugin.ClientMain(&MyPlugin{})
   222  			}
   223  			`,
   224  		}, th.App, th.App.NewPluginAPI)
   225  		defer tearDown()
   226  
   227  		post := &model.Post{
   228  			UserId:    th.BasicUser.Id,
   229  			ChannelId: th.BasicChannel.Id,
   230  			Message:   "message",
   231  			CreateAt:  model.GetMillis() - 10000,
   232  		}
   233  		post, err := th.App.CreatePost(post, th.BasicChannel, false)
   234  		if err != nil {
   235  			t.Fatal(err)
   236  		}
   237  		assert.Equal(t, "message_fromplugin", post.Message)
   238  		if result := <-th.App.Srv.Store.Post().GetSingle(post.Id); result.Err != nil {
   239  			t.Fatal(err)
   240  		} else {
   241  			assert.Equal(t, "message_fromplugin", result.Data.(*model.Post).Message)
   242  		}
   243  	})
   244  
   245  	t.Run("multiple updated", func(t *testing.T) {
   246  		th := Setup(t).InitBasic()
   247  		defer th.TearDown()
   248  
   249  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   250  			`
   251  			package main
   252  
   253  			import (
   254  				"github.com/mattermost/mattermost-server/plugin"
   255  				"github.com/mattermost/mattermost-server/model"
   256  			)
   257  
   258  			type MyPlugin struct {
   259  				plugin.MattermostPlugin
   260  			}
   261  
   262  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   263  				
   264  				post.Message = "prefix_" + post.Message
   265  				return post, ""
   266  			}
   267  
   268  			func main() {
   269  				plugin.ClientMain(&MyPlugin{})
   270  			}
   271  			`,
   272  			`
   273  			package main
   274  
   275  			import (
   276  				"github.com/mattermost/mattermost-server/plugin"
   277  				"github.com/mattermost/mattermost-server/model"
   278  			)
   279  
   280  			type MyPlugin struct {
   281  				plugin.MattermostPlugin
   282  			}
   283  
   284  			func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) {
   285  				post.Message = post.Message + "_suffix"
   286  				return post, ""
   287  			}
   288  
   289  			func main() {
   290  				plugin.ClientMain(&MyPlugin{})
   291  			}
   292  			`,
   293  		}, th.App, th.App.NewPluginAPI)
   294  		defer tearDown()
   295  
   296  		post := &model.Post{
   297  			UserId:    th.BasicUser.Id,
   298  			ChannelId: th.BasicChannel.Id,
   299  			Message:   "message",
   300  			CreateAt:  model.GetMillis() - 10000,
   301  		}
   302  		post, err := th.App.CreatePost(post, th.BasicChannel, false)
   303  		if err != nil {
   304  			t.Fatal(err)
   305  		}
   306  		assert.Equal(t, "prefix_message_suffix", post.Message)
   307  	})
   308  }
   309  
   310  func TestHookMessageHasBeenPosted(t *testing.T) {
   311  	th := Setup(t).InitBasic()
   312  	defer th.TearDown()
   313  
   314  	var mockAPI plugintest.API
   315  	mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   316  	mockAPI.On("LogDebug", "message").Return(nil)
   317  
   318  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   319  		[]string{
   320  			`
   321  		package main
   322  
   323  		import (
   324  			"github.com/mattermost/mattermost-server/plugin"
   325  			"github.com/mattermost/mattermost-server/model"
   326  		)
   327  
   328  		type MyPlugin struct {
   329  			plugin.MattermostPlugin
   330  		}
   331  
   332  		func (p *MyPlugin) MessageHasBeenPosted(c *plugin.Context, post *model.Post) {
   333  			p.API.LogDebug(post.Message)
   334  		}
   335  
   336  		func main() {
   337  			plugin.ClientMain(&MyPlugin{})
   338  		}
   339  	`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   340  	defer tearDown()
   341  
   342  	post := &model.Post{
   343  		UserId:    th.BasicUser.Id,
   344  		ChannelId: th.BasicChannel.Id,
   345  		Message:   "message",
   346  		CreateAt:  model.GetMillis() - 10000,
   347  	}
   348  	_, err := th.App.CreatePost(post, th.BasicChannel, false)
   349  	if err != nil {
   350  		t.Fatal(err)
   351  	}
   352  }
   353  
   354  func TestHookMessageWillBeUpdated(t *testing.T) {
   355  	th := Setup(t).InitBasic()
   356  	defer th.TearDown()
   357  
   358  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   359  		[]string{
   360  			`
   361  		package main
   362  
   363  		import (
   364  			"github.com/mattermost/mattermost-server/plugin"
   365  			"github.com/mattermost/mattermost-server/model"
   366  		)
   367  
   368  		type MyPlugin struct {
   369  			plugin.MattermostPlugin
   370  		}
   371  
   372  		func (p *MyPlugin) MessageWillBeUpdated(c *plugin.Context, newPost, oldPost *model.Post) (*model.Post, string) {
   373  			newPost.Message = newPost.Message + "fromplugin"
   374  			return newPost, ""
   375  		}
   376  
   377  		func main() {
   378  			plugin.ClientMain(&MyPlugin{})
   379  		}
   380  	`}, th.App, th.App.NewPluginAPI)
   381  	defer tearDown()
   382  
   383  	post := &model.Post{
   384  		UserId:    th.BasicUser.Id,
   385  		ChannelId: th.BasicChannel.Id,
   386  		Message:   "message_",
   387  		CreateAt:  model.GetMillis() - 10000,
   388  	}
   389  	post, err := th.App.CreatePost(post, th.BasicChannel, false)
   390  	if err != nil {
   391  		t.Fatal(err)
   392  	}
   393  	assert.Equal(t, "message_", post.Message)
   394  	post.Message = post.Message + "edited_"
   395  	post, err = th.App.UpdatePost(post, true)
   396  	if err != nil {
   397  		t.Fatal(err)
   398  	}
   399  	assert.Equal(t, "message_edited_fromplugin", post.Message)
   400  }
   401  
   402  func TestHookMessageHasBeenUpdated(t *testing.T) {
   403  	th := Setup(t).InitBasic()
   404  	defer th.TearDown()
   405  
   406  	var mockAPI plugintest.API
   407  	mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   408  	mockAPI.On("LogDebug", "message_edited").Return(nil)
   409  	mockAPI.On("LogDebug", "message_").Return(nil)
   410  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   411  		[]string{
   412  			`
   413  		package main
   414  
   415  		import (
   416  			"github.com/mattermost/mattermost-server/plugin"
   417  			"github.com/mattermost/mattermost-server/model"
   418  		)
   419  
   420  		type MyPlugin struct {
   421  			plugin.MattermostPlugin
   422  		}
   423  
   424  		func (p *MyPlugin) MessageHasBeenUpdated(c *plugin.Context, newPost, oldPost *model.Post) {
   425  			p.API.LogDebug(newPost.Message)
   426  			p.API.LogDebug(oldPost.Message)
   427  		}
   428  
   429  		func main() {
   430  			plugin.ClientMain(&MyPlugin{})
   431  		}
   432  	`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   433  	defer tearDown()
   434  
   435  	post := &model.Post{
   436  		UserId:    th.BasicUser.Id,
   437  		ChannelId: th.BasicChannel.Id,
   438  		Message:   "message_",
   439  		CreateAt:  model.GetMillis() - 10000,
   440  	}
   441  	post, err := th.App.CreatePost(post, th.BasicChannel, false)
   442  	if err != nil {
   443  		t.Fatal(err)
   444  	}
   445  	assert.Equal(t, "message_", post.Message)
   446  	post.Message = post.Message + "edited"
   447  	_, err = th.App.UpdatePost(post, true)
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  }
   452  
   453  func TestHookFileWillBeUploaded(t *testing.T) {
   454  	t.Run("rejected", func(t *testing.T) {
   455  		th := Setup(t).InitBasic()
   456  		defer th.TearDown()
   457  
   458  		var mockAPI plugintest.API
   459  		mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   460  		mockAPI.On("LogDebug", "testhook.txt").Return(nil)
   461  		mockAPI.On("LogDebug", "inputfile").Return(nil)
   462  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   463  			`
   464  			package main
   465  
   466  			import (
   467  				"io"
   468  				"github.com/mattermost/mattermost-server/plugin"
   469  				"github.com/mattermost/mattermost-server/model"
   470  			)
   471  
   472  			type MyPlugin struct {
   473  				plugin.MattermostPlugin
   474  			}
   475  
   476  			func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
   477  				return nil, "rejected"
   478  			}
   479  
   480  			func main() {
   481  				plugin.ClientMain(&MyPlugin{})
   482  			}
   483  			`,
   484  		}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   485  		defer tearDown()
   486  
   487  		_, err := th.App.UploadFiles(
   488  			"noteam",
   489  			th.BasicChannel.Id,
   490  			th.BasicUser.Id,
   491  			[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
   492  			[]string{"testhook.txt"},
   493  			[]string{},
   494  			time.Now(),
   495  		)
   496  		if assert.NotNil(t, err) {
   497  			assert.Equal(t, "File rejected by plugin. rejected", err.Message)
   498  		}
   499  	})
   500  
   501  	t.Run("rejected, returned file ignored", func(t *testing.T) {
   502  		th := Setup(t).InitBasic()
   503  		defer th.TearDown()
   504  
   505  		var mockAPI plugintest.API
   506  		mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   507  		mockAPI.On("LogDebug", "testhook.txt").Return(nil)
   508  		mockAPI.On("LogDebug", "inputfile").Return(nil)
   509  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   510  			`
   511  			package main
   512  
   513  			import (
   514  				"io"
   515  				"github.com/mattermost/mattermost-server/plugin"
   516  				"github.com/mattermost/mattermost-server/model"
   517  			)
   518  
   519  			type MyPlugin struct {
   520  				plugin.MattermostPlugin
   521  			}
   522  
   523  			func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
   524  				output.Write([]byte("ignored"))
   525  				info.Name = "ignored"
   526  				return info, "rejected"
   527  			}
   528  
   529  			func main() {
   530  				plugin.ClientMain(&MyPlugin{})
   531  			}
   532  			`,
   533  		}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   534  		defer tearDown()
   535  
   536  		_, err := th.App.UploadFiles(
   537  			"noteam",
   538  			th.BasicChannel.Id,
   539  			th.BasicUser.Id,
   540  			[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
   541  			[]string{"testhook.txt"},
   542  			[]string{},
   543  			time.Now(),
   544  		)
   545  		if assert.NotNil(t, err) {
   546  			assert.Equal(t, "File rejected by plugin. rejected", err.Message)
   547  		}
   548  	})
   549  
   550  	t.Run("allowed", func(t *testing.T) {
   551  		th := Setup(t).InitBasic()
   552  		defer th.TearDown()
   553  
   554  		var mockAPI plugintest.API
   555  		mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   556  		mockAPI.On("LogDebug", "testhook.txt").Return(nil)
   557  		mockAPI.On("LogDebug", "inputfile").Return(nil)
   558  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   559  			`
   560  			package main
   561  
   562  			import (
   563  				"io"
   564  				"github.com/mattermost/mattermost-server/plugin"
   565  				"github.com/mattermost/mattermost-server/model"
   566  			)
   567  
   568  			type MyPlugin struct {
   569  				plugin.MattermostPlugin
   570  			}
   571  
   572  			func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
   573  				return nil, ""
   574  			}
   575  
   576  			func main() {
   577  				plugin.ClientMain(&MyPlugin{})
   578  			}
   579  			`,
   580  		}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   581  		defer tearDown()
   582  
   583  		response, err := th.App.UploadFiles(
   584  			"noteam",
   585  			th.BasicChannel.Id,
   586  			th.BasicUser.Id,
   587  			[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
   588  			[]string{"testhook.txt"},
   589  			[]string{},
   590  			time.Now(),
   591  		)
   592  		assert.Nil(t, err)
   593  		assert.NotNil(t, response)
   594  		assert.Equal(t, 1, len(response.FileInfos))
   595  		fileId := response.FileInfos[0].Id
   596  
   597  		fileInfo, err := th.App.GetFileInfo(fileId)
   598  		assert.Nil(t, err)
   599  		assert.NotNil(t, fileInfo)
   600  		assert.Equal(t, "testhook.txt", fileInfo.Name)
   601  
   602  		fileReader, err := th.App.FileReader(fileInfo.Path)
   603  		assert.Nil(t, err)
   604  		var resultBuf bytes.Buffer
   605  		io.Copy(&resultBuf, fileReader)
   606  		assert.Equal(t, "inputfile", resultBuf.String())
   607  	})
   608  
   609  	t.Run("updated", func(t *testing.T) {
   610  		th := Setup(t).InitBasic()
   611  		defer th.TearDown()
   612  
   613  		var mockAPI plugintest.API
   614  		mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   615  		mockAPI.On("LogDebug", "testhook.txt").Return(nil)
   616  		mockAPI.On("LogDebug", "inputfile").Return(nil)
   617  		tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{
   618  			`
   619  			package main
   620  
   621  			import (
   622  				"io"
   623  				"bytes"
   624  				"github.com/mattermost/mattermost-server/plugin"
   625  				"github.com/mattermost/mattermost-server/model"
   626  			)
   627  
   628  			type MyPlugin struct {
   629  				plugin.MattermostPlugin
   630  			}
   631  
   632  			func (p *MyPlugin) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
   633  				p.API.LogDebug(info.Name)
   634  				var buf bytes.Buffer
   635  				buf.ReadFrom(file)
   636  				p.API.LogDebug(buf.String())
   637  
   638  				outbuf := bytes.NewBufferString("changedtext")
   639  				io.Copy(output, outbuf)
   640  				info.Name = "modifiedinfo"
   641  				return info, ""
   642  			}
   643  
   644  			func main() {
   645  				plugin.ClientMain(&MyPlugin{})
   646  			}
   647  			`,
   648  		}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   649  		defer tearDown()
   650  
   651  		response, err := th.App.UploadFiles(
   652  			"noteam",
   653  			th.BasicChannel.Id,
   654  			th.BasicUser.Id,
   655  			[]io.ReadCloser{ioutil.NopCloser(bytes.NewBufferString("inputfile"))},
   656  			[]string{"testhook.txt"},
   657  			[]string{},
   658  			time.Now(),
   659  		)
   660  		assert.Nil(t, err)
   661  		assert.NotNil(t, response)
   662  		assert.Equal(t, 1, len(response.FileInfos))
   663  		fileId := response.FileInfos[0].Id
   664  
   665  		fileInfo, err := th.App.GetFileInfo(fileId)
   666  		assert.Nil(t, err)
   667  		assert.NotNil(t, fileInfo)
   668  		assert.Equal(t, "modifiedinfo", fileInfo.Name)
   669  
   670  		fileReader, err := th.App.FileReader(fileInfo.Path)
   671  		assert.Nil(t, err)
   672  		var resultBuf bytes.Buffer
   673  		io.Copy(&resultBuf, fileReader)
   674  		assert.Equal(t, "changedtext", resultBuf.String())
   675  	})
   676  }
   677  
   678  func TestUserWillLogIn_Blocked(t *testing.T) {
   679  	th := Setup(t).InitBasic()
   680  	defer th.TearDown()
   681  
   682  	err := th.App.UpdatePassword(th.BasicUser, "hunter2")
   683  
   684  	if err != nil {
   685  		t.Errorf("Error updating user password: %s", err)
   686  	}
   687  
   688  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   689  		[]string{
   690  			`
   691  		package main
   692  
   693  		import (
   694  			"github.com/mattermost/mattermost-server/plugin"
   695  			"github.com/mattermost/mattermost-server/model"
   696  		)
   697  
   698  		type MyPlugin struct {
   699  			plugin.MattermostPlugin
   700  		}
   701  
   702  		func (p *MyPlugin) UserWillLogIn(c *plugin.Context, user *model.User) string {
   703  			return "Blocked By Plugin"
   704  		}
   705  
   706  		func main() {
   707  			plugin.ClientMain(&MyPlugin{})
   708  		}
   709  	`}, th.App, th.App.NewPluginAPI)
   710  	defer tearDown()
   711  
   712  	r := &http.Request{}
   713  	w := httptest.NewRecorder()
   714  	_, err = th.App.DoLogin(w, r, th.BasicUser, "")
   715  
   716  	if !strings.HasPrefix(err.Id, "Login rejected by plugin") {
   717  		t.Errorf("Expected Login rejected by plugin, got %s", err.Id)
   718  	}
   719  }
   720  
   721  func TestUserWillLogInIn_Passed(t *testing.T) {
   722  	th := Setup(t).InitBasic()
   723  	defer th.TearDown()
   724  
   725  	err := th.App.UpdatePassword(th.BasicUser, "hunter2")
   726  
   727  	if err != nil {
   728  		t.Errorf("Error updating user password: %s", err)
   729  	}
   730  
   731  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   732  		[]string{
   733  			`
   734  		package main
   735  
   736  		import (
   737  			"github.com/mattermost/mattermost-server/plugin"
   738  			"github.com/mattermost/mattermost-server/model"
   739  		)
   740  
   741  		type MyPlugin struct {
   742  			plugin.MattermostPlugin
   743  		}
   744  
   745  		func (p *MyPlugin) UserWillLogIn(c *plugin.Context, user *model.User) string {
   746  			return ""
   747  		}
   748  
   749  		func main() {
   750  			plugin.ClientMain(&MyPlugin{})
   751  		}
   752  	`}, th.App, th.App.NewPluginAPI)
   753  	defer tearDown()
   754  
   755  	r := &http.Request{}
   756  	w := httptest.NewRecorder()
   757  	session, err := th.App.DoLogin(w, r, th.BasicUser, "")
   758  
   759  	if err != nil {
   760  		t.Errorf("Expected nil, got %s", err)
   761  	}
   762  
   763  	if session.UserId != th.BasicUser.Id {
   764  		t.Errorf("Expected %s, got %s", th.BasicUser.Id, session.UserId)
   765  	}
   766  }
   767  
   768  func TestUserHasLoggedIn(t *testing.T) {
   769  	th := Setup(t).InitBasic()
   770  	defer th.TearDown()
   771  
   772  	err := th.App.UpdatePassword(th.BasicUser, "hunter2")
   773  
   774  	if err != nil {
   775  		t.Errorf("Error updating user password: %s", err)
   776  	}
   777  
   778  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   779  		[]string{
   780  			`
   781  		package main
   782  
   783  		import (
   784  			"github.com/mattermost/mattermost-server/plugin"
   785  			"github.com/mattermost/mattermost-server/model"
   786  		)
   787  
   788  		type MyPlugin struct {
   789  			plugin.MattermostPlugin
   790  		}
   791  
   792  		func (p *MyPlugin) UserHasLoggedIn(c *plugin.Context, user *model.User) {
   793  			user.FirstName = "plugin-callback-success"
   794  			p.API.UpdateUser(user)
   795  		}
   796  
   797  		func main() {
   798  			plugin.ClientMain(&MyPlugin{})
   799  		}
   800  	`}, th.App, th.App.NewPluginAPI)
   801  	defer tearDown()
   802  
   803  	r := &http.Request{}
   804  	w := httptest.NewRecorder()
   805  	_, err = th.App.DoLogin(w, r, th.BasicUser, "")
   806  
   807  	if err != nil {
   808  		t.Errorf("Expected nil, got %s", err)
   809  	}
   810  
   811  	time.Sleep(2 * time.Second)
   812  
   813  	user, _ := th.App.GetUser(th.BasicUser.Id)
   814  
   815  	if user.FirstName != "plugin-callback-success" {
   816  		t.Errorf("Expected firstname overwrite, got default")
   817  	}
   818  }
   819  
   820  func TestUserHasBeenCreated(t *testing.T) {
   821  	th := Setup(t).InitBasic()
   822  	defer th.TearDown()
   823  
   824  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   825  		[]string{
   826  			`
   827  		package main
   828  
   829  		import (
   830  			"github.com/mattermost/mattermost-server/plugin"
   831  			"github.com/mattermost/mattermost-server/model"
   832  		)
   833  
   834  		type MyPlugin struct {
   835  			plugin.MattermostPlugin
   836  		}
   837  
   838  		func (p *MyPlugin) UserHasBeenCreated(c *plugin.Context, user *model.User) {
   839  			user.Nickname = "plugin-callback-success"
   840  			p.API.UpdateUser(user)
   841  		}
   842  
   843  		func main() {
   844  			plugin.ClientMain(&MyPlugin{})
   845  		}
   846  	`}, th.App, th.App.NewPluginAPI)
   847  	defer tearDown()
   848  
   849  	user := &model.User{
   850  		Email:       model.NewId() + "success+test@example.com",
   851  		Nickname:    "Darth Vader",
   852  		Username:    "vader" + model.NewId(),
   853  		Password:    "passwd1",
   854  		AuthService: "",
   855  	}
   856  	_, err := th.App.CreateUser(user)
   857  	require.Nil(t, err)
   858  
   859  	time.Sleep(1 * time.Second)
   860  
   861  	user, err = th.App.GetUser(user.Id)
   862  	require.Nil(t, err)
   863  
   864  	require.Equal(t, "plugin-callback-success", user.Nickname)
   865  }
   866  
   867  func TestErrorString(t *testing.T) {
   868  	th := Setup(t).InitBasic()
   869  	defer th.TearDown()
   870  
   871  	t.Run("errors.New", func(t *testing.T) {
   872  		tearDown, _, activationErrors := SetAppEnvironmentWithPlugins(t,
   873  			[]string{
   874  				`
   875  			package main
   876  
   877  			import (
   878  				"errors"
   879  
   880  				"github.com/mattermost/mattermost-server/plugin"
   881  			)
   882  
   883  			type MyPlugin struct {
   884  				plugin.MattermostPlugin
   885  			}
   886  
   887  			func (p *MyPlugin) OnActivate() error {
   888  				return errors.New("simulate failure")
   889  			}
   890  
   891  			func main() {
   892  				plugin.ClientMain(&MyPlugin{})
   893  			}
   894  		`}, th.App, th.App.NewPluginAPI)
   895  		defer tearDown()
   896  
   897  		require.Len(t, activationErrors, 1)
   898  		require.NotNil(t, activationErrors[0])
   899  		require.Contains(t, activationErrors[0].Error(), "simulate failure")
   900  	})
   901  
   902  	t.Run("AppError", func(t *testing.T) {
   903  		tearDown, _, activationErrors := SetAppEnvironmentWithPlugins(t,
   904  			[]string{
   905  				`
   906  			package main
   907  
   908  			import (
   909  				"github.com/mattermost/mattermost-server/plugin"
   910  				"github.com/mattermost/mattermost-server/model"
   911  			)
   912  
   913  			type MyPlugin struct {
   914  				plugin.MattermostPlugin
   915  			}
   916  
   917  			func (p *MyPlugin) OnActivate() error {
   918  				return model.NewAppError("where", "id", map[string]interface{}{"param": 1}, "details", 42)
   919  			}
   920  
   921  			func main() {
   922  				plugin.ClientMain(&MyPlugin{})
   923  			}
   924  		`}, th.App, th.App.NewPluginAPI)
   925  		defer tearDown()
   926  
   927  		require.Len(t, activationErrors, 1)
   928  		require.NotNil(t, activationErrors[0])
   929  
   930  		cause := errors.Cause(activationErrors[0])
   931  		require.IsType(t, &model.AppError{}, cause)
   932  
   933  		// params not expected, since not exported
   934  		expectedErr := model.NewAppError("where", "id", nil, "details", 42)
   935  		require.Equal(t, expectedErr, cause)
   936  	})
   937  }
   938  
   939  func TestHookContext(t *testing.T) {
   940  	th := Setup(t).InitBasic()
   941  	defer th.TearDown()
   942  
   943  	// We don't actually have a session, we are faking it so just set something arbitrarily
   944  	th.App.Session.Id = model.NewId()
   945  	th.App.RequestId = model.NewId()
   946  	th.App.IpAddress = model.NewId()
   947  	th.App.AcceptLanguage = model.NewId()
   948  	th.App.UserAgent = model.NewId()
   949  
   950  	var mockAPI plugintest.API
   951  	mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
   952  	mockAPI.On("LogDebug", th.App.Session.Id).Return(nil)
   953  	mockAPI.On("LogInfo", th.App.RequestId).Return(nil)
   954  	mockAPI.On("LogError", th.App.IpAddress).Return(nil)
   955  	mockAPI.On("LogWarn", th.App.AcceptLanguage).Return(nil)
   956  	mockAPI.On("DeleteTeam", th.App.UserAgent).Return(nil)
   957  
   958  	tearDown, _, _ := SetAppEnvironmentWithPlugins(t,
   959  		[]string{
   960  			`
   961  		package main
   962  
   963  		import (
   964  			"github.com/mattermost/mattermost-server/plugin"
   965  			"github.com/mattermost/mattermost-server/model"
   966  		)
   967  
   968  		type MyPlugin struct {
   969  			plugin.MattermostPlugin
   970  		}
   971  
   972  		func (p *MyPlugin) MessageHasBeenPosted(c *plugin.Context, post *model.Post) {
   973  			p.API.LogDebug(c.SessionId)
   974  			p.API.LogInfo(c.RequestId)
   975  			p.API.LogError(c.IpAddress)
   976  			p.API.LogWarn(c.AcceptLanguage)
   977  			p.API.DeleteTeam(c.UserAgent)
   978  		}
   979  
   980  		func main() {
   981  			plugin.ClientMain(&MyPlugin{})
   982  		}
   983  	`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
   984  	defer tearDown()
   985  
   986  	post := &model.Post{
   987  		UserId:    th.BasicUser.Id,
   988  		ChannelId: th.BasicChannel.Id,
   989  		Message:   "not this",
   990  		CreateAt:  model.GetMillis() - 10000,
   991  	}
   992  	_, err := th.App.CreatePost(post, th.BasicChannel, false)
   993  	if err != nil {
   994  		t.Fatal(err)
   995  	}
   996  }