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