mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* bots model, store and api (#9903)
* bots model, store and api
Fixes: MM-13100, MM-13101, MM-13103, MM-13105, MMM-13119
* uncomment tests incorrectly commented, and fix merge issues
* add etags support
* add missing licenses
* remove unused sqlbuilder.go (for now...)
* rejig permissions
* split out READ_BOTS into READ_BOTS and READ_OTHERS_BOTS, the latter
implicitly allowing the former
* make MANAGE_OTHERS_BOTS imply MANAGE_BOTS
* conform to general rest api pattern
* eliminate redundant http.StatusOK
* Update api4/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* s/model.UserFromBotModel/model.UserFromBot/g
* Update model/bot.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* Update model/client4.go
Co-Authored-By: lieut-data <jesse.hallam@gmail.com>
* move sessionHasPermissionToManageBot to app/authorization.go
* use api.ApiSessionRequired for createBot
* introduce BOT_DESCRIPTION_MAX_RUNES constant
* MM-13512 Prevent getting a user by email based on privacy settings (#10021)
* MM-13512 Prevent getting a user by email based on privacy settings
* Add additional config settings to tests
* upgrade db to 5.7 (#10019)
* MM-13526 Add validation when setting a user's Locale field (#10022)
* Fix typos (#10024)
* Fixing first user being created with system admin privilages without being explicity specified. (#10014)
* Revert "Support for Embeded chat (#9129)" (#10017)
This reverts commit 3fcecd521a.
* s/DisableBot/UpdateBotActive
* add permissions on upgrade
* Update NOTICE.txt (#10054)
- add new dependency (text)
- handle switch to forked dependency (go-gomail -> go-mail)
- misc copyright owner updates
* avoid leaking bot knowledge without permission
* [GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)
* 6798 added a new api to get the bulk reactions for posts
* 6798 added the permsission check before getting the reactions
* GH-6798 added a new app function for the new endpoint
* 6798 added a store method to get reactions for multiple posts
* 6798 connected the app function with the new store function
* 6798 fixed the review comments
* MM-13559 Update model.post.is_valid.file_ids.app_error text per report (#10055)
Ticket: https://mattermost.atlassian.net/browse/MM-13559
Report: https://github.com/mattermost/mattermost-server/issues/10023
* Trigger Login Hooks with OAuth (#10061)
* make BotStore.GetAll deterministic even on duplicate CreateAt
* fix spurious TestMuteCommandSpecificChannel test failure
See
https://community-daily.mattermost.com/core/pl/px9p8s3dzbg1pf3ddrm5cr36uw
* fix race in TestExportUserChannels
* TestExportUserChannels: remove SaveMember call, as it is redundant and used to be silently failing anyway
* MM-13117: bot tokens (#10111)
* eliminate redundant Client/AdminClient declarations
* harden TestUpdateChannelScheme to API failures
* eliminate unnecessary config restoration
* minor cleanup
* make TestGenerateMfaSecret config dependency explicit
* TestCreateUserAccessToken for bots
* TestGetUserAccessToken* for bots
* leverage SessionHasPermissionToUserOrBot for user token APIs
* Test(Revoke|Disable|Enable)UserAccessToken
* make EnableUserAccessTokens explicit, so as to not rely on local config.json
* uncomment TestResetPassword, but still skip
* mark assert(Invalid)Token as helper
* fix whitespace issues
* fix mangled comments
* MM-13116: bot plugin api (#10113)
* MM-13117: expose bot API to plugins
This also changes the `CreatorId` column definition to allow for plugin
ids, as the default unless the plugin overrides is to use the plugin id
here. This branch hasn't hit master yet, so no migration needed.
* gofmt issues
* expunge use of BotList in plugin/client API
* introduce model.BotGetOptions
* use botUserId term for clarity
* MM-13129 Adding functionality to deal with orphaned bots (#10238)
* Add way to list orphaned bots.
* Add /assign route to modify ownership of bot accounts.
* Apply suggestions from code review
Co-Authored-By: crspeller <crspeller@gmail.com>
* MM-13120: add IsBot field to returned user objects (#10103)
* MM-13104: forbid bot login (#10251)
* MM-13104: disallow bot login
* fix shadowing
* MM-13136 Disable user bots when user is disabled. (#10293)
* Disable user bots when user is disabled.
* Grammer.
Co-Authored-By: crspeller <crspeller@gmail.com>
* Fixing bot branch for test changes.
* Don't use external dependancies in bot plugin tests.
* Rename bot CreatorId to OwnerId
* Adding ability to re-enable bots
* Fixing IsBot to not attempt to be saved to DB.
* Adding diagnostics and licencing counting for bot accounts.
* Modifying gorp to allow reading of '-' fields.
* Removing unnessisary nil values from UserCountOptions.
* Changing comment to GoDoc format
* Improving user count SQL
* Some improvments from feedback.
* Omit empty on User.IsBot
1056 lines
27 KiB
Go
1056 lines
27 KiB
Go
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost-server/model"
|
|
"github.com/mattermost/mattermost-server/plugin"
|
|
"github.com/mattermost/mattermost-server/services/mailservice"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) {
|
|
pluginDir, err := ioutil.TempDir("", "")
|
|
require.NoError(t, err)
|
|
webappPluginDir, err := ioutil.TempDir("", "")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(pluginDir)
|
|
defer os.RemoveAll(webappPluginDir)
|
|
|
|
env, err := plugin.NewEnvironment(app.NewPluginAPI, pluginDir, webappPluginDir, app.Log)
|
|
require.NoError(t, err)
|
|
|
|
backend := filepath.Join(pluginDir, pluginId, "backend.exe")
|
|
compileGo(t, pluginCode, backend)
|
|
|
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600)
|
|
manifest, activated, reterr := env.Activate(pluginId)
|
|
require.Nil(t, reterr)
|
|
require.NotNil(t, manifest)
|
|
require.True(t, activated)
|
|
|
|
app.SetPluginsEnvironment(env)
|
|
}
|
|
|
|
func TestPluginAPIGetUsers(t *testing.T) {
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
api := th.SetupPluginAPI()
|
|
|
|
user1, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user1" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user1)
|
|
|
|
user2, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user2" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user2)
|
|
|
|
user3, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user3" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user3)
|
|
|
|
user4, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user4" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user4)
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
Page int
|
|
PerPage int
|
|
ExpectedUsers []*model.User
|
|
}{
|
|
{
|
|
"page 0, perPage 0",
|
|
0,
|
|
0,
|
|
[]*model.User{},
|
|
},
|
|
{
|
|
"page 0, perPage 10",
|
|
0,
|
|
10,
|
|
[]*model.User{user1, user2, user3, user4},
|
|
},
|
|
{
|
|
"page 0, perPage 2",
|
|
0,
|
|
2,
|
|
[]*model.User{user1, user2},
|
|
},
|
|
{
|
|
"page 1, perPage 2",
|
|
1,
|
|
2,
|
|
[]*model.User{user3, user4},
|
|
},
|
|
{
|
|
"page 10, perPage 10",
|
|
10,
|
|
10,
|
|
[]*model.User{},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
users, err := api.GetUsers(&model.UserGetOptions{
|
|
Page: testCase.Page,
|
|
PerPage: testCase.PerPage,
|
|
})
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, testCase.ExpectedUsers, users)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPluginAPIGetUsersInTeam(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
api := th.SetupPluginAPI()
|
|
|
|
team1 := th.CreateTeam()
|
|
team2 := th.CreateTeam()
|
|
|
|
user1, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user1" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user1)
|
|
|
|
user2, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user2" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user2)
|
|
|
|
user3, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user3" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user3)
|
|
|
|
user4, err := th.App.CreateUser(&model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Password: "password",
|
|
Username: "user4" + model.NewId(),
|
|
})
|
|
require.Nil(t, err)
|
|
defer th.App.PermanentDeleteUser(user4)
|
|
|
|
// Add all users to team 1
|
|
_, _, err = th.App.joinUserToTeam(team1, user1)
|
|
require.Nil(t, err)
|
|
_, _, err = th.App.joinUserToTeam(team1, user2)
|
|
require.Nil(t, err)
|
|
_, _, err = th.App.joinUserToTeam(team1, user3)
|
|
require.Nil(t, err)
|
|
_, _, err = th.App.joinUserToTeam(team1, user4)
|
|
require.Nil(t, err)
|
|
|
|
// Add only user3 and user4 to team 2
|
|
_, _, err = th.App.joinUserToTeam(team2, user3)
|
|
require.Nil(t, err)
|
|
_, _, err = th.App.joinUserToTeam(team2, user4)
|
|
require.Nil(t, err)
|
|
|
|
testCases := []struct {
|
|
Description string
|
|
TeamId string
|
|
Page int
|
|
PerPage int
|
|
ExpectedUsers []*model.User
|
|
}{
|
|
{
|
|
"unknown team",
|
|
model.NewId(),
|
|
0,
|
|
0,
|
|
[]*model.User{},
|
|
},
|
|
{
|
|
"team 1, page 0, perPage 10",
|
|
team1.Id,
|
|
0,
|
|
10,
|
|
[]*model.User{user1, user2, user3, user4},
|
|
},
|
|
{
|
|
"team 1, page 0, perPage 2",
|
|
team1.Id,
|
|
0,
|
|
2,
|
|
[]*model.User{user1, user2},
|
|
},
|
|
{
|
|
"team 1, page 1, perPage 2",
|
|
team1.Id,
|
|
1,
|
|
2,
|
|
[]*model.User{user3, user4},
|
|
},
|
|
{
|
|
"team 2, page 0, perPage 10",
|
|
team2.Id,
|
|
0,
|
|
10,
|
|
[]*model.User{user3, user4},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
users, err := api.GetUsersInTeam(testCase.TeamId, testCase.Page, testCase.PerPage)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, testCase.ExpectedUsers, users)
|
|
})
|
|
}
|
|
}
|
|
|
|
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()
|
|
api := th.SetupPluginAPI()
|
|
|
|
// check a valid file first
|
|
uploadTime := time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local)
|
|
filename := "testGetFile"
|
|
fileData := []byte("Hello World")
|
|
info, err := th.App.DoUploadFile(uploadTime, th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, filename, fileData)
|
|
require.Nil(t, err)
|
|
defer func() {
|
|
<-th.App.Srv.Store.FileInfo().PermanentDelete(info.Id)
|
|
th.App.RemoveFile(info.Path)
|
|
}()
|
|
|
|
data, err1 := api.GetFile(info.Id)
|
|
require.Nil(t, err1)
|
|
assert.Equal(t, data, fileData)
|
|
|
|
// then checking invalid file
|
|
data, err = api.GetFile("../fake/testingApi")
|
|
require.NotNil(t, err)
|
|
require.Nil(t, data)
|
|
}
|
|
func TestPluginAPISavePluginConfig(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
manifest := &model.Manifest{
|
|
Id: "pluginid",
|
|
SettingsSchema: &model.PluginSettingsSchema{
|
|
Settings: []*model.PluginSetting{
|
|
{Key: "MyStringSetting", Type: "text"},
|
|
{Key: "MyIntSetting", Type: "text"},
|
|
{Key: "MyBoolSetting", Type: "bool"},
|
|
},
|
|
},
|
|
}
|
|
|
|
api := NewPluginAPI(th.App, manifest)
|
|
|
|
pluginConfigJsonString := `{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`
|
|
|
|
var pluginConfig map[string]interface{}
|
|
if err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := api.SavePluginConfig(pluginConfig); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
type Configuration struct {
|
|
MyStringSetting string
|
|
MyIntSetting int
|
|
MyBoolSetting bool
|
|
}
|
|
|
|
savedConfiguration := new(Configuration)
|
|
if err := api.LoadPluginConfiguration(savedConfiguration); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedConfiguration := new(Configuration)
|
|
if err := json.Unmarshal([]byte(pluginConfigJsonString), &expectedConfiguration); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.Equal(t, expectedConfiguration, savedConfiguration)
|
|
}
|
|
|
|
func TestPluginAPIGetPluginConfig(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
manifest := &model.Manifest{
|
|
Id: "pluginid",
|
|
SettingsSchema: &model.PluginSettingsSchema{
|
|
Settings: []*model.PluginSetting{
|
|
{Key: "MyStringSetting", Type: "text"},
|
|
{Key: "MyIntSetting", Type: "text"},
|
|
{Key: "MyBoolSetting", Type: "bool"},
|
|
},
|
|
},
|
|
}
|
|
|
|
api := NewPluginAPI(th.App, manifest)
|
|
|
|
pluginConfigJsonString := `{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`
|
|
var pluginConfig map[string]interface{}
|
|
|
|
if err := json.Unmarshal([]byte(pluginConfigJsonString), &pluginConfig); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.PluginSettings.Plugins["pluginid"] = pluginConfig
|
|
})
|
|
|
|
savedPluginConfig := api.GetPluginConfig()
|
|
assert.Equal(t, pluginConfig, savedPluginConfig)
|
|
}
|
|
|
|
func TestPluginAPILoadPluginConfiguration(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
var pluginJson map[string]interface{}
|
|
if err := json.Unmarshal([]byte(`{"mystringsetting": "str", "MyIntSetting": 32, "myboolsetting": true}`), &pluginJson); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
|
|
})
|
|
setupPluginApiTest(t,
|
|
`
|
|
package main
|
|
|
|
import (
|
|
"github.com/mattermost/mattermost-server/plugin"
|
|
"github.com/mattermost/mattermost-server/model"
|
|
"fmt"
|
|
)
|
|
|
|
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": {
|
|
"settings": [
|
|
{
|
|
"key": "MyStringSetting",
|
|
"type": "text"
|
|
},
|
|
{
|
|
"key": "MyIntSetting",
|
|
"type": "text"
|
|
},
|
|
{
|
|
"key": "MyBoolSetting",
|
|
"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)
|
|
}
|
|
|
|
func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
var pluginJson map[string]interface{}
|
|
if err := json.Unmarshal([]byte(`{"mystringsetting": "override"}`), &pluginJson); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.PluginSettings.Plugins["testloadpluginconfig"] = pluginJson
|
|
})
|
|
setupPluginApiTest(t,
|
|
`
|
|
package main
|
|
|
|
import (
|
|
"github.com/mattermost/mattermost-server/plugin"
|
|
"github.com/mattermost/mattermost-server/model"
|
|
"fmt"
|
|
)
|
|
|
|
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": {
|
|
"settings": [
|
|
{
|
|
"key": "MyStringSetting",
|
|
"type": "text",
|
|
"default": "notthis"
|
|
},
|
|
{
|
|
"key": "MyIntSetting",
|
|
"type": "text",
|
|
"default": 35
|
|
},
|
|
{
|
|
"key": "MyBoolSetting",
|
|
"type": "bool",
|
|
"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 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) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
api := th.SetupPluginAPI()
|
|
|
|
pluginCode := `
|
|
package main
|
|
|
|
import (
|
|
"github.com/mattermost/mattermost-server/plugin"
|
|
)
|
|
|
|
type MyPlugin struct {
|
|
plugin.MattermostPlugin
|
|
}
|
|
|
|
func main() {
|
|
plugin.ClientMain(&MyPlugin{})
|
|
}
|
|
`
|
|
|
|
pluginDir, err := ioutil.TempDir("", "")
|
|
require.NoError(t, err)
|
|
webappPluginDir, err := ioutil.TempDir("", "")
|
|
require.NoError(t, err)
|
|
defer os.RemoveAll(pluginDir)
|
|
defer os.RemoveAll(webappPluginDir)
|
|
|
|
env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log)
|
|
require.NoError(t, err)
|
|
|
|
pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
|
|
var pluginManifests []*model.Manifest
|
|
for _, pluginID := range pluginIDs {
|
|
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
|
compileGo(t, pluginCode, backend)
|
|
|
|
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
|
manifest, activated, reterr := env.Activate(pluginID)
|
|
|
|
require.Nil(t, reterr)
|
|
require.NotNil(t, manifest)
|
|
require.True(t, activated)
|
|
pluginManifests = append(pluginManifests, manifest)
|
|
}
|
|
th.App.SetPluginsEnvironment(env)
|
|
|
|
// Decativate the last one for testing
|
|
sucess := env.Deactivate(pluginIDs[len(pluginIDs)-1])
|
|
require.True(t, sucess)
|
|
|
|
// check existing user first
|
|
plugins, err := api.GetPlugins()
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, plugins)
|
|
assert.Equal(t, pluginManifests, plugins)
|
|
}
|
|
|
|
func TestPluginAPIGetTeamIcon(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()
|
|
fileReader := bytes.NewReader(dataBytes)
|
|
|
|
// Set the Team Icon
|
|
err = th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
|
|
require.Nil(t, err)
|
|
|
|
// Get the team icon to check
|
|
teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
|
|
require.Nil(t, err)
|
|
require.NotEmpty(t, teamIcon)
|
|
|
|
colorful := color.NRGBA{255, 0, 0, 255}
|
|
byteReader := bytes.NewReader(teamIcon)
|
|
img2, _, err2 := image.Decode(byteReader)
|
|
require.Nil(t, err2)
|
|
require.Equal(t, img2.At(2, 3), colorful)
|
|
}
|
|
|
|
func TestPluginAPISetTeamIcon(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.SetTeamIcon(th.BasicTeam.Id, dataBytes)
|
|
require.Nil(t, err)
|
|
|
|
// Get the user profile image to check
|
|
teamIcon, err := api.GetTeamIcon(th.BasicTeam.Id)
|
|
require.Nil(t, err)
|
|
require.NotEmpty(t, teamIcon)
|
|
|
|
colorful := color.NRGBA{255, 0, 0, 255}
|
|
byteReader := bytes.NewReader(teamIcon)
|
|
img2, _, err2 := image.Decode(byteReader)
|
|
require.Nil(t, err2)
|
|
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()
|
|
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)
|
|
err1 := png.Encode(buf, img)
|
|
require.Nil(t, err1)
|
|
dataBytes := buf.Bytes()
|
|
fileReader := bytes.NewReader(dataBytes)
|
|
|
|
// Set the Team Icon
|
|
err := th.App.SetTeamIconFromFile(th.BasicTeam, fileReader)
|
|
require.Nil(t, err)
|
|
err = api.RemoveTeamIcon(th.BasicTeam.Id)
|
|
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)
|
|
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)
|
|
|
|
}
|
|
|
|
func TestPluginAPI_SearchTeams(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
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/plugin"
|
|
"github.com/mattermost/mattermost-server/model"
|
|
)
|
|
|
|
type MyPlugin struct {
|
|
plugin.MattermostPlugin
|
|
}
|
|
|
|
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)
|
|
}
|