2019-11-29 12:59:40 +01:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
|
|
|
// See LICENSE.txt for license information.
|
2017-01-30 08:30:02 -05:00
|
|
|
|
|
|
|
|
package api4
|
|
|
|
|
|
|
|
|
|
import (
|
2017-10-02 03:50:56 -05:00
|
|
|
"fmt"
|
2018-01-17 12:38:37 -06:00
|
|
|
"io/ioutil"
|
2017-10-12 12:24:54 -07:00
|
|
|
"net"
|
2017-01-30 08:30:02 -05:00
|
|
|
"net/http"
|
2017-02-17 10:31:21 -05:00
|
|
|
"os"
|
2020-01-15 13:38:55 -05:00
|
|
|
"path/filepath"
|
2017-01-30 08:30:02 -05:00
|
|
|
"strings"
|
2020-03-03 14:19:54 +01:00
|
|
|
"sync"
|
2017-01-30 08:30:02 -05:00
|
|
|
"testing"
|
2017-01-31 09:31:53 -05:00
|
|
|
"time"
|
2017-01-30 08:30:02 -05:00
|
|
|
|
2019-11-28 14:39:38 +01:00
|
|
|
"github.com/mattermost/mattermost-server/v5/app"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/config"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/mlog"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
2020-03-30 19:17:40 +02:00
|
|
|
"github.com/mattermost/mattermost-server/v5/services/searchengine"
|
2019-11-28 14:39:38 +01:00
|
|
|
"github.com/mattermost/mattermost-server/v5/store"
|
2020-03-04 14:18:03 +01:00
|
|
|
"github.com/mattermost/mattermost-server/v5/store/localcachelayer"
|
2020-03-02 17:13:39 +01:00
|
|
|
"github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/testlib"
|
2019-11-28 14:39:38 +01:00
|
|
|
"github.com/mattermost/mattermost-server/v5/utils"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/web"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v5/wsapi"
|
2017-02-17 10:31:21 -05:00
|
|
|
|
2019-09-10 09:43:51 +02:00
|
|
|
s3 "github.com/minio/minio-go/v6"
|
|
|
|
|
"github.com/minio/minio-go/v6/pkg/credentials"
|
2019-10-27 18:54:19 +07:00
|
|
|
"github.com/stretchr/testify/require"
|
2017-01-30 08:30:02 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type TestHelper struct {
|
2019-03-06 15:06:45 -05:00
|
|
|
App *app.App
|
|
|
|
|
Server *app.Server
|
|
|
|
|
ConfigStore config.Store
|
2017-09-06 17:12:54 -05:00
|
|
|
|
2019-12-11 20:18:36 +01:00
|
|
|
Client *model.Client4
|
|
|
|
|
BasicUser *model.User
|
|
|
|
|
BasicUser2 *model.User
|
|
|
|
|
TeamAdminUser *model.User
|
|
|
|
|
BasicTeam *model.Team
|
|
|
|
|
BasicChannel *model.Channel
|
|
|
|
|
BasicPrivateChannel *model.Channel
|
|
|
|
|
BasicPrivateChannel2 *model.Channel
|
|
|
|
|
BasicDeletedChannel *model.Channel
|
|
|
|
|
BasicChannel2 *model.Channel
|
|
|
|
|
BasicPost *model.Post
|
|
|
|
|
Group *model.Group
|
2017-01-31 09:31:53 -05:00
|
|
|
|
|
|
|
|
SystemAdminClient *model.Client4
|
|
|
|
|
SystemAdminUser *model.User
|
2018-06-25 12:33:13 -07:00
|
|
|
tempWorkspace string
|
2020-03-04 14:18:03 +01:00
|
|
|
|
2020-05-19 18:20:41 +02:00
|
|
|
LocalClient *model.Client4
|
|
|
|
|
|
2020-03-04 14:18:03 +01:00
|
|
|
IncludeCacheLayer bool
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2020-03-02 17:13:39 +01:00
|
|
|
var mainHelper *testlib.MainHelper
|
2018-11-20 20:16:25 -05:00
|
|
|
|
2020-03-11 16:25:45 +01:00
|
|
|
func SetMainHelper(mh *testlib.MainHelper) {
|
|
|
|
|
mainHelper = mh
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 19:17:40 +02:00
|
|
|
func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, enterprise bool, includeCache bool, updateConfig func(*model.Config)) *TestHelper {
|
2020-01-15 13:38:55 -05:00
|
|
|
tempWorkspace, err := ioutil.TempDir("", "apptest")
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 11:33:59 -04:00
|
|
|
memoryStore, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{IgnoreEnvironmentOverrides: true})
|
2018-01-17 12:38:37 -06:00
|
|
|
if err != nil {
|
2019-03-06 15:06:45 -05:00
|
|
|
panic("failed to initialize memory store: " + err.Error())
|
2018-01-17 12:38:37 -06:00
|
|
|
}
|
|
|
|
|
|
2020-01-15 13:38:55 -05:00
|
|
|
config := memoryStore.Get()
|
|
|
|
|
*config.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
|
|
|
|
*config.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
2020-05-19 18:20:41 +02:00
|
|
|
config.ServiceSettings.EnableLocalMode = model.NewBool(true)
|
|
|
|
|
*config.ServiceSettings.LocalModeSocketLocation = filepath.Join(tempWorkspace, "mattermost_local.sock")
|
2020-01-15 13:38:55 -05:00
|
|
|
if updateConfig != nil {
|
|
|
|
|
updateConfig(config)
|
|
|
|
|
}
|
|
|
|
|
memoryStore.Set(config)
|
|
|
|
|
|
2019-03-06 15:06:45 -05:00
|
|
|
var options []app.Option
|
|
|
|
|
options = append(options, app.ConfigStore(memoryStore))
|
2020-03-02 17:13:39 +01:00
|
|
|
options = append(options, app.StoreOverride(dbStore))
|
2017-10-12 12:24:54 -07:00
|
|
|
|
2018-11-28 10:56:21 -08:00
|
|
|
s, err := app.NewServer(options...)
|
2018-01-11 15:23:41 -06:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2020-03-04 14:18:03 +01:00
|
|
|
if includeCache {
|
|
|
|
|
// Adds the cache layer to the test store
|
|
|
|
|
s.Store = localcachelayer.NewLocalCacheLayer(s.Store, s.Metrics, s.Cluster, s.CacheProvider)
|
|
|
|
|
}
|
2018-01-11 15:23:41 -06:00
|
|
|
|
2017-09-12 09:19:52 -05:00
|
|
|
th := &TestHelper{
|
2020-03-04 14:18:03 +01:00
|
|
|
App: s.FakeApp(),
|
|
|
|
|
Server: s,
|
|
|
|
|
ConfigStore: memoryStore,
|
|
|
|
|
IncludeCacheLayer: includeCache,
|
2017-09-12 09:19:52 -05:00
|
|
|
}
|
|
|
|
|
|
2020-03-30 19:17:40 +02:00
|
|
|
if searchEngine != nil {
|
|
|
|
|
th.App.SetSearchEngine(searchEngine)
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-31 09:39:31 -05:00
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
|
|
|
*cfg.TeamSettings.MaxUsersPerTeam = 50
|
|
|
|
|
*cfg.RateLimitSettings.Enable = false
|
2019-01-31 08:12:01 -05:00
|
|
|
*cfg.EmailSettings.SendEmailNotifications = true
|
2019-07-26 18:53:12 -04:00
|
|
|
|
|
|
|
|
// Disable sniffing, otherwise elastic client fails to connect to docker node
|
|
|
|
|
// More details: https://github.com/olivere/elastic/wiki/Sniffing
|
|
|
|
|
*cfg.ElasticsearchSettings.Sniff = false
|
2017-10-31 09:39:31 -05:00
|
|
|
})
|
2017-10-18 15:36:43 -07:00
|
|
|
prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress
|
2018-12-06 13:19:32 -05:00
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
2020-05-19 18:20:41 +02:00
|
|
|
if err := th.Server.Start(); err != nil {
|
|
|
|
|
panic(err)
|
2018-02-07 13:41:15 +05:30
|
|
|
}
|
|
|
|
|
|
2017-10-18 15:36:43 -07:00
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
2020-02-13 13:26:58 +01:00
|
|
|
Init(th.Server, th.Server.AppOptions, th.App.Srv().Router)
|
2020-05-19 18:20:41 +02:00
|
|
|
InitLocal(th.Server, th.Server.AppOptions, th.App.Srv().LocalRouter)
|
2020-02-13 13:26:58 +01:00
|
|
|
web.New(th.Server, th.Server.AppOptions, th.App.Srv().Router)
|
|
|
|
|
wsapi.Init(th.App, th.App.Srv().WebSocketRouter)
|
2019-04-30 20:36:21 +02:00
|
|
|
th.App.DoAppMigrations()
|
2017-10-09 14:59:48 -07:00
|
|
|
|
2017-10-18 15:36:43 -07:00
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
2017-03-13 08:26:23 -04:00
|
|
|
|
2019-05-21 20:03:36 +02:00
|
|
|
// Disable strict password requirements for test
|
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
|
|
|
*cfg.PasswordSettings.MinimumLength = 5
|
|
|
|
|
*cfg.PasswordSettings.Lowercase = false
|
|
|
|
|
*cfg.PasswordSettings.Uppercase = false
|
|
|
|
|
*cfg.PasswordSettings.Symbol = false
|
|
|
|
|
*cfg.PasswordSettings.Number = false
|
|
|
|
|
})
|
|
|
|
|
|
2017-09-12 14:12:29 -05:00
|
|
|
if enterprise {
|
2018-02-09 10:04:48 -06:00
|
|
|
th.App.SetLicense(model.NewTestLicense())
|
|
|
|
|
} else {
|
|
|
|
|
th.App.SetLicense(nil)
|
2017-09-12 14:12:29 -05:00
|
|
|
}
|
|
|
|
|
|
2017-03-13 08:26:23 -04:00
|
|
|
th.Client = th.CreateClient()
|
|
|
|
|
th.SystemAdminClient = th.CreateClient()
|
2018-06-25 12:33:13 -07:00
|
|
|
|
2020-05-19 18:20:41 +02:00
|
|
|
th.LocalClient = th.CreateLocalClient(*config.ServiceSettings.LocalModeSocketLocation)
|
|
|
|
|
|
2018-06-25 12:33:13 -07:00
|
|
|
if th.tempWorkspace == "" {
|
2020-01-15 13:38:55 -05:00
|
|
|
th.tempWorkspace = tempWorkspace
|
2018-06-25 12:33:13 -07:00
|
|
|
}
|
|
|
|
|
|
2017-03-13 08:26:23 -04:00
|
|
|
return th
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-10 19:31:41 +01:00
|
|
|
func SetupEnterprise(tb testing.TB) *TestHelper {
|
2020-03-02 17:13:39 +01:00
|
|
|
if testing.Short() {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if mainHelper == nil {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbStore := mainHelper.GetStore()
|
|
|
|
|
dbStore.DropAllTables()
|
|
|
|
|
dbStore.MarkSystemRanUnitTests()
|
2020-03-30 19:17:40 +02:00
|
|
|
searchEngine := mainHelper.GetSearchEngine()
|
|
|
|
|
return setupTestHelper(dbStore, searchEngine, true, true, nil)
|
2017-09-12 09:19:52 -05:00
|
|
|
}
|
2017-07-20 16:25:35 +01:00
|
|
|
|
2020-02-10 19:31:41 +01:00
|
|
|
func Setup(tb testing.TB) *TestHelper {
|
2020-03-02 17:13:39 +01:00
|
|
|
if testing.Short() {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if mainHelper == nil {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbStore := mainHelper.GetStore()
|
|
|
|
|
dbStore.DropAllTables()
|
|
|
|
|
dbStore.MarkSystemRanUnitTests()
|
2020-03-30 19:17:40 +02:00
|
|
|
searchEngine := mainHelper.GetSearchEngine()
|
|
|
|
|
return setupTestHelper(dbStore, searchEngine, false, true, nil)
|
2018-07-26 08:31:22 -07:00
|
|
|
}
|
|
|
|
|
|
2020-02-10 19:31:41 +01:00
|
|
|
func SetupConfig(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper {
|
2020-03-02 17:13:39 +01:00
|
|
|
if testing.Short() {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if mainHelper == nil {
|
|
|
|
|
tb.SkipNow()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dbStore := mainHelper.GetStore()
|
|
|
|
|
dbStore.DropAllTables()
|
|
|
|
|
dbStore.MarkSystemRanUnitTests()
|
2020-03-30 19:17:40 +02:00
|
|
|
searchEngine := mainHelper.GetSearchEngine()
|
|
|
|
|
return setupTestHelper(dbStore, searchEngine, false, true, updateConfig)
|
2020-03-02 17:13:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetupConfigWithStoreMock(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper {
|
2020-03-30 19:17:40 +02:00
|
|
|
th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, false, false, updateConfig)
|
2020-03-02 17:13:39 +01:00
|
|
|
emptyMockStore := mocks.Store{}
|
|
|
|
|
emptyMockStore.On("Close").Return(nil)
|
|
|
|
|
th.App.Srv().Store = &emptyMockStore
|
|
|
|
|
return th
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetupWithStoreMock(tb testing.TB) *TestHelper {
|
2020-03-30 19:17:40 +02:00
|
|
|
th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, false, false, nil)
|
2020-03-02 17:13:39 +01:00
|
|
|
emptyMockStore := mocks.Store{}
|
|
|
|
|
emptyMockStore.On("Close").Return(nil)
|
|
|
|
|
th.App.Srv().Store = &emptyMockStore
|
|
|
|
|
return th
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetupEnterpriseWithStoreMock(tb testing.TB) *TestHelper {
|
2020-03-30 19:17:40 +02:00
|
|
|
th := setupTestHelper(testlib.GetMockStoreForSetupFunctions(), nil, true, false, nil)
|
2020-03-02 17:13:39 +01:00
|
|
|
emptyMockStore := mocks.Store{}
|
|
|
|
|
emptyMockStore.On("Close").Return(nil)
|
|
|
|
|
th.App.Srv().Store = &emptyMockStore
|
|
|
|
|
return th
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2018-11-22 04:53:44 -05:00
|
|
|
func (me *TestHelper) ShutdownApp() {
|
|
|
|
|
done := make(chan bool)
|
2017-04-20 09:55:02 -04:00
|
|
|
go func() {
|
2018-11-28 10:56:21 -08:00
|
|
|
me.Server.Shutdown()
|
2018-11-22 04:53:44 -05:00
|
|
|
close(done)
|
2017-04-20 09:55:02 -04:00
|
|
|
}()
|
|
|
|
|
|
2018-11-22 04:53:44 -05:00
|
|
|
select {
|
|
|
|
|
case <-done:
|
|
|
|
|
case <-time.After(30 * time.Second):
|
2020-02-13 17:53:23 +01:00
|
|
|
// panic instead of fatal to terminate all tests in this package, otherwise the
|
2018-11-22 04:53:44 -05:00
|
|
|
// still running App could spuriously fail subsequent tests.
|
|
|
|
|
panic("failed to shutdown App within 30 seconds")
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-20 09:55:02 -04:00
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
func (me *TestHelper) TearDown() {
|
2017-02-07 09:35:58 -08:00
|
|
|
utils.DisableDebugLogForTest()
|
2020-03-04 14:18:03 +01:00
|
|
|
if me.IncludeCacheLayer {
|
|
|
|
|
// Clean all the caches
|
|
|
|
|
me.App.InvalidateAllCaches()
|
|
|
|
|
}
|
2017-02-07 09:35:58 -08:00
|
|
|
|
2018-11-22 04:53:44 -05:00
|
|
|
me.ShutdownApp()
|
2017-10-02 03:50:56 -05:00
|
|
|
|
2017-02-07 09:35:58 -08:00
|
|
|
utils.EnableDebugLogForTest()
|
2017-02-02 11:46:42 -05:00
|
|
|
}
|
|
|
|
|
|
2020-03-03 14:19:54 +01:00
|
|
|
var initBasicOnce sync.Once
|
|
|
|
|
var userCache struct {
|
|
|
|
|
SystemAdminUser *model.User
|
|
|
|
|
TeamAdminUser *model.User
|
|
|
|
|
BasicUser *model.User
|
|
|
|
|
BasicUser2 *model.User
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func (me *TestHelper) InitBasic() *TestHelper {
|
2017-10-12 12:24:54 -07:00
|
|
|
me.waitForConnectivity()
|
|
|
|
|
|
2020-03-03 14:19:54 +01:00
|
|
|
// create users once and cache them because password hashing is slow
|
|
|
|
|
initBasicOnce.Do(func() {
|
|
|
|
|
me.SystemAdminUser = me.CreateUser()
|
|
|
|
|
me.App.UpdateUserRoles(me.SystemAdminUser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_ADMIN_ROLE_ID, false)
|
|
|
|
|
me.SystemAdminUser, _ = me.App.GetUser(me.SystemAdminUser.Id)
|
|
|
|
|
userCache.SystemAdminUser = me.SystemAdminUser.DeepCopy()
|
|
|
|
|
|
|
|
|
|
me.TeamAdminUser = me.CreateUser()
|
|
|
|
|
me.App.UpdateUserRoles(me.TeamAdminUser.Id, model.SYSTEM_USER_ROLE_ID, false)
|
|
|
|
|
me.TeamAdminUser, _ = me.App.GetUser(me.TeamAdminUser.Id)
|
|
|
|
|
userCache.TeamAdminUser = me.TeamAdminUser.DeepCopy()
|
|
|
|
|
|
|
|
|
|
me.BasicUser = me.CreateUser()
|
|
|
|
|
me.BasicUser, _ = me.App.GetUser(me.BasicUser.Id)
|
|
|
|
|
userCache.BasicUser = me.BasicUser.DeepCopy()
|
|
|
|
|
|
|
|
|
|
me.BasicUser2 = me.CreateUser()
|
|
|
|
|
me.BasicUser2, _ = me.App.GetUser(me.BasicUser2.Id)
|
|
|
|
|
userCache.BasicUser2 = me.BasicUser2.DeepCopy()
|
|
|
|
|
})
|
|
|
|
|
// restore cached users
|
|
|
|
|
me.SystemAdminUser = userCache.SystemAdminUser.DeepCopy()
|
|
|
|
|
me.TeamAdminUser = userCache.TeamAdminUser.DeepCopy()
|
|
|
|
|
me.BasicUser = userCache.BasicUser.DeepCopy()
|
|
|
|
|
me.BasicUser2 = userCache.BasicUser2.DeepCopy()
|
|
|
|
|
mainHelper.GetSQLSupplier().GetMaster().Insert(me.SystemAdminUser, me.TeamAdminUser, me.BasicUser, me.BasicUser2)
|
|
|
|
|
// restore non hashed password for login
|
|
|
|
|
me.SystemAdminUser.Password = "Pa$$word11"
|
|
|
|
|
me.TeamAdminUser.Password = "Pa$$word11"
|
|
|
|
|
me.BasicUser.Password = "Pa$$word11"
|
|
|
|
|
me.BasicUser2.Password = "Pa$$word11"
|
2018-11-20 20:16:25 -05:00
|
|
|
|
2020-03-03 14:19:54 +01:00
|
|
|
me.LoginSystemAdmin()
|
2017-01-31 09:31:53 -05:00
|
|
|
me.LoginTeamAdmin()
|
2018-11-28 09:05:39 -05:00
|
|
|
|
2017-01-31 09:31:53 -05:00
|
|
|
me.BasicTeam = me.CreateTeam()
|
2017-02-02 09:04:36 -05:00
|
|
|
me.BasicChannel = me.CreatePublicChannel()
|
2017-03-22 11:13:44 -04:00
|
|
|
me.BasicPrivateChannel = me.CreatePrivateChannel()
|
2019-12-11 20:18:36 +01:00
|
|
|
me.BasicPrivateChannel2 = me.CreatePrivateChannel()
|
2018-07-30 15:06:08 -04:00
|
|
|
me.BasicDeletedChannel = me.CreatePublicChannel()
|
2017-02-13 10:52:50 -05:00
|
|
|
me.BasicChannel2 = me.CreatePublicChannel()
|
|
|
|
|
me.BasicPost = me.CreatePost()
|
2017-10-02 03:50:56 -05:00
|
|
|
me.LinkUserToTeam(me.BasicUser, me.BasicTeam)
|
|
|
|
|
me.LinkUserToTeam(me.BasicUser2, me.BasicTeam)
|
2017-09-06 17:12:54 -05:00
|
|
|
me.App.AddUserToChannel(me.BasicUser, me.BasicChannel)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser2, me.BasicChannel)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser, me.BasicChannel2)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser2, me.BasicChannel2)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser, me.BasicPrivateChannel)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser2, me.BasicPrivateChannel)
|
2018-07-30 15:06:08 -04:00
|
|
|
me.App.AddUserToChannel(me.BasicUser, me.BasicDeletedChannel)
|
|
|
|
|
me.App.AddUserToChannel(me.BasicUser2, me.BasicDeletedChannel)
|
2017-11-21 13:08:32 -06:00
|
|
|
me.App.UpdateUserRoles(me.BasicUser.Id, model.SYSTEM_USER_ROLE_ID, false)
|
2018-07-30 15:06:08 -04:00
|
|
|
me.Client.DeleteChannel(me.BasicDeletedChannel.Id)
|
2017-01-30 08:30:02 -05:00
|
|
|
me.LoginBasic()
|
2019-04-09 07:09:57 -04:00
|
|
|
me.Group = me.CreateGroup()
|
2017-01-30 08:30:02 -05:00
|
|
|
|
|
|
|
|
return me
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-12 12:24:54 -07:00
|
|
|
func (me *TestHelper) waitForConnectivity() {
|
|
|
|
|
for i := 0; i < 1000; i++ {
|
2020-02-13 13:26:58 +01:00
|
|
|
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", me.App.Srv().ListenAddr.Port))
|
2017-10-12 12:24:54 -07:00
|
|
|
if err == nil {
|
2017-10-16 08:09:43 -07:00
|
|
|
conn.Close()
|
2017-10-12 12:24:54 -07:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Millisecond * 20)
|
|
|
|
|
}
|
|
|
|
|
panic("unable to connect")
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func (me *TestHelper) CreateClient() *model.Client4 {
|
2020-02-13 13:26:58 +01:00
|
|
|
return model.NewAPIv4Client(fmt.Sprintf("http://localhost:%v", me.App.Srv().ListenAddr.Port))
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2020-05-19 18:20:41 +02:00
|
|
|
// ToDo: maybe move this to NewAPIv4SocketClient and reuse it in mmctl
|
|
|
|
|
func (me *TestHelper) CreateLocalClient(socketPath string) *model.Client4 {
|
|
|
|
|
httpClient := &http.Client{
|
|
|
|
|
Transport: &http.Transport{
|
|
|
|
|
Dial: func(network, addr string) (net.Conn, error) {
|
|
|
|
|
return net.Dial("unix", socketPath)
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &model.Client4{
|
|
|
|
|
ApiUrl: "http://_" + model.API_URL_SUFFIX,
|
|
|
|
|
HttpClient: httpClient,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-28 04:58:19 -04:00
|
|
|
func (me *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, *model.AppError) {
|
2020-02-13 13:26:58 +01:00
|
|
|
return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv().ListenAddr.Port), me.Client.AuthToken)
|
2017-03-28 04:58:19 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-08 17:04:17 +02:00
|
|
|
func (me *TestHelper) CreateWebSocketSystemAdminClient() (*model.WebSocketClient, *model.AppError) {
|
2020-02-13 13:26:58 +01:00
|
|
|
return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv().ListenAddr.Port), me.SystemAdminClient.AuthToken)
|
2018-06-08 17:04:17 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 14:24:04 +00:00
|
|
|
func (me *TestHelper) CreateWebSocketClientWithClient(client *model.Client4) (*model.WebSocketClient, *model.AppError) {
|
2020-02-13 13:26:58 +01:00
|
|
|
return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv().ListenAddr.Port), client.AuthToken)
|
2019-11-28 14:24:04 +00:00
|
|
|
}
|
|
|
|
|
|
2020-01-29 11:01:06 -05:00
|
|
|
func (me *TestHelper) CreateBotWithSystemAdminClient() *model.Bot {
|
|
|
|
|
return me.CreateBotWithClient((me.SystemAdminClient))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateBotWithClient(client *model.Client4) *model.Bot {
|
|
|
|
|
bot := &model.Bot{
|
|
|
|
|
Username: GenerateTestUsername(),
|
|
|
|
|
DisplayName: "a bot",
|
|
|
|
|
Description: "bot",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
rbot, resp := client.CreateBot(bot)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rbot
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func (me *TestHelper) CreateUser() *model.User {
|
2017-01-31 09:31:53 -05:00
|
|
|
return me.CreateUserWithClient(me.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateTeam() *model.Team {
|
|
|
|
|
return me.CreateTeamWithClient(me.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateTeamWithClient(client *model.Client4) *model.Team {
|
|
|
|
|
id := model.NewId()
|
|
|
|
|
team := &model.Team{
|
|
|
|
|
DisplayName: "dn_" + id,
|
|
|
|
|
Name: GenerateTestTeamName(),
|
2018-01-17 12:38:37 -06:00
|
|
|
Email: me.GenerateTestEmail(),
|
2017-01-31 09:31:53 -05:00
|
|
|
Type: model.TEAM_OPEN,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
rteam, resp := client.CreateTeam(team)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-01-31 09:31:53 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rteam
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
|
2017-01-30 08:30:02 -05:00
|
|
|
id := model.NewId()
|
|
|
|
|
|
|
|
|
|
user := &model.User{
|
2018-01-17 12:38:37 -06:00
|
|
|
Email: me.GenerateTestEmail(),
|
2017-01-30 08:30:02 -05:00
|
|
|
Username: GenerateTestUsername(),
|
|
|
|
|
Nickname: "nn_" + id,
|
|
|
|
|
FirstName: "f_" + id,
|
|
|
|
|
LastName: "l_" + id,
|
2019-05-21 20:03:36 +02:00
|
|
|
Password: "Pa$$word11",
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
2018-03-21 14:27:14 -04:00
|
|
|
ruser, response := client.CreateUser(user)
|
|
|
|
|
if response.Error != nil {
|
|
|
|
|
panic(response.Error)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-21 20:03:36 +02:00
|
|
|
ruser.Password = "Pa$$word11"
|
2020-02-13 13:26:58 +01:00
|
|
|
_, err := me.App.Srv().Store.User().VerifyEmail(ruser.Id, ruser.Email)
|
2019-06-12 19:30:50 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2017-01-30 08:30:02 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return ruser
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-02 09:04:36 -05:00
|
|
|
func (me *TestHelper) CreatePublicChannel() *model.Channel {
|
|
|
|
|
return me.CreateChannelWithClient(me.Client, model.CHANNEL_OPEN)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreatePrivateChannel() *model.Channel {
|
|
|
|
|
return me.CreateChannelWithClient(me.Client, model.CHANNEL_PRIVATE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateChannelWithClient(client *model.Client4, channelType string) *model.Channel {
|
2017-10-17 13:21:12 -04:00
|
|
|
return me.CreateChannelWithClientAndTeam(client, channelType, me.BasicTeam.Id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) CreateChannelWithClientAndTeam(client *model.Client4, channelType string, teamId string) *model.Channel {
|
2017-02-02 09:04:36 -05:00
|
|
|
id := model.NewId()
|
|
|
|
|
|
|
|
|
|
channel := &model.Channel{
|
|
|
|
|
DisplayName: "dn_" + id,
|
|
|
|
|
Name: GenerateTestChannelName(),
|
|
|
|
|
Type: channelType,
|
2017-10-17 13:21:12 -04:00
|
|
|
TeamId: teamId,
|
2017-02-02 09:04:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
rchannel, resp := client.CreateChannel(channel)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-02-02 09:04:36 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rchannel
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-13 10:52:50 -05:00
|
|
|
func (me *TestHelper) CreatePost() *model.Post {
|
|
|
|
|
return me.CreatePostWithClient(me.Client, me.BasicChannel)
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-30 00:09:05 +09:00
|
|
|
func (me *TestHelper) CreatePinnedPost() *model.Post {
|
|
|
|
|
return me.CreatePinnedPostWithClient(me.Client, me.BasicChannel)
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 07:36:52 -05:00
|
|
|
func (me *TestHelper) CreateMessagePost(message string) *model.Post {
|
|
|
|
|
return me.CreateMessagePostWithClient(me.Client, me.BasicChannel, message)
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-13 10:52:50 -05:00
|
|
|
func (me *TestHelper) CreatePostWithClient(client *model.Client4, channel *model.Channel) *model.Post {
|
|
|
|
|
id := model.NewId()
|
|
|
|
|
|
|
|
|
|
post := &model.Post{
|
|
|
|
|
ChannelId: channel.Id,
|
|
|
|
|
Message: "message_" + id,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
rpost, resp := client.CreatePost(post)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rpost
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-30 00:09:05 +09:00
|
|
|
func (me *TestHelper) CreatePinnedPostWithClient(client *model.Client4, channel *model.Channel) *model.Post {
|
|
|
|
|
id := model.NewId()
|
|
|
|
|
|
|
|
|
|
post := &model.Post{
|
|
|
|
|
ChannelId: channel.Id,
|
|
|
|
|
Message: "message_" + id,
|
|
|
|
|
IsPinned: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
rpost, resp := client.CreatePost(post)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rpost
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 07:36:52 -05:00
|
|
|
func (me *TestHelper) CreateMessagePostWithClient(client *model.Client4, channel *model.Channel, message string) *model.Post {
|
|
|
|
|
post := &model.Post{
|
|
|
|
|
ChannelId: channel.Id,
|
|
|
|
|
Message: message,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
rpost, resp := client.CreatePost(post)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return rpost
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-28 13:09:32 -04:00
|
|
|
func (me *TestHelper) CreateMessagePostNoClient(channel *model.Channel, message string, createAtTime int64) *model.Post {
|
2020-02-13 13:26:58 +01:00
|
|
|
post, err := me.App.Srv().Store.Post().Save(&model.Post{
|
2018-08-28 13:09:32 -04:00
|
|
|
UserId: me.BasicUser.Id,
|
|
|
|
|
ChannelId: channel.Id,
|
|
|
|
|
Message: message,
|
|
|
|
|
CreateAt: createAtTime,
|
2019-06-14 12:02:33 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2018-08-28 13:09:32 -04:00
|
|
|
|
|
|
|
|
return post
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 20:15:47 +01:00
|
|
|
func (me *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
var err *model.AppError
|
|
|
|
|
var channel *model.Channel
|
2018-11-28 18:01:49 +01:00
|
|
|
if channel, err = me.App.GetOrCreateDirectChannel(me.BasicUser.Id, user.Id); err != nil {
|
2018-09-27 20:15:47 +01:00
|
|
|
mlog.Error(err.Error())
|
|
|
|
|
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return channel
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func (me *TestHelper) LoginBasic() {
|
2017-01-31 09:31:53 -05:00
|
|
|
me.LoginBasicWithClient(me.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginBasic2() {
|
|
|
|
|
me.LoginBasic2WithClient(me.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginTeamAdmin() {
|
|
|
|
|
me.LoginTeamAdminWithClient(me.Client)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginSystemAdmin() {
|
|
|
|
|
me.LoginSystemAdminWithClient(me.SystemAdminClient)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginBasicWithClient(client *model.Client4) {
|
2017-01-30 08:30:02 -05:00
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
_, resp := client.Login(me.BasicUser.Email, me.BasicUser.Password)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-01-30 08:30:02 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 09:31:53 -05:00
|
|
|
func (me *TestHelper) LoginBasic2WithClient(client *model.Client4) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
_, resp := client.Login(me.BasicUser2.Email, me.BasicUser2.Password)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-01-31 09:31:53 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginTeamAdminWithClient(client *model.Client4) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
_, resp := client.Login(me.TeamAdminUser.Email, me.TeamAdminUser.Password)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-01-31 09:31:53 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) LoginSystemAdminWithClient(client *model.Client4) {
|
2017-01-30 08:30:02 -05:00
|
|
|
utils.DisableDebugLogForTest()
|
2018-11-28 09:05:39 -05:00
|
|
|
_, resp := client.Login(me.SystemAdminUser.Email, me.SystemAdminUser.Password)
|
|
|
|
|
if resp.Error != nil {
|
|
|
|
|
panic(resp.Error)
|
|
|
|
|
}
|
2017-01-31 09:31:53 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-18 05:50:17 +09:00
|
|
|
func (me *TestHelper) UpdateActiveUser(user *model.User, active bool) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
2017-09-12 09:19:52 -05:00
|
|
|
_, err := me.App.UpdateActive(user, active)
|
2017-02-18 05:50:17 +09:00
|
|
|
if err != nil {
|
2018-04-27 12:49:45 -07:00
|
|
|
mlog.Error(err.Error())
|
|
|
|
|
|
2017-02-18 05:50:17 +09:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
2017-01-31 09:31:53 -05:00
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
err := me.App.JoinUserToTeam(team, user, "")
|
2017-01-31 09:31:53 -05:00
|
|
|
if err != nil {
|
2018-04-27 12:49:45 -07:00
|
|
|
mlog.Error(err.Error())
|
|
|
|
|
|
2017-01-31 09:31:53 -05:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-09 05:48:30 -07:00
|
|
|
func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
member, err := me.App.AddUserToChannel(user, channel)
|
|
|
|
|
if err != nil {
|
2018-04-27 12:49:45 -07:00
|
|
|
mlog.Error(err.Error())
|
|
|
|
|
|
2018-03-09 05:48:30 -07:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
return member
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-17 12:38:37 -06:00
|
|
|
func (me *TestHelper) GenerateTestEmail() string {
|
2019-08-02 10:53:00 -04:00
|
|
|
if *me.App.Config().EmailSettings.SMTPServer != "localhost" && os.Getenv("CI_INBUCKET_PORT") == "" {
|
2017-10-18 14:13:14 -07:00
|
|
|
return strings.ToLower("success+" + model.NewId() + "@simulator.amazonses.com")
|
|
|
|
|
}
|
2019-08-02 10:53:00 -04:00
|
|
|
return strings.ToLower(model.NewId() + "@localhost")
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2019-04-09 07:09:57 -04:00
|
|
|
func (me *TestHelper) CreateGroup() *model.Group {
|
|
|
|
|
id := model.NewId()
|
|
|
|
|
group := &model.Group{
|
|
|
|
|
Name: "n-" + id,
|
|
|
|
|
DisplayName: "dn_" + id,
|
|
|
|
|
Source: model.GroupSourceLdap,
|
|
|
|
|
RemoteId: "ri_" + id,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
group, err := me.App.CreateGroup(group)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return group
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-19 18:20:41 +02:00
|
|
|
// TestForSystemAdminAndLocal runs a test function for both
|
|
|
|
|
// SystemAdmin and Local clients. Several endpoints work in the same
|
|
|
|
|
// way when used by a fully privileged user and through the local
|
|
|
|
|
// mode, so this helper facilitates checking both
|
|
|
|
|
func (me *TestHelper) TestForSystemAdminAndLocal(t *testing.T, f func(*testing.T, *model.Client4), name ...string) {
|
|
|
|
|
var testName string
|
|
|
|
|
if len(name) > 0 {
|
|
|
|
|
testName = name[0] + "/"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Run(testName+"SystemAdminClient", func(t *testing.T) {
|
|
|
|
|
f(t, me.SystemAdminClient)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run(testName+"LocalClient", func(t *testing.T) {
|
|
|
|
|
f(t, me.LocalClient)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TestForAllClients runs a test function for all the clients
|
|
|
|
|
// registered in the TestHelper
|
|
|
|
|
func (me *TestHelper) TestForAllClients(t *testing.T, f func(*testing.T, *model.Client4), name ...string) {
|
|
|
|
|
var testName string
|
|
|
|
|
if len(name) > 0 {
|
|
|
|
|
testName = name[0] + "/"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Run(testName+"Client", func(t *testing.T) {
|
|
|
|
|
f(t, me.Client)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run(testName+"SystemAdminClient", func(t *testing.T) {
|
|
|
|
|
f(t, me.SystemAdminClient)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run(testName+"LocalClient", func(t *testing.T) {
|
|
|
|
|
f(t, me.LocalClient)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func GenerateTestUsername() string {
|
2017-04-20 09:55:02 -04:00
|
|
|
return "fakeuser" + model.NewRandomString(10)
|
2017-01-31 09:31:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GenerateTestTeamName() string {
|
2017-02-14 10:28:08 -05:00
|
|
|
return "faketeam" + model.NewRandomString(6)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2017-02-02 09:04:36 -05:00
|
|
|
func GenerateTestChannelName() string {
|
2017-02-14 10:28:08 -05:00
|
|
|
return "fakechannel" + model.NewRandomString(10)
|
2017-02-02 09:04:36 -05:00
|
|
|
}
|
|
|
|
|
|
2017-04-20 09:55:02 -04:00
|
|
|
func GenerateTestAppName() string {
|
|
|
|
|
return "fakeoauthapp" + model.NewRandomString(10)
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-24 14:27:47 +01:00
|
|
|
func GenerateTestId() string {
|
|
|
|
|
return model.NewId()
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-30 08:30:02 -05:00
|
|
|
func CheckUserSanitization(t *testing.T, user *model.User) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
|
|
|
|
|
2019-10-27 18:54:19 +07:00
|
|
|
require.Equal(t, "", user.Password, "password wasn't blank")
|
|
|
|
|
require.Empty(t, user.AuthData, "auth data wasn't blank")
|
|
|
|
|
require.Equal(t, "", user.MfaSecret, "mfa secret wasn't blank")
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CheckEtag(t *testing.T, data interface{}, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
|
|
|
|
|
2019-10-27 18:54:19 +07:00
|
|
|
require.Empty(t, data)
|
|
|
|
|
require.Equal(t, resp.StatusCode, http.StatusNotModified, "wrong status code for etag")
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CheckNoError(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
|
|
|
|
|
2020-01-15 13:38:55 -05:00
|
|
|
require.Nil(t, resp.Error, "expected no error")
|
2017-03-31 09:56:20 -04:00
|
|
|
}
|
|
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func checkHTTPStatus(t *testing.T, resp *model.Response, expectedStatus int, expectError bool) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
|
|
|
|
|
2019-10-27 18:54:19 +07:00
|
|
|
require.NotNilf(t, resp, "Unexpected nil response, expected http:%v, expectError:%v", expectedStatus, expectError)
|
|
|
|
|
if expectError {
|
|
|
|
|
require.NotNil(t, resp.Error, "Expected a non-nil error and http status:%v, got nil, %v", expectedStatus, resp.StatusCode)
|
|
|
|
|
} else {
|
|
|
|
|
require.Nil(t, resp.Error, "Expected no error and http status:%v, got %q, http:%v", expectedStatus, resp.Error, resp.StatusCode)
|
2017-03-31 09:56:20 -04:00
|
|
|
}
|
2019-10-27 18:54:19 +07:00
|
|
|
require.Equalf(t, expectedStatus, resp.StatusCode, "Expected http status:%v, got %v (err: %q)", expectedStatus, resp.StatusCode, resp.Error)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func CheckOKStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusOK, false)
|
|
|
|
|
}
|
2018-02-20 08:51:01 -05:00
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func CheckCreatedStatus(t *testing.T, resp *model.Response) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
checkHTTPStatus(t, resp, http.StatusCreated, false)
|
|
|
|
|
}
|
2017-01-30 08:30:02 -05:00
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func CheckForbiddenStatus(t *testing.T, resp *model.Response) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
checkHTTPStatus(t, resp, http.StatusForbidden, true)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusUnauthorized, true)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CheckNotFoundStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusNotFound, true)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func CheckBadRequestStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusBadRequest, true)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
|
|
|
|
|
2017-02-21 19:42:34 -05:00
|
|
|
func CheckNotImplementedStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusNotImplemented, true)
|
2017-02-21 19:42:34 -05:00
|
|
|
}
|
|
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func CheckRequestEntityTooLargeStatus(t *testing.T, resp *model.Response) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
checkHTTPStatus(t, resp, http.StatusRequestEntityTooLarge, true)
|
|
|
|
|
}
|
2018-02-20 08:51:01 -05:00
|
|
|
|
MM-7633: Optimize memory utilization during file uploads (#9835)
* MM-7633: Optimize memory utilization during file uploads
Refactored the file upload code to reduce redundant buffering and stream
directly to the file store. Added tests.
Benchmark results:
```
levs-mbp:mattermost-server levb$ go test -v -run nothing -bench Upload -benchmem ./app
...
BenchmarkUploadFile/random-5Mb-gif-raw-ish_DoUploadFile-4 10 122598031 ns/op 21211370 B/op 1008 allocs/op
BenchmarkUploadFile/random-5Mb-gif-raw_UploadFileTask-4 100 20211926 ns/op 5678750 B/op 126 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFiles-4 2 1037051184 ns/op 81806360 B/op 3705013 allocs/op
BenchmarkUploadFile/random-5Mb-gif-UploadFileTask-4 2 933644431 ns/op 67015868 B/op 3704410 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw-ish_DoUploadFile-4 100 13110509 ns/op 6032614 B/op 8052 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-raw_UploadFileTask-4 100 10729867 ns/op 1738303 B/op 125 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFiles-4 2 925274912 ns/op 70326352 B/op 3718856 allocs/op
BenchmarkUploadFile/random-2Mb-jpg-UploadFileTask-4 2 995033336 ns/op 58113796 B/op 3710943 allocs/op
BenchmarkUploadFile/zero-10Mb-raw-ish_DoUploadFile-4 30 50777211 ns/op 54791929 B/op 2714 allocs/op
BenchmarkUploadFile/zero-10Mb-raw_UploadFileTask-4 50 36387339 ns/op 10503920 B/op 126 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFiles-4 30 48657678 ns/op 54791948 B/op 2719 allocs/op
BenchmarkUploadFile/zero-10Mb-UploadFileTask-4 50 37506467 ns/op 31492060 B/op 131 allocs/op
...
```
https://mattermost.atlassian.net/browse/MM-7633 https://github.com/mattermost/mattermost-server/issues/7801
[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.]
- [x] Added or updated unit tests (required for all new features)
- [ ] Added API documentation (required for all new APIs)
- [ ] All new/modified APIs include changes to the drivers
*N/A*???
- [x] Includes text changes and localization file ([.../i18n/en.json](https://github.com/mattermost/mattermost-server/blob/master/i18n/en.json)) updates
Overview of changes:
- api4
- Replaced `uploadFile` handler with `uploadFileStream` that reduces
unnecessary buffering.
- Added/refactored tests for the new API.
- Refactored apitestlib/Check...Status functions.
- app
- Added App.UploadFileTask, a more efficient refactor of UploadFile.
- Consistently set `FileInfo.HasPreviewImage`
- Added benchmarks for the new and prior implementations
- Replaced passing around `*image.Image` with `image.Image` in the
existing code.
- model
- Added a more capable `client4.UploadFiles` API to match the new server
API’s capabilities.
- I18n
- Replaced `api.file.upload_file.bad_parse.app_error` with a more generic
`api.file.upload_file.read_request.app_error`
- plugin
- Removed type `plugin.multiPluginHookRunnerFunc` in favor of using
`func(hooks Hooks) bool` explicitly, to help with testing
- tests
- Added test files for testing images
Still remaining, but can be separate PRs - please let me know the preferred
course of action
- Investigate JS client API - how does it do multipart?
- Performance loss from old code on (small) image processing?
- Deprecate the old functions, change other API implementations to use
UploadFileTask
Definitely separate future PRs - should I file tickets foe these?
- Only invoke t.readAll() if there are indeed applicable plugins to run
- Find a way to leverage goexif buffer rather than re-reading
Suggested long-term improvements - should I file separate tickets for these?
- Actually allow uploading of large (GB-sized) files. This may require a
change in how the file is passed to plugins.
- (Many) api4 tests should probably be subtests and share a server setup -
will be much faster
- Performance improvements in image processing (goexif/thumbnail/preview)
(maybe use https://mattermost.atlassian.net/browse/MM-10188 for this)
Questions:
1. I am commiting MBs of test images, are there better alternatives? I can
probably create much less dense images that would take up considerably less
space, even at pretty large sizes
2. I18n: Do I need to do anything special for the string change? Or just wait
until it gets picked up and translated/updated?
3. The image dimensions are flipped in resulting FileInfo to match the actual
orientation. Is this by design? Should add a test for it, perhaps?
4. What to do in the case of partial success? S3 but not DB, some files but not
others? For now, just doing what the old code did, I think.
5. Make maxUploadDrainBytes configurable? Also, should this be the systemic
behavior of all APIs with non-empty body? Otherwise dropped altogether?
Check all other ioutil.ReadAll() from sockets. Find a way to set a total
byte limit on request Body?
* WIP - Fixed for GetPluginsEnvironment() changes
* WIP - PR feedback
1. Refactored checkHTTPStatus to improve failure messages
2. Use `var name []type` rather than `name := ([]type)(nil)`
3. Replaced single-letter `p` with a more intention-revealing `part`
4. Added tests for full image size, `HasPreviewImage`
* WIP - rebased (c.Session->c.App.Session)
* WIP - PR feedback: eliminated use of Request.MultipartReader
Instead of hacking the request object to use r.MultipartReader now have own
functions `parseMultipartRequestHeader` and `multipartReader` eliminating the
need to hack the request object to use Request.MultipartReader limitations.
* WIP - PR feedback: UploadFileX with functional options
* WIP - PR feedback: style
* WIP - PR feedback: errors cleanup
* WIP - clarified File Upload benchmarks
* WIP - PR feedback: display the value of erroneous formname
* WIP - PR feedback: fixed handling of multiple channel_ids
* WIP - rebased from master - fixed tests
* PR Feedback
* PR feedback - moved client4.UploadFiles to _test for now
2018-12-13 13:32:07 -08:00
|
|
|
func CheckInternalErrorStatus(t *testing.T, resp *model.Response) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
checkHTTPStatus(t, resp, http.StatusInternalServerError, true)
|
2017-03-12 04:10:56 +05:30
|
|
|
}
|
|
|
|
|
|
2019-11-27 20:41:09 -05:00
|
|
|
func CheckServiceUnavailableStatus(t *testing.T, resp *model.Response) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
checkHTTPStatus(t, resp, http.StatusServiceUnavailable, true)
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 09:31:53 -05:00
|
|
|
func CheckErrorMessage(t *testing.T, resp *model.Response, errorId string) {
|
2018-02-20 08:51:01 -05:00
|
|
|
t.Helper()
|
|
|
|
|
|
2019-11-12 03:43:10 -08:00
|
|
|
require.NotNilf(t, resp.Error, "should have errored with message: %s", errorId)
|
|
|
|
|
require.Equalf(t, resp.Error.Id, errorId, "incorrect error message, actual: %s, expected: %s", resp.Error.Id, errorId)
|
2017-01-30 08:30:02 -05:00
|
|
|
}
|
2017-02-17 10:31:21 -05:00
|
|
|
|
2019-10-23 08:45:15 -04:00
|
|
|
func CheckStartsWith(t *testing.T, value, prefix, message string) {
|
|
|
|
|
require.True(t, strings.HasPrefix(value, prefix), message, value)
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-31 09:22:52 -07:00
|
|
|
// Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
|
|
|
|
|
// If signV2 input is false, function always returns signature v4.
|
|
|
|
|
//
|
|
|
|
|
// Additionally this function also takes a user defined region, if set
|
|
|
|
|
// disables automatic region lookup.
|
|
|
|
|
func s3New(endpoint, accessKey, secretKey string, secure bool, signV2 bool, region string) (*s3.Client, error) {
|
|
|
|
|
var creds *credentials.Credentials
|
2017-05-30 16:12:24 -07:00
|
|
|
if signV2 {
|
2017-07-31 09:22:52 -07:00
|
|
|
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV2)
|
|
|
|
|
} else {
|
|
|
|
|
creds = credentials.NewStatic(accessKey, secretKey, "", credentials.SignatureV4)
|
2017-05-30 16:12:24 -07:00
|
|
|
}
|
2017-07-31 09:22:52 -07:00
|
|
|
return s3.NewWithCredentials(endpoint, creds, secure, region)
|
2017-05-30 16:12:24 -07:00
|
|
|
}
|
|
|
|
|
|
2017-11-09 14:46:20 -06:00
|
|
|
func (me *TestHelper) cleanupTestFile(info *model.FileInfo) error {
|
|
|
|
|
cfg := me.App.Config()
|
|
|
|
|
if *cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
|
2019-01-31 08:12:01 -05:00
|
|
|
endpoint := *cfg.FileSettings.AmazonS3Endpoint
|
|
|
|
|
accessKey := *cfg.FileSettings.AmazonS3AccessKeyId
|
|
|
|
|
secretKey := *cfg.FileSettings.AmazonS3SecretAccessKey
|
2017-11-09 14:46:20 -06:00
|
|
|
secure := *cfg.FileSettings.AmazonS3SSL
|
|
|
|
|
signV2 := *cfg.FileSettings.AmazonS3SignV2
|
2019-01-31 08:12:01 -05:00
|
|
|
region := *cfg.FileSettings.AmazonS3Region
|
2017-07-31 09:22:52 -07:00
|
|
|
s3Clnt, err := s3New(endpoint, accessKey, secretKey, secure, signV2, region)
|
2017-02-17 10:31:21 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-01-31 08:12:01 -05:00
|
|
|
bucket := *cfg.FileSettings.AmazonS3Bucket
|
2017-02-17 10:31:21 -05:00
|
|
|
if err := s3Clnt.RemoveObject(bucket, info.Path); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if info.ThumbnailPath != "" {
|
|
|
|
|
if err := s3Clnt.RemoveObject(bucket, info.ThumbnailPath); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if info.PreviewPath != "" {
|
|
|
|
|
if err := s3Clnt.RemoveObject(bucket, info.PreviewPath); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-09 14:46:20 -06:00
|
|
|
} else if *cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
|
2019-01-31 08:12:01 -05:00
|
|
|
if err := os.Remove(*cfg.FileSettings.Directory + info.Path); err != nil {
|
2017-02-17 10:31:21 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if info.ThumbnailPath != "" {
|
2019-01-31 08:12:01 -05:00
|
|
|
if err := os.Remove(*cfg.FileSettings.Directory + info.ThumbnailPath); err != nil {
|
2017-02-17 10:31:21 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if info.PreviewPath != "" {
|
2019-01-31 08:12:01 -05:00
|
|
|
if err := os.Remove(*cfg.FileSettings.Directory + info.PreviewPath); err != nil {
|
2017-02-17 10:31:21 -05:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2017-03-14 21:08:58 +09:00
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Channel) {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
2020-02-13 13:26:58 +01:00
|
|
|
if cm, err := me.App.Srv().Store.Channel().GetMember(channel.Id, user.Id); err == nil {
|
2018-04-20 19:49:13 +01:00
|
|
|
cm.SchemeAdmin = true
|
2020-02-13 13:26:58 +01:00
|
|
|
if _, err = me.App.Srv().Store.Channel().UpdateMember(cm); err != nil {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.EnableDebugLogForTest()
|
2019-06-28 18:41:50 +06:00
|
|
|
panic(err)
|
2017-03-14 21:08:58 +09:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
2019-04-15 22:53:52 +02:00
|
|
|
panic(err)
|
2017-03-14 21:08:58 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
2020-02-13 13:26:58 +01:00
|
|
|
if tm, err := me.App.Srv().Store.Team().GetMember(team.Id, user.Id); err == nil {
|
2018-04-20 19:49:13 +01:00
|
|
|
tm.SchemeAdmin = true
|
2020-02-13 13:26:58 +01:00
|
|
|
if _, err = me.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
2018-04-20 19:49:13 +01:00
|
|
|
utils.EnableDebugLogForTest()
|
2019-06-14 14:32:24 -04:00
|
|
|
panic(err)
|
2018-04-20 19:49:13 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.EnableDebugLogForTest()
|
2019-06-14 10:28:21 -04:00
|
|
|
mlog.Error(err.Error())
|
2018-04-27 12:49:45 -07:00
|
|
|
|
2017-03-14 21:08:58 +09:00
|
|
|
time.Sleep(time.Second)
|
2019-06-14 10:28:21 -04:00
|
|
|
panic(err)
|
2017-03-14 21:08:58 +09:00
|
|
|
}
|
2018-04-20 19:49:13 +01:00
|
|
|
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 03:50:56 -05:00
|
|
|
func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
2020-02-13 13:26:58 +01:00
|
|
|
if tm, err := me.App.Srv().Store.Team().GetMember(team.Id, user.Id); err == nil {
|
2018-04-20 19:49:13 +01:00
|
|
|
tm.SchemeAdmin = false
|
2020-02-13 13:26:58 +01:00
|
|
|
if _, err = me.App.Srv().Store.Team().UpdateMember(tm); err != nil {
|
2018-04-20 19:49:13 +01:00
|
|
|
utils.EnableDebugLogForTest()
|
2019-06-14 14:32:24 -04:00
|
|
|
panic(err)
|
2018-04-20 19:49:13 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.EnableDebugLogForTest()
|
2019-06-14 10:28:21 -04:00
|
|
|
mlog.Error(err.Error())
|
2018-04-27 12:49:45 -07:00
|
|
|
|
2017-03-14 21:08:58 +09:00
|
|
|
time.Sleep(time.Second)
|
2019-06-14 10:28:21 -04:00
|
|
|
panic(err)
|
2017-03-14 21:08:58 +09:00
|
|
|
}
|
2018-04-20 19:49:13 +01:00
|
|
|
|
2017-03-14 21:08:58 +09:00
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
2018-02-06 15:34:08 +00:00
|
|
|
|
|
|
|
|
func (me *TestHelper) SaveDefaultRolePermissions() map[string][]string {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
results := make(map[string][]string)
|
|
|
|
|
|
|
|
|
|
for _, roleName := range []string{
|
|
|
|
|
"system_user",
|
|
|
|
|
"system_admin",
|
|
|
|
|
"team_user",
|
|
|
|
|
"team_admin",
|
|
|
|
|
"channel_user",
|
|
|
|
|
"channel_admin",
|
|
|
|
|
} {
|
|
|
|
|
role, err1 := me.App.GetRoleByName(roleName)
|
|
|
|
|
if err1 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results[roleName] = role.Permissions
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
for roleName, permissions := range data {
|
|
|
|
|
role, err1 := me.App.GetRoleByName(roleName)
|
|
|
|
|
if err1 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
role.Permissions = permissions
|
|
|
|
|
|
|
|
|
|
_, err2 := me.App.UpdateRole(role)
|
|
|
|
|
if err2 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err2)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
role, err1 := me.App.GetRoleByName(roleName)
|
|
|
|
|
if err1 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var newPermissions []string
|
|
|
|
|
for _, p := range role.Permissions {
|
|
|
|
|
if p != permission {
|
|
|
|
|
newPermissions = append(newPermissions, p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
role.Permissions = newPermissions
|
|
|
|
|
|
|
|
|
|
_, err2 := me.App.UpdateRole(role)
|
|
|
|
|
if err2 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) AddPermissionToRole(permission string, roleName string) {
|
|
|
|
|
utils.DisableDebugLogForTest()
|
|
|
|
|
|
|
|
|
|
role, err1 := me.App.GetRoleByName(roleName)
|
|
|
|
|
if err1 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, existingPermission := range role.Permissions {
|
|
|
|
|
if existingPermission == permission {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
role.Permissions = append(role.Permissions, permission)
|
|
|
|
|
|
|
|
|
|
_, err2 := me.App.UpdateRole(role)
|
|
|
|
|
if err2 != nil {
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
panic(err2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
utils.EnableDebugLogForTest()
|
|
|
|
|
}
|
2020-03-05 10:04:34 -05:00
|
|
|
|
|
|
|
|
func (me *TestHelper) SetupTeamScheme() *model.Scheme {
|
|
|
|
|
return me.SetupScheme(model.SCHEME_SCOPE_TEAM)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) SetupChannelScheme() *model.Scheme {
|
|
|
|
|
return me.SetupScheme(model.SCHEME_SCOPE_CHANNEL)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (me *TestHelper) SetupScheme(scope string) *model.Scheme {
|
|
|
|
|
scheme := model.Scheme{
|
|
|
|
|
Name: model.NewId(),
|
|
|
|
|
DisplayName: model.NewId(),
|
|
|
|
|
Scope: scope,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if scheme, err := me.App.CreateScheme(&scheme); err == nil {
|
|
|
|
|
return scheme
|
|
|
|
|
} else {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|