From 04e6911a6b11f7e6319f2afef6b694eeab0baca6 Mon Sep 17 00:00:00 2001 From: Eli Yukelzon Date: Tue, 21 Jan 2020 10:41:28 +0200 Subject: [PATCH] =?UTF-8?q?GH-10438=20-=20Rewrite=20existing=20plugin=20AP?= =?UTF-8?q?I=20tests=20to=20exclusively=20te=E2=80=A6=20(#13302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .circleci/config.yml | 4 + app/plugin_api_test.go | 646 +++--------------- app/plugin_api_tests/basic_config.go | 47 ++ .../main.go | 48 ++ .../main.go | 51 ++ app/plugin_api_tests/test_bots_plugin/main.go | 130 ++++ .../test_call_log_api_plugin/main.go} | 5 +- .../test_get_bundle_path_plugin/main.go | 40 ++ .../main.go | 45 ++ .../test_get_direct_channel_plugin/main.go | 53 ++ .../test_get_profile_image_plugin/main.go | 45 ++ .../test_member_channels_plugin/main.go | 40 ++ .../test_members_plugin/main.go | 38 ++ .../test_search_channels_plugin/main.go | 47 ++ .../test_search_posts_in_team_plugin/main.go | 73 ++ .../test_search_teams_plugin/main.go | 64 ++ .../test_send_mail_plugin/main.go | 68 ++ .../test_set_profile_image_plugin/main.go | 71 ++ .../test_update_user_active_plugin/main.go | 77 +++ .../test_update_user_status_plugin/main.go | 58 ++ 20 files changed, 1099 insertions(+), 551 deletions(-) create mode 100644 app/plugin_api_tests/basic_config.go create mode 100644 app/plugin_api_tests/manual.test_load_configuration_defaults_plugin/main.go create mode 100644 app/plugin_api_tests/manual.test_load_configuration_plugin/main.go create mode 100644 app/plugin_api_tests/test_bots_plugin/main.go rename app/{plugin_api_test/plugin_using_log_api.go => plugin_api_tests/test_call_log_api_plugin/main.go} (79%) create mode 100644 app/plugin_api_tests/test_get_bundle_path_plugin/main.go create mode 100644 app/plugin_api_tests/test_get_channels_for_team_for_user_plugin/main.go create mode 100644 app/plugin_api_tests/test_get_direct_channel_plugin/main.go create mode 100644 app/plugin_api_tests/test_get_profile_image_plugin/main.go create mode 100644 app/plugin_api_tests/test_member_channels_plugin/main.go create mode 100644 app/plugin_api_tests/test_members_plugin/main.go create mode 100644 app/plugin_api_tests/test_search_channels_plugin/main.go create mode 100644 app/plugin_api_tests/test_search_posts_in_team_plugin/main.go create mode 100644 app/plugin_api_tests/test_search_teams_plugin/main.go create mode 100644 app/plugin_api_tests/test_send_mail_plugin/main.go create mode 100644 app/plugin_api_tests/test_set_profile_image_plugin/main.go create mode 100644 app/plugin_api_tests/test_update_user_active_plugin/main.go create mode 100644 app/plugin_api_tests/test_update_user_status_plugin/main.go diff --git a/.circleci/config.yml b/.circleci/config.yml index d71777ee09..0a82ef7f2b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,12 +10,14 @@ executors: image: "ubuntu-1604:201903-01" environment: COMPOSE_PROJECT_NAME: "circleci" + resource_class: "large" jobs: setup: working_directory: ~/go/mattermost/mattermost-server docker: - image: mattermost/mattermost-build-webapp:oct-2-2018 + resource_class: xlarge steps: - checkout - run: | @@ -57,6 +59,7 @@ jobs: build: docker: - image: mattermost/mattermost-build-server:oct-18-2019 + resource_class: xlarge working_directory: ~/go/mattermost steps: - attach_workspace: @@ -290,6 +293,7 @@ jobs: working_directory: ~/ docker: - image: circleci/buildpack-deps:stretch + resource_class: xlarge steps: - attach_workspace: at: . diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index 93414e5c21..e19e0fd3db 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -6,6 +6,7 @@ package app import ( "bytes" "encoding/json" + "errors" "fmt" "image" "image/color" @@ -14,6 +15,7 @@ import ( "net/http" "net/http/httptest" "os" + "path" "path/filepath" "strings" "testing" @@ -21,13 +23,48 @@ import ( "github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/plugin" - "github.com/mattermost/mattermost-server/v5/services/mailservice" "github.com/mattermost/mattermost-server/v5/utils" "github.com/mattermost/mattermost-server/v5/utils/fileutils" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func getDefaultPluginSettingsSchema() string { + ret, _ := json.Marshal(model.PluginSettingsSchema{ + Settings: []*model.PluginSetting{ + {Key: "BasicChannelName", Type: "text"}, + {Key: "BasicChannelId", Type: "text"}, + {Key: "BasicTeamDisplayName", Type: "text"}, + {Key: "BasicTeamName", Type: "text"}, + {Key: "BasicTeamId", Type: "text"}, + {Key: "BasicUserEmail", Type: "text"}, + {Key: "BasicUserId", Type: "text"}, + {Key: "BasicUser2Email", Type: "text"}, + {Key: "BasicUser2Id", Type: "text"}, + {Key: "BasicPostMessage", Type: "text"}, + }, + }) + return string(ret) +} + +func setDefaultPluginConfig(th *TestHelper, pluginId string) { + th.App.UpdateConfig(func(cfg *model.Config) { + cfg.PluginSettings.Plugins[pluginId] = map[string]interface{}{ + "BasicChannelName": th.BasicChannel.Name, + "BasicChannelId": th.BasicChannel.Id, + "BasicTeamName": th.BasicTeam.Name, + "BasicTeamId": th.BasicTeam.Id, + "BasicTeamDisplayName": th.BasicTeam.DisplayName, + "BasicUserEmail": th.BasicUser.Email, + "BasicUserId": th.BasicUser.Id, + "BasicUser2Email": th.BasicUser2.Email, + "BasicUser2Id": th.BasicUser2.Id, + "BasicPostMessage": th.BasicPost.Message, + } + }) +} + func setupMultiPluginApiTest(t *testing.T, pluginCodes []string, pluginManifests []string, pluginIds []string, app *App) string { pluginDir, err := ioutil.TempDir("", "") require.NoError(t, err) @@ -287,25 +324,6 @@ func TestPluginAPIGetUsersInTeam(t *testing.T) { } } -func TestPluginAPIUpdateUserStatus(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - statuses := []string{model.STATUS_ONLINE, model.STATUS_AWAY, model.STATUS_DND, model.STATUS_OFFLINE} - - for _, s := range statuses { - status, err := api.UpdateUserStatus(th.BasicUser.Id, s) - require.Nil(t, err) - require.NotNil(t, status) - assert.Equal(t, s, status.Status) - } - - status, err := api.UpdateUserStatus(th.BasicUser.Id, "notrealstatus") - assert.NotNil(t, err) - assert.Nil(t, status) -} - func TestPluginAPIGetFile(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() @@ -417,45 +435,12 @@ func TestPluginAPILoadPluginConfiguration(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson }) - setupPluginApiTest(t, - ` - package main - import ( - "github.com/mattermost/mattermost-server/v5/plugin" - "github.com/mattermost/mattermost-server/v5/model" - "fmt" - ) + testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests") + require.True(t, found, "Cannot find tests folder") + fullPath := path.Join(testFolder, "manual.test_load_configuration_plugin", "main.go") - type configuration struct { - MyStringSetting string - MyIntSetting int - MyBoolSetting bool - } - - type MyPlugin struct { - plugin.MattermostPlugin - - configuration configuration - } - - func (p *MyPlugin) OnConfigurationChange() error { - if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { - return err - } - - return nil - } - - func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { - return nil, fmt.Sprintf("%v%v%v", p.configuration.MyStringSetting, p.configuration.MyIntSetting, p.configuration.MyBoolSetting) - } - - func main() { - plugin.ClientMain(&MyPlugin{}) - } - `, - `{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": { + err = pluginAPIHookTest(t, th, fullPath, "testloadpluginconfig", `{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": { "settings": [ { "key": "MyStringSetting", @@ -470,11 +455,9 @@ func TestPluginAPILoadPluginConfiguration(t *testing.T) { "type": "bool" } ] - }}`, "testloadpluginconfig", th.App) - hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testloadpluginconfig") - assert.NoError(t, err) - _, ret := hooks.MessageWillBePosted(nil, nil) - assert.Equal(t, "str32true", ret) + }}`) + require.NoError(t, err) + } func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) { @@ -488,45 +471,12 @@ func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson }) - setupPluginApiTest(t, - ` - package main - import ( - "github.com/mattermost/mattermost-server/v5/plugin" - "github.com/mattermost/mattermost-server/v5/model" - "fmt" - ) + testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests") + require.True(t, found, "Cannot find tests folder") + fullPath := path.Join(testFolder, "manual.test_load_configuration_defaults_plugin", "main.go") - type configuration struct { - MyStringSetting string - MyIntSetting int - MyBoolSetting bool - } - - type MyPlugin struct { - plugin.MattermostPlugin - - configuration configuration - } - - func (p *MyPlugin) OnConfigurationChange() error { - if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { - return err - } - - return nil - } - - func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { - return nil, fmt.Sprintf("%v%v%v", p.configuration.MyStringSetting, p.configuration.MyIntSetting, p.configuration.MyBoolSetting) - } - - func main() { - plugin.ClientMain(&MyPlugin{}) - } - `, - `{"id": "testloadpluginconfig", "backend": {"executable": "backend.exe"}, "settings_schema": { + err = pluginAPIHookTest(t, th, fullPath, "testloadpluginconfig", `{ "settings": [ { "key": "MyStringSetting", @@ -544,98 +494,10 @@ func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) { "default": true } ] - }}`, "testloadpluginconfig", th.App) - hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testloadpluginconfig") - assert.NoError(t, err) - _, ret := hooks.MessageWillBePosted(nil, nil) - assert.Equal(t, "override35true", ret) -} + }`) -func TestPluginAPIGetBundlePath(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() + require.NoError(t, err) - setupPluginApiTest(t, - ` - package main - - import ( - "github.com/mattermost/mattermost-server/v5/plugin" - "github.com/mattermost/mattermost-server/v5/model" - ) - - type MyPlugin struct { - plugin.MattermostPlugin - } - - func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { - bundlePath, err := p.API.GetBundlePath() - if err != nil { - return nil, err.Error() + "failed get bundle path" - } - - return nil, bundlePath - } - - func main() { - plugin.ClientMain(&MyPlugin{}) - } - `, `{"id": "testplugin", "backend": {"executable": "backend.exe"}}`, "testplugin", th.App) - - hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testplugin") - require.Nil(t, err) - require.NotNil(t, hooks) - bundlePath, err := filepath.Abs(filepath.Join(*th.App.Config().PluginSettings.Directory, "testplugin")) - require.Nil(t, err) - - _, errString := hooks.MessageWillBePosted(nil, nil) - assert.Equal(t, bundlePath, errString) -} - -func TestPluginAPIGetProfileImage(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - // check existing user first - data, err := api.GetProfileImage(th.BasicUser.Id) - require.Nil(t, err) - require.NotEmpty(t, data) - - // then unknown user - data, err = api.GetProfileImage(model.NewId()) - require.NotNil(t, err) - require.Nil(t, data) -} - -func TestPluginAPISetProfileImage(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - // Create an 128 x 128 image - img := image.NewRGBA(image.Rect(0, 0, 128, 128)) - // Draw a red dot at (2, 3) - img.Set(2, 3, color.RGBA{255, 0, 0, 255}) - buf := new(bytes.Buffer) - err := png.Encode(buf, img) - require.Nil(t, err) - dataBytes := buf.Bytes() - - // Set the user profile image - err = api.SetProfileImage(th.BasicUser.Id, dataBytes) - require.Nil(t, err) - - // Get the user profile image to check - imageProfile, err := api.GetProfileImage(th.BasicUser.Id) - require.Nil(t, err) - require.NotEmpty(t, imageProfile) - - colorful := color.NRGBA{255, 0, 0, 255} - byteReader := bytes.NewReader(imageProfile) - img2, _, err2 := image.Decode(byteReader) - require.Nil(t, err2) - require.Equal(t, img2.At(2, 3), colorful) } func TestPluginAPIGetPlugins(t *testing.T) { @@ -916,88 +778,6 @@ func TestPluginAPISetTeamIcon(t *testing.T) { require.Equal(t, img2.At(2, 3), colorful) } -func TestPluginAPISearchChannels(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - t.Run("all fine", func(t *testing.T) { - channels, err := api.SearchChannels(th.BasicTeam.Id, th.BasicChannel.Name) - assert.Nil(t, err) - assert.Len(t, channels, 1) - }) - - t.Run("invalid team id", func(t *testing.T) { - channels, err := api.SearchChannels("invalidid", th.BasicChannel.Name) - assert.Nil(t, err) - assert.Empty(t, channels) - }) -} - -func TestPluginAPISearchPostsInTeam(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - testCases := []struct { - description string - teamId string - params []*model.SearchParams - expectedPostsLen int - }{ - { - "nil params", - th.BasicTeam.Id, - nil, - 0, - }, - { - "empty params", - th.BasicTeam.Id, - []*model.SearchParams{}, - 0, - }, - { - "doesn't match any posts", - th.BasicTeam.Id, - model.ParseSearchParams("bad message", 0), - 0, - }, - { - "matched posts", - th.BasicTeam.Id, - model.ParseSearchParams(th.BasicPost.Message, 0), - 1, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.description, func(t *testing.T) { - posts, err := api.SearchPostsInTeam(testCase.teamId, testCase.params) - assert.Nil(t, err) - assert.Equal(t, testCase.expectedPostsLen, len(posts)) - }) - } -} - -func TestPluginAPIGetChannelsForTeamForUser(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - t.Run("all fine", func(t *testing.T) { - channels, err := api.GetChannelsForTeamForUser(th.BasicTeam.Id, th.BasicUser.Id, false) - assert.Nil(t, err) - assert.Len(t, channels, 3) - }) - - t.Run("invalid team id", func(t *testing.T) { - channels, err := api.GetChannelsForTeamForUser("invalidid", th.BasicUser.Id, false) - assert.NotNil(t, err) - assert.Empty(t, channels) - }) -} - func TestPluginAPIRemoveTeamIcon(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() @@ -1021,278 +801,57 @@ func TestPluginAPIRemoveTeamIcon(t *testing.T) { require.Nil(t, err) } -func TestPluginAPIUpdateUserActive(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - err := api.UpdateUserActive(th.BasicUser.Id, true) - require.Nil(t, err) - user, err := api.GetUser(th.BasicUser.Id) - require.Nil(t, err) - require.Equal(t, int64(0), user.DeleteAt) - - err = api.UpdateUserActive(th.BasicUser.Id, false) - require.Nil(t, err) - user, err = api.GetUser(th.BasicUser.Id) - require.Nil(t, err) - require.NotNil(t, user) - require.NotEqual(t, int64(0), user.DeleteAt) - - err = api.UpdateUserActive(th.BasicUser.Id, true) - require.Nil(t, err) - err = api.UpdateUserActive(th.BasicUser.Id, true) - require.Nil(t, err) - user, err = api.GetUser(th.BasicUser.Id) - require.Nil(t, err) - require.Equal(t, int64(0), user.DeleteAt) -} - -func TestPluginAPIGetDirectChannel(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - dm1, err := api.GetDirectChannel(th.BasicUser.Id, th.BasicUser2.Id) - require.Nil(t, err) - require.NotEmpty(t, dm1) - - dm2, err := api.GetDirectChannel(th.BasicUser.Id, th.BasicUser.Id) - require.Nil(t, err) - require.NotEmpty(t, dm2) - - dm3, err := api.GetDirectChannel(th.BasicUser.Id, model.NewId()) - require.NotNil(t, err) - require.Empty(t, dm3) -} - -func TestPluginAPISendMail(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - to := th.BasicUser.Email - subject := "testing plugin api sending email" - body := "this is a test." - - err := api.SendMail(to, subject, body) - require.Nil(t, err) - - // Check if we received the email - var resultsMailbox mailservice.JSONMessageHeaderInbucket - errMail := mailservice.RetryInbucket(5, func() error { - var err error - resultsMailbox, err = mailservice.GetMailBox(to) +func pluginAPIHookTest(t *testing.T, th *TestHelper, fileName string, id string, settingsSchema string) error { + data, err := ioutil.ReadFile(fileName) + if err != nil { return err - }) - require.Nil(t, errMail) - require.NotZero(t, len(resultsMailbox)) - require.True(t, strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], to)) - - resultsEmail, err1 := mailservice.GetMessageFromMailbox(to, resultsMailbox[len(resultsMailbox)-1].ID) - require.Nil(t, err1) - require.Equal(t, resultsEmail.Subject, subject) - require.Equal(t, resultsEmail.Body.Text, body) - + } + code := string(data) + schema := `{"settings": [ ] }` + if settingsSchema != "" { + schema = settingsSchema + } + setupPluginApiTest(t, code, + fmt.Sprintf(`{"id": "%v", "backend": {"executable": "backend.exe"}, "settings_schema": %v}`, id, schema), + id, th.App) + hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin(id) + require.NoError(t, err) + require.NotNil(t, hooks) + _, ret := hooks.MessageWillBePosted(nil, nil) + if ret != "OK" { + return errors.New(ret) + } + return nil } -func TestPluginAPI_SearchTeams(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() +// This is a meta-test function. It does the following: +// 1. Scans "tests/plugin_tests" folder +// 2. For each folder - compiles the main.go inside and executes it, validating it's result +// 3. If folder starts with "manual." it is skipped ("manual." tests executed in other part of this file) +// 4. Before compiling the main.go file is passed through templating and the following values are available in the template: BasicUser, BasicUser2, BasicChannel, BasicTeam, BasicPost +// 5. Succesfully running test should return nil, "OK". Any other returned string is considered and error - api := th.SetupPluginAPI() - - t.Run("all fine", func(t *testing.T) { - teams, err := api.SearchTeams(th.BasicTeam.Name) - assert.Nil(t, err) - assert.Len(t, teams, 1) - - teams, err = api.SearchTeams(th.BasicTeam.DisplayName) - assert.Nil(t, err) - assert.Len(t, teams, 1) - - teams, err = api.SearchTeams(th.BasicTeam.Name[:3]) - assert.Nil(t, err) - assert.Len(t, teams, 1) - }) - - t.Run("invalid team name", func(t *testing.T) { - teams, err := api.SearchTeams("not found") - assert.Nil(t, err) - assert.Empty(t, teams) - }) -} - -func TestPluginBots(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - - setupPluginApiTest(t, - ` - package main - - import ( - "github.com/mattermost/mattermost-server/v5/plugin" - "github.com/mattermost/mattermost-server/v5/model" - ) - - type MyPlugin struct { - plugin.MattermostPlugin +func TestBasicAPIPlugins(t *testing.T) { + defaultSchema := getDefaultPluginSettingsSchema() + testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests") + require.True(t, found, "Cannot read find app folder") + dirs, err := ioutil.ReadDir(testFolder) + require.NoError(t, err, "Cannot read test folder %v", testFolder) + for _, dir := range dirs { + d := dir.Name() + if dir.IsDir() && !strings.HasPrefix(d, "manual.") { + t.Run(d, func(t *testing.T) { + mainPath := path.Join(testFolder, d, "main.go") + _, err := os.Stat(mainPath) + require.NoError(t, err, "Cannot find plugin main file at %v", mainPath) + th := Setup(t).InitBasic() + defer th.TearDown() + setDefaultPluginConfig(th, dir.Name()) + err = pluginAPIHookTest(t, th, mainPath, dir.Name(), defaultSchema) + require.NoError(t, err) + }) } - - func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { - createdBot, err := p.API.CreateBot(&model.Bot{ - Username: "bot", - Description: "a plugin bot", - }) - if err != nil { - return nil, err.Error() + "failed to create bot" - } - - fetchedBot, err := p.API.GetBot(createdBot.UserId, false) - if err != nil { - return nil, err.Error() + "failed to get bot" - } - if fetchedBot.Description != "a plugin bot" { - return nil, "GetBot did not return the expected bot Description" - } - if fetchedBot.OwnerId != "testpluginbots" { - return nil, "GetBot did not return the expected bot OwnerId" - } - - updatedDescription := createdBot.Description + ", updated" - patchedBot, err := p.API.PatchBot(createdBot.UserId, &model.BotPatch{ - Description: &updatedDescription, - }) - if err != nil { - return nil, err.Error() + "failed to patch bot" - } - - fetchedBot, err = p.API.GetBot(patchedBot.UserId, false) - if err != nil { - return nil, err.Error() + "failed to get bot" - } - - if fetchedBot.UserId != patchedBot.UserId { - return nil, "GetBot did not return the expected bot" - } - if fetchedBot.Description != "a plugin bot, updated" { - return nil, "GetBot did not return the updated bot Description" - } - - fetchedBots, err := p.API.GetBots(&model.BotGetOptions{ - Page: 0, - PerPage: 1, - OwnerId: "", - IncludeDeleted: false, - }) - if err != nil { - return nil, err.Error() + "failed to get bots" - } - - if len(fetchedBots) != 1 { - return nil, "GetBots did not return a single bot" - } - if fetchedBot.UserId != fetchedBots[0].UserId { - return nil, "GetBots did not return the expected bot" - } - - _, err = p.API.UpdateBotActive(fetchedBot.UserId, false) - if err != nil { - return nil, err.Error() + "failed to disable bot" - } - - fetchedBot, err = p.API.GetBot(patchedBot.UserId, false) - if err == nil { - return nil, "expected not to find disabled bot" - } - - _, err = p.API.UpdateBotActive(fetchedBot.UserId, true) - if err != nil { - return nil, err.Error() + "failed to disable bot" - } - - fetchedBot, err = p.API.GetBot(patchedBot.UserId, false) - if err != nil { - return nil, err.Error() + "failed to get bot after enabling" - } - if fetchedBot.UserId != patchedBot.UserId { - return nil, "GetBot did not return the expected bot after enabling" - } - - err = p.API.PermanentDeleteBot(patchedBot.UserId) - if err != nil { - return nil, err.Error() + "failed to delete bot" - } - - _, err = p.API.GetBot(patchedBot.UserId, false) - if err == nil { - return nil, err.Error() + "found bot after permanently deleting" - } - - createdBotWithOverriddenCreator, err := p.API.CreateBot(&model.Bot{ - Username: "bot", - Description: "a plugin bot", - OwnerId: "abc123", - }) - if err != nil { - return nil, err.Error() + "failed to create bot with overridden creator" - } - - fetchedBot, err = p.API.GetBot(createdBotWithOverriddenCreator.UserId, false) - if err != nil { - return nil, err.Error() + "failed to get bot" - } - if fetchedBot.Description != "a plugin bot" { - return nil, "GetBot did not return the expected bot Description" - } - if fetchedBot.OwnerId != "abc123" { - return nil, "GetBot did not return the expected bot OwnerId" - } - - return nil, "" - } - - func main() { - plugin.ClientMain(&MyPlugin{}) - } - `, - `{"id": "testpluginbots", "backend": {"executable": "backend.exe"}}`, - "testpluginbots", - th.App, - ) - - hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testpluginbots") - assert.NoError(t, err) - _, errString := hooks.MessageWillBePosted(nil, nil) - assert.Empty(t, errString) -} - -func TestPluginAPI_GetTeamMembersForUser(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - userId := th.BasicUser.Id - teamMembers, err := api.GetTeamMembersForUser(userId, 0, 10) - assert.Nil(t, err) - assert.Equal(t, len(teamMembers), 1) - assert.Equal(t, teamMembers[0].TeamId, th.BasicTeam.Id) - assert.Equal(t, teamMembers[0].UserId, th.BasicUser.Id) -} - -func TestPluginAPI_GetChannelMembersForUser(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - api := th.SetupPluginAPI() - - userId := th.BasicUser.Id - teamId := th.BasicTeam.Id - channelMembers, err := api.GetChannelMembersForUser(teamId, userId, 0, 10) - - assert.Nil(t, err) - assert.Equal(t, len(channelMembers), 3) - assert.Equal(t, channelMembers[0].UserId, th.BasicUser.Id) + } } func TestPluginAPIKVCompareAndSet(t *testing.T) { @@ -1573,17 +1132,6 @@ func TestPluginAPIGetUnsanitizedConfig(t *testing.T) { } } -func TestPluginCallLogAPI(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - pluginID := "com.mattermost.sample" - path, _ := fileutils.FindDir("mattermost-server/app/plugin_api_test") - pluginCode, err := ioutil.ReadFile(filepath.Join(path, "plugin_using_log_api.go")) - assert.NoError(t, err) - setupPluginApiTest(t, string(pluginCode), - `{"id": "com.mattermost.sample", "server": {"executable": "backend.exe"}, "settings_schema": {"settings": []}}`, pluginID, th.App) -} - func TestPluginAddUserToChannel(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/app/plugin_api_tests/basic_config.go b/app/plugin_api_tests/basic_config.go new file mode 100644 index 0000000000..a1addbd8cb --- /dev/null +++ b/app/plugin_api_tests/basic_config.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package plugin_api_tests + +import "reflect" + +type BasicConfig struct { + BasicChannelId string + BasicChannelName string + BasicPostId string + BasicPostMessage string + BasicTeamDisplayName string + BasicTeamId string + BasicTeamName string + BasicUser2Email string + BasicUser2Id string + BasicUserEmail string + BasicUserId string +} + +func IsEmpty(object interface{}) bool { + + // get nil case out of the way + if object == nil { + return true + } + + objValue := reflect.ValueOf(object) + + switch objValue.Kind() { + // collection types are empty when they have no element + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: + return objValue.Len() == 0 + // pointers are empty if nil or if the value they point to is empty + case reflect.Ptr: + if objValue.IsNil() { + return true + } + deref := objValue.Elem().Interface() + return IsEmpty(deref) + // for all other types, compare against the zero value + default: + zero := reflect.Zero(objValue.Type()) + return reflect.DeepEqual(object, zero.Interface()) + } +} diff --git a/app/plugin_api_tests/manual.test_load_configuration_defaults_plugin/main.go b/app/plugin_api_tests/manual.test_load_configuration_defaults_plugin/main.go new file mode 100644 index 0000000000..a1b8b4fca3 --- /dev/null +++ b/app/plugin_api_tests/manual.test_load_configuration_defaults_plugin/main.go @@ -0,0 +1,48 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type configuration struct { + plugin_api_tests.BasicConfig + MyStringSetting string + MyIntSetting int + MyBoolSetting bool +} + +type MyPlugin struct { + plugin.MattermostPlugin + + configuration configuration +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + if p.configuration.MyStringSetting != "override" { + return nil, "MyStringSetting has invalid value" + } + if p.configuration.MyIntSetting != 35 { + return nil, "MyIntSetting has invalid value" + } + if !p.configuration.MyBoolSetting { + return nil, "MyBoolSetting has invalid value" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/manual.test_load_configuration_plugin/main.go b/app/plugin_api_tests/manual.test_load_configuration_plugin/main.go new file mode 100644 index 0000000000..35f930d34d --- /dev/null +++ b/app/plugin_api_tests/manual.test_load_configuration_plugin/main.go @@ -0,0 +1,51 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type configuration struct { + plugin_api_tests.BasicConfig + + MyStringSetting string + MyIntSetting int + MyBoolSetting bool +} + +type MyPlugin struct { + plugin.MattermostPlugin + + configuration configuration +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + if p.configuration.MyStringSetting != "str" { + return nil, "MyStringSetting has invalid value" + } + if p.configuration.MyIntSetting != 32 { + return nil, fmt.Sprintf("MyIntSetting has invalid value %v != %v", p.configuration.MyIntSetting, 32) + } + if !p.configuration.MyBoolSetting { + return nil, "MyBoolSetting has invalid value" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_bots_plugin/main.go b/app/plugin_api_tests/test_bots_plugin/main.go new file mode 100644 index 0000000000..d85767fff5 --- /dev/null +++ b/app/plugin_api_tests/test_bots_plugin/main.go @@ -0,0 +1,130 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + createdBot, err := p.API.CreateBot(&model.Bot{ + Username: "bot", + Description: "a plugin bot", + }) + + if err != nil { + return nil, err.Error() + "failed to create bot" + } + + fetchedBot, err := p.API.GetBot(createdBot.UserId, false) + if err != nil { + return nil, err.Error() + "failed to get bot" + } + if fetchedBot.Description != "a plugin bot" { + return nil, "GetBot did not return the expected bot Description" + } + if fetchedBot.OwnerId != "test_bots_plugin" { + return nil, "GetBot did not return the expected bot OwnerId" + } + + updatedDescription := createdBot.Description + ", updated" + patchedBot, err := p.API.PatchBot(createdBot.UserId, &model.BotPatch{ + Description: &updatedDescription, + }) + if err != nil { + return nil, err.Error() + "failed to patch bot" + } + + fetchedBot, err = p.API.GetBot(patchedBot.UserId, false) + if err != nil { + return nil, err.Error() + "failed to get bot" + } + + if fetchedBot.UserId != patchedBot.UserId { + return nil, "GetBot did not return the expected bot" + } + if fetchedBot.Description != "a plugin bot, updated" { + return nil, "GetBot did not return the updated bot Description" + } + + fetchedBots, err := p.API.GetBots(&model.BotGetOptions{ + Page: 0, + PerPage: 1, + OwnerId: "", + IncludeDeleted: false, + }) + if err != nil { + return nil, err.Error() + "failed to get bots" + } + + if len(fetchedBots) != 1 { + return nil, "GetBots did not return a single bot" + } + + if fetchedBot.UserId != fetchedBots[0].UserId { + return nil, "GetBots did not return the expected bot" + } + if _, err = p.API.UpdateBotActive(fetchedBot.UserId, false); err != nil { + return nil, err.Error() + "failed to disable bot" + } + + // TODO: investigate why the following code panics + /* + if fetchedBot, err = p.API.GetBot(patchedBot.UserId, false); err == nil { + return nil, "expected not to find disabled bot" + } + if _, err = p.API.UpdateBotActive(fetchedBot.UserId, true); err != nil { + return nil, err.Error() + "failed to disable bot" + } + if fetchedBot, err = p.API.GetBot(patchedBot.UserId, false); err != nil { + return nil, err.Error() + "failed to get bot after enabling" + } + if fetchedBot.UserId != patchedBot.UserId { + return nil, "GetBot did not return the expected bot after enabling" + } + if err = p.API.PermanentDeleteBot(patchedBot.UserId); err != nil { + return nil, err.Error() + "failed to delete bot" + } + + if _, err = p.API.GetBot(patchedBot.UserId, false); err == nil { + return nil, err.Error() + "found bot after permanently deleting" + } + createdBotWithOverriddenCreator, err := p.API.CreateBot(&model.Bot{ + Username: "bot", + Description: "a plugin bot", + OwnerId: "abc123", + }) + if err != nil { + return nil, err.Error() + "failed to create bot with overridden creator" + } + if fetchedBot, err = p.API.GetBot(createdBotWithOverriddenCreator.UserId, false); err != nil { + return nil, err.Error() + "failed to get bot" + } + if fetchedBot.Description != "a plugin bot" { + return nil, "GetBot did not return the expected bot Description" + } + if fetchedBot.OwnerId != "abc123" { + return nil, "GetBot did not return the expected bot OwnerId" + } + */ + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_test/plugin_using_log_api.go b/app/plugin_api_tests/test_call_log_api_plugin/main.go similarity index 79% rename from app/plugin_api_test/plugin_using_log_api.go rename to app/plugin_api_tests/test_call_log_api_plugin/main.go index f3d25b38cf..23880d4980 100644 --- a/app/plugin_api_test/plugin_using_log_api.go +++ b/app/plugin_api_tests/test_call_log_api_plugin/main.go @@ -4,6 +4,7 @@ package main import ( + "github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/plugin" "github.com/pkg/errors" ) @@ -20,10 +21,10 @@ func main() { plugin.ClientMain(&PluginUsingLogAPI{}) } -func (p *PluginUsingLogAPI) OnActivate() error { +func (p *PluginUsingLogAPI) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { p.API.LogDebug("LogDebug", "one", 1, "two", "two", "foo", Foo{bar: 3.1416}) p.API.LogInfo("LogInfo", "one", 1, "two", "two", "foo", Foo{bar: 3.1416}) p.API.LogWarn("LogWarn", "one", 1, "two", "two", "foo", Foo{bar: 3.1416}) p.API.LogError("LogError", "error", errors.WithStack(errors.New("boom!"))) - return nil + return nil, "OK" } diff --git a/app/plugin_api_tests/test_get_bundle_path_plugin/main.go b/app/plugin_api_tests/test_get_bundle_path_plugin/main.go new file mode 100644 index 0000000000..9cade4dc73 --- /dev/null +++ b/app/plugin_api_tests/test_get_bundle_path_plugin/main.go @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + "path/filepath" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + bundlePath, err := p.API.GetBundlePath() + if err != nil { + return nil, err.Error() + "failed get bundle path" + } else if bundlePathFromConfig, _ := filepath.Abs(filepath.Join(*p.API.GetConfig().PluginSettings.Directory, "test_get_bundle_path_plugin")); bundlePathFromConfig != bundlePath { + return nil, fmt.Sprintf("Invalid bundle path returned: %v vs %v", bundlePathFromConfig, bundlePath) + } + + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_get_channels_for_team_for_user_plugin/main.go b/app/plugin_api_tests/test_get_channels_for_team_for_user_plugin/main.go new file mode 100644 index 0000000000..4997ddab15 --- /dev/null +++ b/app/plugin_api_tests/test_get_channels_for_team_for_user_plugin/main.go @@ -0,0 +1,45 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + + channels, err := p.API.GetChannelsForTeamForUser(p.configuration.BasicTeamId, p.configuration.BasicUserId, false) + if err != nil { + return nil, err.Error() + } + if len(channels) != 3 { + return nil, "Returned invalid number of channels" + } + channels, err = p.API.GetChannelsForTeamForUser("invalidid", p.configuration.BasicUserId, false) + if err == nil { + return nil, "Expected to get an error while retrieving channels for invalid id" + } + if len(channels) != 0 { + return nil, "Returned invalid number of channels" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_get_direct_channel_plugin/main.go b/app/plugin_api_tests/test_get_direct_channel_plugin/main.go new file mode 100644 index 0000000000..1b4b3a1dc6 --- /dev/null +++ b/app/plugin_api_tests/test_get_direct_channel_plugin/main.go @@ -0,0 +1,53 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + dm1, err := p.API.GetDirectChannel(p.configuration.BasicUserId, p.configuration.BasicUser2Id) + if err != nil { + return nil, err.Error() + } + if plugin_api_tests.IsEmpty(dm1) { + return nil, "dm1 is empty" + } + + dm2, err := p.API.GetDirectChannel(p.configuration.BasicUserId, p.configuration.BasicUserId) + if err != nil { + return nil, err.Error() + } + if plugin_api_tests.IsEmpty(dm2) { + return nil, "dm2 is empty" + } + + dm3, err := p.API.GetDirectChannel(p.configuration.BasicUserId, model.NewId()) + if err == nil { + return nil, "Expected to get error while fetching incorrect channel" + } + if !plugin_api_tests.IsEmpty(dm3) { + return nil, "dm3 is NOT empty" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_get_profile_image_plugin/main.go b/app/plugin_api_tests/test_get_profile_image_plugin/main.go new file mode 100644 index 0000000000..278ab1de52 --- /dev/null +++ b/app/plugin_api_tests/test_get_profile_image_plugin/main.go @@ -0,0 +1,45 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + + // check existing user first + data, err := p.API.GetProfileImage(p.configuration.BasicUserId) + if err != nil { + return nil, err.Error() + } + if plugin_api_tests.IsEmpty(data) { + return nil, "GetProfileImage return empty" + } + + // then unknown user + data, err = p.API.GetProfileImage(model.NewId()) + if err == nil || data != nil { + return nil, "GetProfileImage should've returned an error" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_member_channels_plugin/main.go b/app/plugin_api_tests/test_member_channels_plugin/main.go new file mode 100644 index 0000000000..8b16033131 --- /dev/null +++ b/app/plugin_api_tests/test_member_channels_plugin/main.go @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + channelMembers, err := p.API.GetChannelMembersForUser(p.configuration.BasicTeamId, p.configuration.BasicUserId, 0, 10) + + if err != nil { + return nil, err.Error() + "failed to get channel members" + } else if len(channelMembers) != 3 { + return nil, "Invalid number of channel members" + } else if channelMembers[0].UserId != p.configuration.BasicUserId { + return nil, "Invalid user id returned" + } + + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_members_plugin/main.go b/app/plugin_api_tests/test_members_plugin/main.go new file mode 100644 index 0000000000..22d4de6a65 --- /dev/null +++ b/app/plugin_api_tests/test_members_plugin/main.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + teamMembers, err := p.API.GetTeamMembersForUser(p.configuration.BasicUserId, 0, 10) + if err != nil { + return nil, err.Error() + "failed to get team members" + } else if len(teamMembers) != 1 { + return nil, "Invalid number of team members" + } else if teamMembers[0].UserId != p.configuration.BasicUserId || teamMembers[0].TeamId != p.configuration.BasicTeamId { + return nil, "Invalid user or team id returned" + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_search_channels_plugin/main.go b/app/plugin_api_tests/test_search_channels_plugin/main.go new file mode 100644 index 0000000000..4af77778eb --- /dev/null +++ b/app/plugin_api_tests/test_search_channels_plugin/main.go @@ -0,0 +1,47 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + + channels, err := p.API.SearchChannels(p.configuration.BasicTeamId, p.configuration.BasicChannelName) + if err != nil { + return nil, err.Error() + } + if len(channels) != 1 { + return nil, "Returned invalid number of channels" + } + + channels, err = p.API.SearchChannels("invalidid", p.configuration.BasicChannelName) + if err != nil { + return nil, err.Error() + } + if len(channels) != 0 { + return nil, "Returned invalid number of channels" + } + + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_search_posts_in_team_plugin/main.go b/app/plugin_api_tests/test_search_posts_in_team_plugin/main.go new file mode 100644 index 0000000000..2aba40f631 --- /dev/null +++ b/app/plugin_api_tests/test_search_posts_in_team_plugin/main.go @@ -0,0 +1,73 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + testCases := []struct { + description string + teamID string + params []*model.SearchParams + expectedPostsLen int + }{ + { + "nil params", + p.configuration.BasicTeamId, + nil, + 0, + }, + { + "empty params", + p.configuration.BasicTeamId, + []*model.SearchParams{}, + 0, + }, + { + "doesn't match any posts", + p.configuration.BasicTeamId, + model.ParseSearchParams("bad message", 0), + 0, + }, + { + "matched posts", + p.configuration.BasicTeamId, + model.ParseSearchParams(p.configuration.BasicPostMessage, 0), + 1, + }, + } + + for _, testCase := range testCases { + posts, err := p.API.SearchPostsInTeam(testCase.teamID, testCase.params) + if err != nil { + return nil, fmt.Sprintf("%v: %v", testCase.description, err.Error()) + } + if testCase.expectedPostsLen != len(posts) { + return nil, fmt.Sprintf("%v: invalid number of posts: %v != %v", testCase.description, testCase.expectedPostsLen, len(posts)) + } + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_search_teams_plugin/main.go b/app/plugin_api_tests/test_search_teams_plugin/main.go new file mode 100644 index 0000000000..6404c9c329 --- /dev/null +++ b/app/plugin_api_tests/test_search_teams_plugin/main.go @@ -0,0 +1,64 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + teams, err := p.API.SearchTeams(p.configuration.BasicTeamName) + if err != nil { + return nil, "search failed: " + err.Message + } + if len(teams) != 1 { + return nil, fmt.Sprintf("search failed, wrong number of teams: %v", len(teams)) + } + + teams, err = p.API.SearchTeams(p.configuration.BasicTeamDisplayName) + if err != nil { + return nil, "search failed: " + err.Message + } + if len(teams) != 1 { + return nil, fmt.Sprintf("search failed, wrong number of teams: %v", len(teams)) + } + + teams, err = p.API.SearchTeams(p.configuration.BasicTeamName[:3]) + + if err != nil { + return nil, "search failed: " + err.Message + } + if len(teams) != 1 { + return nil, fmt.Sprintf("search failed, wrong number of teams: %v", len(teams)) + } + + teams, err = p.API.SearchTeams("not found") + if err != nil { + return nil, "search failed: " + err.Message + } + if len(teams) != 0 { + return nil, fmt.Sprintf("search failed, wrong number of teams: %v", len(teams)) + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_send_mail_plugin/main.go b/app/plugin_api_tests/test_send_mail_plugin/main.go new file mode 100644 index 0000000000..8579b01396 --- /dev/null +++ b/app/plugin_api_tests/test_send_mail_plugin/main.go @@ -0,0 +1,68 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + "strings" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" + "github.com/mattermost/mattermost-server/v5/services/mailservice" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + to := p.configuration.BasicUserEmail + subject := "testing plugin api sending email" + body := "this is a test." + + if err := p.API.SendMail(to, subject, body); err != nil { + return nil, err.Error() + } + + // Check if we received the email + var resultsMailbox mailservice.JSONMessageHeaderInbucket + if errMail := mailservice.RetryInbucket(5, func() error { + var err error + resultsMailbox, err = mailservice.GetMailBox(to) + return err + }); errMail != nil { + return nil, errMail.Error() + } + if len(resultsMailbox) == 0 { + return nil, fmt.Sprintf("No mailbox results. Should be %v", len(resultsMailbox)) + } + if !strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], to) { + return nil, "Result doesn't contain recipient" + } + + resultsEmail, err1 := mailservice.GetMessageFromMailbox(to, resultsMailbox[len(resultsMailbox)-1].ID) + if err1 != nil { + return nil, err1.Error() + } + if resultsEmail.Subject != subject { + return nil, fmt.Sprintf("subject differs: %v vs %s", resultsEmail.Subject, subject) + } + if resultsEmail.Body.Text != body { + return nil, fmt.Sprintf("body differs: %v vs %s", resultsEmail.Body.Text, body) + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_set_profile_image_plugin/main.go b/app/plugin_api_tests/test_set_profile_image_plugin/main.go new file mode 100644 index 0000000000..99256e97e0 --- /dev/null +++ b/app/plugin_api_tests/test_set_profile_image_plugin/main.go @@ -0,0 +1,71 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "bytes" + "fmt" + "image" + "image/color" + "image/png" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + + // Create an 128 x 128 image + img := image.NewRGBA(image.Rect(0, 0, 128, 128)) + // Draw a red dot at (2, 3) + img.Set(2, 3, color.RGBA{255, 0, 0, 255}) + buf := new(bytes.Buffer) + if err := png.Encode(buf, img); err != nil { + return nil, err.Error() + } + + dataBytes := buf.Bytes() + + // Set the user profile image + if err := p.API.SetProfileImage(p.configuration.BasicUserId, dataBytes); err != nil { + return nil, err.Error() + } + + // Get the user profile image to check + imageProfile, err := p.API.GetProfileImage(p.configuration.BasicUserId) + if err != nil { + return nil, err.Error() + } + if plugin_api_tests.IsEmpty(imageProfile) { + return nil, "profile image is empty" + } + + colorful := color.NRGBA{255, 0, 0, 255} + byteReader := bytes.NewReader(imageProfile) + img2, _, err2 := image.Decode(byteReader) + if err2 != nil { + return nil, err.Error() + } + if img2.At(2, 3) != colorful { + return nil, fmt.Sprintf("color mismatch %v != %v", img2.At(2, 3), colorful) + } + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_update_user_active_plugin/main.go b/app/plugin_api_tests/test_update_user_active_plugin/main.go new file mode 100644 index 0000000000..708c192545 --- /dev/null +++ b/app/plugin_api_tests/test_update_user_active_plugin/main.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + uid := p.configuration.BasicUserId + if err := p.API.UpdateUserActive(uid, true); err != nil { + return nil, err.Error() + } + + user, err := p.API.GetUser(uid) + if err != nil { + return nil, err.Error() + } + + if int64(0) != user.DeleteAt { + return nil, "DeleteAt value is not 0" + } + + if err = p.API.UpdateUserActive(uid, false); err != nil { + return nil, err.Error() + } + + user, err = p.API.GetUser(uid) + if err != nil { + return nil, err.Error() + } + if user == nil { + return nil, "GetUser returned nil" + } + + if int64(0) == user.DeleteAt { + return nil, "DeleteAt value is 0" + } + + if err = p.API.UpdateUserActive(uid, true); err != nil { + return nil, err.Error() + } + + if err = p.API.UpdateUserActive(uid, true); err != nil { + return nil, err.Error() + } + + user, err = p.API.GetUser(uid) + if err != nil { + return nil, err.Error() + } + + if int64(0) != user.DeleteAt { + return nil, "DeleteAt value is not 0" + } + + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +} diff --git a/app/plugin_api_tests/test_update_user_status_plugin/main.go b/app/plugin_api_tests/test_update_user_status_plugin/main.go new file mode 100644 index 0000000000..e1a1b30de9 --- /dev/null +++ b/app/plugin_api_tests/test_update_user_status_plugin/main.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package main + +import ( + "fmt" + + "github.com/mattermost/mattermost-server/v5/app/plugin_api_tests" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/plugin" +) + +type MyPlugin struct { + plugin.MattermostPlugin + configuration plugin_api_tests.BasicConfig +} + +func (p *MyPlugin) OnConfigurationChange() error { + if err := p.API.LoadPluginConfiguration(&p.configuration); err != nil { + return err + } + return nil +} + +func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + uid := p.configuration.BasicUserId + + statuses := []string{model.STATUS_ONLINE, model.STATUS_AWAY, model.STATUS_DND, model.STATUS_OFFLINE} + + for _, s := range statuses { + status, err := p.API.UpdateUserStatus(uid, s) + if err != nil { + return nil, err.Error() + } + if status == nil { + return nil, "Status was expected, got nil" + } + if s != status.Status { + return nil, fmt.Sprintf("Invalid status returned: %v != %v", s, status.Status) + } + + } + + status, err := p.API.UpdateUserStatus(uid, "notrealstatus") + if err == nil { + return nil, "Expected to get an error while updating invalid user status" + } + if status != nil { + return nil, "Status was expected to be nil, got: " + status.Status + } + + return nil, "OK" +} + +func main() { + plugin.ClientMain(&MyPlugin{}) +}