From 428454cee47050a9d1b4f69619019b313f13056a Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Mon, 4 Nov 2019 17:35:58 -0800 Subject: [PATCH 01/15] Adding interplugin communication. (#12829) * Adding interplugin communication. * Naming changes and moving ResponseTransfer to own file. * Fix. * Tests and moving to buffering bytes. * Switching API to passing plugin ID through path rather than a header. * Review feedback. --- app/plugin_api.go | 27 +++++++ app/plugin_api_test.go | 120 +++++++++++++++++++++++++++-- app/plugin_requests.go | 31 ++++++++ app/response_transfer.go | 70 +++++++++++++++++ plugin/api.go | 6 ++ plugin/client_rpc.go | 82 +++++++++++++++++++- plugin/context.go | 1 + plugin/interface_generator/main.go | 13 ++-- plugin/plugintest/api.go | 20 ++++- 9 files changed, 351 insertions(+), 19 deletions(-) create mode 100644 app/response_transfer.go diff --git a/app/plugin_api.go b/app/plugin_api.go index 1aaced6823..8aeb2c8f8d 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -10,6 +10,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "path/filepath" "strings" @@ -825,3 +826,29 @@ func (api *PluginAPI) DeleteBotIconImage(userId string) *model.AppError { return api.app.DeleteBotIconImage(userId) } + +func (api *PluginAPI) PluginHTTP(request *http.Request) *http.Response { + split := strings.SplitN(request.URL.Path, "/", 3) + if len(split) != 3 { + return &http.Response{ + StatusCode: http.StatusBadRequest, + Body: ioutil.NopCloser(bytes.NewBufferString("Not enough URL. Form of URL should be //*")), + } + } + destinationPluginId := split[1] + newURL, err := url.Parse("/" + split[2]) + request.URL = newURL + if destinationPluginId == "" || err != nil { + message := "No plugin specified. Form of URL should be //*" + if err != nil { + message = "Form of URL should be //* Error: " + err.Error() + } + return &http.Response{ + StatusCode: http.StatusBadRequest, + Body: ioutil.NopCloser(bytes.NewBufferString(message)), + } + } + responseTransfer := &PluginResponseWriter{} + api.app.ServeInterPluginRequest(responseTransfer, request, api.id, destinationPluginId) + return responseTransfer.GenerateResponse() +} diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index a353e5ce74..e6c4b2b94e 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/require" ) -func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) string { +func setupMultiPluginApiTest(t *testing.T, pluginCodes []string, pluginManifests []string, pluginIds []string, app *App) string { pluginDir, err := ioutil.TempDir("", "") require.NoError(t, err) webappPluginDir, err := ioutil.TempDir("", "") @@ -37,20 +37,29 @@ func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, env, err := plugin.NewEnvironment(app.NewPluginAPI, pluginDir, webappPluginDir, app.Log) require.NoError(t, err) - backend := filepath.Join(pluginDir, pluginId, "backend.exe") - utils.CompileGo(t, pluginCode, backend) + require.Equal(t, len(pluginCodes), len(pluginIds)) + require.Equal(t, len(pluginManifests), len(pluginIds)) - ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600) - manifest, activated, reterr := env.Activate(pluginId) - require.Nil(t, reterr) - require.NotNil(t, manifest) - require.True(t, activated) + for i, pluginId := range pluginIds { + backend := filepath.Join(pluginDir, pluginId, "backend.exe") + utils.CompileGo(t, pluginCodes[i], backend) + + ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifests[i]), 0600) + manifest, activated, reterr := env.Activate(pluginId) + require.Nil(t, reterr) + require.NotNil(t, manifest) + require.True(t, activated) + } app.SetPluginsEnvironment(env) return pluginDir } +func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string, pluginId string, app *App) string { + return setupMultiPluginApiTest(t, []string{pluginCode}, []string{pluginManifest}, []string{pluginId}, app) +} + func TestPublicFilesPathConfiguration(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() @@ -1462,3 +1471,98 @@ func TestPluginAddUserToChannel(t *testing.T) { require.Equal(t, th.BasicChannel.Id, member.ChannelId) require.Equal(t, th.BasicUser.Id, member.UserId) } + +func TestInterpluginPluginHTTP(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + setupMultiPluginApiTest(t, + []string{` + package main + + import ( + "github.com/mattermost/mattermost-server/plugin" + "bytes" + "net/http" + ) + + type MyPlugin struct { + plugin.MattermostPlugin + } + + func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/api/v2/test" { + return + } + buf := bytes.Buffer{} + buf.ReadFrom(r.Body) + resp := "we got:" + buf.String() + w.WriteHeader(598) + w.Write([]byte(resp)) + } + + func main() { + plugin.ClientMain(&MyPlugin{}) + } + `, + ` + package main + + import ( + "github.com/mattermost/mattermost-server/plugin" + "github.com/mattermost/mattermost-server/model" + "bytes" + "net/http" + "io/ioutil" + ) + + type MyPlugin struct { + plugin.MattermostPlugin + } + + func (p *MyPlugin) MessageWillBePosted(c *plugin.Context, post *model.Post) (*model.Post, string) { + buf := bytes.Buffer{} + buf.WriteString("This is the request") + req, err := http.NewRequest("GET", "/testplugininterserver/api/v2/test", &buf) + if err != nil { + return nil, err.Error() + } + req.Header.Add("Mattermost-User-Id", "userid") + resp := p.API.PluginHTTP(req) + if resp == nil { + return nil, "Nil resp" + } + if resp.Body == nil { + return nil, "Nil body" + } + respbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err.Error() + } + if resp.StatusCode != 598 { + return nil, "wrong status " + string(respbody) + } + return nil, string(respbody) + } + + func main() { + plugin.ClientMain(&MyPlugin{}) + } + `, + }, + []string{ + `{"id": "testplugininterserver", "backend": {"executable": "backend.exe"}}`, + `{"id": "testplugininterclient", "backend": {"executable": "backend.exe"}}`, + }, + []string{ + "testplugininterserver", + "testplugininterclient", + }, + th.App, + ) + + hooks, err := th.App.GetPluginsEnvironment().HooksForPlugin("testplugininterclient") + require.NoError(t, err) + _, ret := hooks.MessageWillBePosted(nil, nil) + assert.Equal(t, "we got:This is the request", ret) +} diff --git a/app/plugin_requests.go b/app/plugin_requests.go index 208b564de0..b36d6e4319 100644 --- a/app/plugin_requests.go +++ b/app/plugin_requests.go @@ -42,6 +42,37 @@ func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) { a.servePluginRequest(w, r, hooks.ServeHTTP) } +func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) { + pluginsEnvironment := a.GetPluginsEnvironment() + if pluginsEnvironment == nil { + err := model.NewAppError("ServeInterPluginRequest", "app.plugin.disabled.app_error", nil, "Plugin enviroment not found.", http.StatusNotImplemented) + a.Log.Error(err.Error()) + w.WriteHeader(err.StatusCode) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(err.ToJson())) + return + } + + hooks, err := pluginsEnvironment.HooksForPlugin(destinationPluginId) + if err != nil { + a.Log.Error("Access to route for non-existent plugin in inter plugin request", + mlog.String("sourse_plugin_id", sourcePluginId), + mlog.String("destination_plugin_id", destinationPluginId), + mlog.Err(err), + ) + http.NotFound(w, r) + return + } + + context := &plugin.Context{ + RequestId: model.NewId(), + UserAgent: r.UserAgent(), + SourcePluginId: sourcePluginId, + } + + hooks.ServeHTTP(context, w, r) +} + // ServePluginPublicRequest serves public plugin files // at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything} func (a *App) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) { diff --git a/app/response_transfer.go b/app/response_transfer.go new file mode 100644 index 0000000000..3f28e2c17c --- /dev/null +++ b/app/response_transfer.go @@ -0,0 +1,70 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "strings" +) + +type PluginResponseWriter struct { + bytes.Buffer + headers http.Header + statusCode int +} + +func (rt *PluginResponseWriter) Header() http.Header { + if rt.headers == nil { + rt.headers = make(http.Header) + } + return rt.headers +} + +func (rt *PluginResponseWriter) WriteHeader(statusCode int) { + rt.statusCode = statusCode +} + +// From net/http/httptest/recorder.go +func parseContentLength(cl string) int64 { + cl = strings.TrimSpace(cl) + if cl == "" { + return -1 + } + n, err := strconv.ParseInt(cl, 10, 64) + if err != nil { + return -1 + } + return n + +} + +func (rt *PluginResponseWriter) GenerateResponse() *http.Response { + res := &http.Response{ + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + StatusCode: rt.statusCode, + Header: rt.headers.Clone(), + } + + if res.StatusCode == 0 { + res.StatusCode = http.StatusOK + } + + res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode)) + + if rt.Len() > 0 { + res.Body = ioutil.NopCloser(rt) + } else { + res.Body = http.NoBody + } + + res.ContentLength = parseContentLength(rt.headers.Get("Content-Length")) + + return res +} diff --git a/plugin/api.go b/plugin/api.go index 7c7a665efa..7756eec9c8 100644 --- a/plugin/api.go +++ b/plugin/api.go @@ -5,6 +5,7 @@ package plugin import ( "io" + "net/http" plugin "github.com/hashicorp/go-plugin" "github.com/mattermost/mattermost-server/model" @@ -747,6 +748,11 @@ type API interface { // // Minimum server version: 5.14 DeleteBotIconImage(botUserId string) *model.AppError + + // PluginHTTP allows inter-plugin requests to plugin APIs. + // + // Minimum server version: 5.18 + PluginHTTP(request *http.Request) *http.Response } var handshake = plugin.HandshakeConfig{ diff --git a/plugin/client_rpc.go b/plugin/client_rpc.go index 66b7fcf489..a902252df8 100644 --- a/plugin/client_rpc.go +++ b/plugin/client_rpc.go @@ -56,11 +56,13 @@ func (p *hooksPlugin) Client(b *plugin.MuxBroker, client *rpc.Client) (interface } type apiRPCClient struct { - client *rpc.Client + client *rpc.Client + muxBroker *plugin.MuxBroker } type apiRPCServer struct { - impl API + impl API + muxBroker *plugin.MuxBroker } // ErrorString is a fallback for sending unregistered implementations of the error interface across @@ -171,7 +173,8 @@ type Z_OnActivateReturns struct { func (g *hooksRPCClient) OnActivate() error { muxId := g.muxBroker.NextId() go g.muxBroker.AcceptAndServe(muxId, &apiRPCServer{ - impl: g.apiImpl, + impl: g.apiImpl, + muxBroker: g.muxBroker, }) _args := &Z_OnActivateArgs{ @@ -192,7 +195,8 @@ func (s *hooksRPCServer) OnActivate(args *Z_OnActivateArgs, returns *Z_OnActivat } s.apiRPCClient = &apiRPCClient{ - client: rpc.NewClient(connection), + client: rpc.NewClient(connection), + muxBroker: s.muxBroker, } if mmplugin, ok := s.impl.(interface { @@ -363,6 +367,76 @@ func (s *hooksRPCServer) ServeHTTP(args *Z_ServeHTTPArgs, returns *struct{}) err return nil } +type Z_PluginHTTPArgs struct { + Request *http.Request + RequestBody []byte +} + +type Z_PluginHTTPReturns struct { + Response *http.Response + ResponseBody []byte +} + +func (g *apiRPCClient) PluginHTTP(request *http.Request) *http.Response { + forwardedRequest := &http.Request{ + Method: request.Method, + URL: request.URL, + Proto: request.Proto, + ProtoMajor: request.ProtoMajor, + ProtoMinor: request.ProtoMinor, + Header: request.Header, + Host: request.Host, + RemoteAddr: request.RemoteAddr, + RequestURI: request.RequestURI, + } + + requestBody, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("RPC call to PluginHTTP API failed: %s", err.Error()) + return nil + } + request.Body.Close() + request.Body = nil + + _args := &Z_PluginHTTPArgs{ + Request: forwardedRequest, + RequestBody: requestBody, + } + + _returns := &Z_PluginHTTPReturns{} + if err := g.client.Call("Plugin.PluginHTTP", _args, _returns); err != nil { + log.Printf("RPC call to PluginHTTP API failed: %s", err.Error()) + return nil + } + + _returns.Response.Body = ioutil.NopCloser(bytes.NewBuffer(_returns.ResponseBody)) + + return _returns.Response +} + +func (s *apiRPCServer) PluginHTTP(args *Z_PluginHTTPArgs, returns *Z_PluginHTTPReturns) error { + args.Request.Body = ioutil.NopCloser(bytes.NewBuffer(args.RequestBody)) + + if hook, ok := s.impl.(interface { + PluginHTTP(request *http.Request) *http.Response + }); ok { + response := hook.PluginHTTP(args.Request) + + responseBody, err := ioutil.ReadAll(response.Body) + if err != nil { + return encodableError(fmt.Errorf("RPC call to PluginHTTP API failed: %s", err.Error())) + } + response.Body.Close() + response.Body = nil + + returns.Response = response + returns.ResponseBody = responseBody + } else { + return encodableError(fmt.Errorf("API PluginHTTP called but not implemented.")) + } + return nil +} + func init() { hookNameToId["FileWillBeUploaded"] = FileWillBeUploadedId } diff --git a/plugin/context.go b/plugin/context.go index 288739e7c3..98e786e8c5 100644 --- a/plugin/context.go +++ b/plugin/context.go @@ -12,4 +12,5 @@ type Context struct { IpAddress string AcceptLanguage string UserAgent string + SourcePluginId string } diff --git a/plugin/interface_generator/main.go b/plugin/interface_generator/main.go index e3a70fbd08..4a80edaa23 100644 --- a/plugin/interface_generator/main.go +++ b/plugin/interface_generator/main.go @@ -392,17 +392,18 @@ func getPluginPackageDir() string { func removeExcluded(info *PluginInterfaceInfo) *PluginInterfaceInfo { toBeExcluded := func(item string) bool { excluded := []string{ - "OnActivate", + "FileWillBeUploaded", "Implemented", "LoadPluginConfiguration", - "ServeHTTP", - "FileWillBeUploaded", - "MessageWillBePosted", - "MessageWillBeUpdated", "LogDebug", + "LogError", "LogInfo", "LogWarn", - "LogError", + "MessageWillBePosted", + "MessageWillBeUpdated", + "OnActivate", + "PluginHTTP", + "ServeHTTP", } for _, exclusion := range excluded { if exclusion == item { diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go index 81c8eb44d3..d7e3525e71 100644 --- a/plugin/plugintest/api.go +++ b/plugin/plugintest/api.go @@ -6,9 +6,11 @@ package plugintest import ( io "io" + http "net/http" + + mock "github.com/stretchr/testify/mock" model "github.com/mattermost/mattermost-server/model" - mock "github.com/stretchr/testify/mock" ) // API is an autogenerated mock type for the API type @@ -2334,6 +2336,22 @@ func (_m *API) PermanentDeleteBot(botUserId string) *model.AppError { return r0 } +// PluginHTTP provides a mock function with given fields: request +func (_m *API) PluginHTTP(request *http.Request) *http.Response { + ret := _m.Called(request) + + var r0 *http.Response + if rf, ok := ret.Get(0).(func(*http.Request) *http.Response); ok { + r0 = rf(request) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*http.Response) + } + } + + return r0 +} + // PublishWebSocketEvent provides a mock function with given fields: event, payload, broadcast func (_m *API) PublishWebSocketEvent(event string, payload map[string]interface{}, broadcast *model.WebsocketBroadcast) { _m.Called(event, payload, broadcast) From 5f91c145761ce375c3aa3386ca044e1d1fad81f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Collin?= Date: Wed, 6 Nov 2019 07:49:28 +0100 Subject: [PATCH 02/15] [MM-18676] Add tag annotations to plugin server API methods (#12701) * [MM-18676] Add tag annotations to plugin server API methods - With mattermost-developer-documentation/pull/383, it will allow to group methods by tag in documentation index * Refine tag annotations in plugin API documentation - Replace "Profile" by "User" - Add additional tags to multi purpose methods - Add "Group" tag - Add tags to new methods --- plugin/api.go | 169 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/plugin/api.go b/plugin/api.go index 7756eec9c8..69f5ca0b8a 100644 --- a/plugin/api.go +++ b/plugin/api.go @@ -20,17 +20,20 @@ type API interface { // LoadPluginConfiguration loads the plugin's configuration. dest should be a pointer to a // struct that the configuration JSON can be unmarshalled to. // + // @tag Plugin // Minimum server version: 5.2 LoadPluginConfiguration(dest interface{}) error // RegisterCommand registers a custom slash command. When the command is triggered, your plugin // can fulfill it via the ExecuteCommand hook. // + // @tag Command // Minimum server version: 5.2 RegisterCommand(command *model.Command) error // UnregisterCommand unregisters a command previously registered via RegisterCommand. // + // @tag Command // Minimum server version: 5.2 UnregisterCommand(teamId, trigger string) error @@ -41,139 +44,168 @@ type API interface { // GetConfig fetches the currently persisted config // + // @tag Configuration // Minimum server version: 5.2 GetConfig() *model.Config // GetUnsanitizedConfig fetches the currently persisted config without removing secrets. // + // @tag Configuration // Minimum server version: 5.16 GetUnsanitizedConfig() *model.Config // SaveConfig sets the given config and persists the changes // + // @tag Configuration // Minimum server version: 5.2 SaveConfig(config *model.Config) *model.AppError // GetPluginConfig fetches the currently persisted config of plugin // + // @tag Plugin // Minimum server version: 5.6 GetPluginConfig() map[string]interface{} // SavePluginConfig sets the given config for plugin and persists the changes // + // @tag Plugin // Minimum server version: 5.6 SavePluginConfig(config map[string]interface{}) *model.AppError // GetBundlePath returns the absolute path where the plugin's bundle was unpacked. // + // @tag Plugin // Minimum server version: 5.10 GetBundlePath() (string, error) // GetLicense returns the current license used by the Mattermost server. Returns nil if the // the server does not have a license. // + // @tag Server // Minimum server version: 5.10 GetLicense() *model.License // GetServerVersion return the current Mattermost server version // + // @tag Server // Minimum server version: 5.4 GetServerVersion() string // GetSystemInstallDate returns the time that Mattermost was first installed and ran. // + // @tag Server // Minimum server version: 5.10 GetSystemInstallDate() (int64, *model.AppError) // GetDiagnosticId returns a unique identifier used by the server for diagnostic reports. // + // @tag Server // Minimum server version: 5.10 GetDiagnosticId() string // CreateUser creates a user. // + // @tag User // Minimum server version: 5.2 CreateUser(user *model.User) (*model.User, *model.AppError) // DeleteUser deletes a user. // + // @tag User // Minimum server version: 5.2 DeleteUser(userId string) *model.AppError // GetUsers a list of users based on search options. // + // @tag User // Minimum server version: 5.10 GetUsers(options *model.UserGetOptions) ([]*model.User, *model.AppError) // GetUser gets a user. // + // @tag User // Minimum server version: 5.2 GetUser(userId string) (*model.User, *model.AppError) // GetUserByEmail gets a user by their email address. // + // @tag User // Minimum server version: 5.2 GetUserByEmail(email string) (*model.User, *model.AppError) // GetUserByUsername gets a user by their username. // + // @tag User // Minimum server version: 5.2 GetUserByUsername(name string) (*model.User, *model.AppError) // GetUsersByUsernames gets users by their usernames. // + // @tag User // Minimum server version: 5.6 GetUsersByUsernames(usernames []string) ([]*model.User, *model.AppError) // GetUsersInTeam gets users in team. // + // @tag User + // @tag Team // Minimum server version: 5.6 GetUsersInTeam(teamId string, page int, perPage int) ([]*model.User, *model.AppError) // GetTeamIcon gets the team icon. // + // @tag Team // Minimum server version: 5.6 GetTeamIcon(teamId string) ([]byte, *model.AppError) // SetTeamIcon sets the team icon. // + // @tag Team // Minimum server version: 5.6 SetTeamIcon(teamId string, data []byte) *model.AppError // RemoveTeamIcon removes the team icon. // + // @tag Team // Minimum server version: 5.6 RemoveTeamIcon(teamId string) *model.AppError // UpdateUser updates a user. // + // @tag User // Minimum server version: 5.2 UpdateUser(user *model.User) (*model.User, *model.AppError) // GetUserStatus will get a user's status. // + // @tag User // Minimum server version: 5.2 GetUserStatus(userId string) (*model.Status, *model.AppError) // GetUserStatusesByIds will return a list of user statuses based on the provided slice of user IDs. // + // @tag User // Minimum server version: 5.2 GetUserStatusesByIds(userIds []string) ([]*model.Status, *model.AppError) // UpdateUserStatus will set a user's status until the user, or another integration/plugin, sets it back to online. // The status parameter can be: "online", "away", "dnd", or "offline". // + // @tag User // Minimum server version: 5.2 UpdateUserStatus(userId, status string) (*model.Status, *model.AppError) // UpdateUserActive deactivates or reactivates an user. // + // @tag User // Minimum server version: 5.8 UpdateUserActive(userId string, active bool) *model.AppError // GetUsersInChannel returns a page of users in a channel. Page counting starts at 0. // The sortBy parameter can be: "username" or "status". // + // @tag User + // @tag Channel // Minimum server version: 5.6 GetUsersInChannel(channelId, sortBy string, page, perPage int) ([]*model.User, *model.AppError) @@ -182,312 +214,403 @@ type API interface { // Returns a map with attribute names as keys and the user's attributes as values. // Requires an enterprise license, LDAP to be configured and for the user to use LDAP as an authentication method. // + // @tag User // Minimum server version: 5.3 GetLDAPUserAttributes(userId string, attributes []string) (map[string]string, *model.AppError) // CreateTeam creates a team. // + // @tag Team // Minimum server version: 5.2 CreateTeam(team *model.Team) (*model.Team, *model.AppError) // DeleteTeam deletes a team. // + // @tag Team // Minimum server version: 5.2 DeleteTeam(teamId string) *model.AppError // GetTeam gets all teams. // + // @tag Team // Minimum server version: 5.2 GetTeams() ([]*model.Team, *model.AppError) // GetTeam gets a team. // + // @tag Team // Minimum server version: 5.2 GetTeam(teamId string) (*model.Team, *model.AppError) // GetTeamByName gets a team by its name. // + // @tag Team // Minimum server version: 5.2 GetTeamByName(name string) (*model.Team, *model.AppError) // GetTeamsUnreadForUser gets the unread message and mention counts for each team to which the given user belongs. // + // @tag Team + // @tag User // Minimum server version: 5.6 GetTeamsUnreadForUser(userId string) ([]*model.TeamUnread, *model.AppError) // UpdateTeam updates a team. // + // @tag Team // Minimum server version: 5.2 UpdateTeam(team *model.Team) (*model.Team, *model.AppError) // SearchTeams search a team. // + // @tag Team // Minimum server version: 5.8 SearchTeams(term string) ([]*model.Team, *model.AppError) // GetTeamsForUser returns list of teams of given user ID. // + // @tag Team + // @tag User // Minimum server version: 5.6 GetTeamsForUser(userId string) ([]*model.Team, *model.AppError) // CreateTeamMember creates a team membership. // + // @tag Team + // @tag User // Minimum server version: 5.2 CreateTeamMember(teamId, userId string) (*model.TeamMember, *model.AppError) - // CreateTeamMember creates a team membership for all provided user ids. + // CreateTeamMembers creates a team membership for all provided user ids. // + // @tag Team + // @tag User // Minimum server version: 5.2 CreateTeamMembers(teamId string, userIds []string, requestorId string) ([]*model.TeamMember, *model.AppError) // DeleteTeamMember deletes a team membership. // + // @tag Team + // @tag User // Minimum server version: 5.2 DeleteTeamMember(teamId, userId, requestorId string) *model.AppError // GetTeamMembers returns the memberships of a specific team. // + // @tag Team + // @tag User // Minimum server version: 5.2 GetTeamMembers(teamId string, page, perPage int) ([]*model.TeamMember, *model.AppError) // GetTeamMember returns a specific membership. // + // @tag Team + // @tag User // Minimum server version: 5.2 GetTeamMember(teamId, userId string) (*model.TeamMember, *model.AppError) // GetTeamMembersForUser returns all team memberships for a user. // + // @tag Team + // @tag User // Minimum server version: 5.10 GetTeamMembersForUser(userId string, page int, perPage int) ([]*model.TeamMember, *model.AppError) // UpdateTeamMemberRoles updates the role for a team membership. // + // @tag Team + // @tag User // Minimum server version: 5.2 UpdateTeamMemberRoles(teamId, userId, newRoles string) (*model.TeamMember, *model.AppError) // CreateChannel creates a channel. // + // @tag Channel // Minimum server version: 5.2 CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) // DeleteChannel deletes a channel. // + // @tag Channel // Minimum server version: 5.2 DeleteChannel(channelId string) *model.AppError // GetPublicChannelsForTeam gets a list of all channels. // + // @tag Channel + // @tag Team // Minimum server version: 5.2 GetPublicChannelsForTeam(teamId string, page, perPage int) ([]*model.Channel, *model.AppError) // GetChannel gets a channel. // + // @tag Channel // Minimum server version: 5.2 GetChannel(channelId string) (*model.Channel, *model.AppError) // GetChannelByName gets a channel by its name, given a team id. // + // @tag Channel // Minimum server version: 5.2 GetChannelByName(teamId, name string, includeDeleted bool) (*model.Channel, *model.AppError) // GetChannelByNameForTeamName gets a channel by its name, given a team name. // + // @tag Channel + // @tag Team // Minimum server version: 5.2 GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError) // GetChannelsForTeamForUser gets a list of channels for given user ID in given team ID. // + // @tag Channel + // @tag Team + // @tag User // Minimum server version: 5.6 GetChannelsForTeamForUser(teamId, userId string, includeDeleted bool) ([]*model.Channel, *model.AppError) // GetChannelStats gets statistics for a channel. // + // @tag Channel // Minimum server version: 5.6 GetChannelStats(channelId string) (*model.ChannelStats, *model.AppError) // GetDirectChannel gets a direct message channel. // If the channel does not exist it will create it. // + // @tag Channel + // @tag User // Minimum server version: 5.2 GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) // GetGroupChannel gets a group message channel. // If the channel does not exist it will create it. // + // @tag Channel + // @tag User // Minimum server version: 5.2 GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) // UpdateChannel updates a channel. // + // @tag Channel // Minimum server version: 5.2 UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) // SearchChannels returns the channels on a team matching the provided search term. // + // @tag Channel // Minimum server version: 5.6 SearchChannels(teamId string, term string) ([]*model.Channel, *model.AppError) // SearchUsers returns a list of users based on some search criteria. // + // @tag User // Minimum server version: 5.6 SearchUsers(search *model.UserSearch) ([]*model.User, *model.AppError) // SearchPostsInTeam returns a list of posts in a specific team that match the given params. // + // @tag Post + // @tag Team // Minimum server version: 5.10 SearchPostsInTeam(teamId string, paramsList []*model.SearchParams) ([]*model.Post, *model.AppError) // AddChannelMember joins a user to a channel (as if they joined themselves) // This means the user will not receive notifications for joining the channel. // + // @tag Channel + // @tag User // Minimum server version: 5.2 AddChannelMember(channelId, userId string) (*model.ChannelMember, *model.AppError) // AddUserToChannel adds a user to a channel as if the specified user had invited them. // This means the user will receive the regular notifications for being added to the channel. // + // @tag User + // @tag Channel // Minimum server version: 5.18 AddUserToChannel(channelId, userId, asUserId string) (*model.ChannelMember, *model.AppError) // GetChannelMember gets a channel membership for a user. // + // @tag Channel + // @tag User // Minimum server version: 5.2 GetChannelMember(channelId, userId string) (*model.ChannelMember, *model.AppError) // GetChannelMembers gets a channel membership for all users. // + // @tag Channel + // @tag User // Minimum server version: 5.6 GetChannelMembers(channelId string, page, perPage int) (*model.ChannelMembers, *model.AppError) // GetChannelMembersByIds gets a channel membership for a particular User // + // @tag Channel + // @tag User // Minimum server version: 5.6 GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) // GetChannelMembersForUser returns all channel memberships on a team for a user. // + // @tag Channel + // @tag User // Minimum server version: 5.10 GetChannelMembersForUser(teamId, userId string, page, perPage int) ([]*model.ChannelMember, *model.AppError) // UpdateChannelMemberRoles updates a user's roles for a channel. // + // @tag Channel + // @tag User // Minimum server version: 5.2 UpdateChannelMemberRoles(channelId, userId, newRoles string) (*model.ChannelMember, *model.AppError) // UpdateChannelMemberNotifications updates a user's notification properties for a channel. // + // @tag Channel + // @tag User // Minimum server version: 5.2 UpdateChannelMemberNotifications(channelId, userId string, notifications map[string]string) (*model.ChannelMember, *model.AppError) // GetGroup gets a group by ID. // + // @tag Group // Minimum server version: 5.18 GetGroup(groupId string) (*model.Group, *model.AppError) // GetGroupByName gets a group by name. // + // @tag Group // Minimum server version: 5.18 GetGroupByName(name string) (*model.Group, *model.AppError) // GetGroupsForUser gets the groups a user is in. // + // @tag Group + // @tag User // Minimum server version: 5.18 GetGroupsForUser(userId string) ([]*model.Group, *model.AppError) // DeleteChannelMember deletes a channel membership for a user. // + // @tag Channel + // @tag User // Minimum server version: 5.2 DeleteChannelMember(channelId, userId string) *model.AppError // CreatePost creates a post. // + // @tag Post // Minimum server version: 5.2 CreatePost(post *model.Post) (*model.Post, *model.AppError) // AddReaction add a reaction to a post. // + // @tag Post // Minimum server version: 5.3 AddReaction(reaction *model.Reaction) (*model.Reaction, *model.AppError) // RemoveReaction remove a reaction from a post. // + // @tag Post // Minimum server version: 5.3 RemoveReaction(reaction *model.Reaction) *model.AppError // GetReaction get the reactions of a post. // + // @tag Post // Minimum server version: 5.3 GetReactions(postId string) ([]*model.Reaction, *model.AppError) // SendEphemeralPost creates an ephemeral post. // + // @tag Post // Minimum server version: 5.2 SendEphemeralPost(userId string, post *model.Post) *model.Post // UpdateEphemeralPost updates an ephemeral message previously sent to the user. // EXPERIMENTAL: This API is experimental and can be changed without advance notice. // + // @tag Post // Minimum server version: 5.2 UpdateEphemeralPost(userId string, post *model.Post) *model.Post // DeleteEphemeralPost deletes an ephemeral message previously sent to the user. // EXPERIMENTAL: This API is experimental and can be changed without advance notice. // + // @tag Post // Minimum server version: 5.2 DeleteEphemeralPost(userId, postId string) // DeletePost deletes a post. // + // @tag Post // Minimum server version: 5.2 DeletePost(postId string) *model.AppError // GetPostThread gets a post with all the other posts in the same thread. // + // @tag Post // Minimum server version: 5.6 GetPostThread(postId string) (*model.PostList, *model.AppError) // GetPost gets a post. // + // @tag Post // Minimum server version: 5.2 GetPost(postId string) (*model.Post, *model.AppError) // GetPostsSince gets posts created after a specified time as Unix time in milliseconds. // + // @tag Post + // @tag Channel // Minimum server version: 5.6 GetPostsSince(channelId string, time int64) (*model.PostList, *model.AppError) // GetPostsAfter gets a page of posts that were posted after the post provided. // + // @tag Post + // @tag Channel // Minimum server version: 5.6 GetPostsAfter(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) // GetPostsBefore gets a page of posts that were posted before the post provided. // + // @tag Post + // @tag Channel // Minimum server version: 5.6 GetPostsBefore(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) // GetPostsForChannel gets a list of posts for a channel. // + // @tag Post + // @tag Channel // Minimum server version: 5.6 GetPostsForChannel(channelId string, page, perPage int) (*model.PostList, *model.AppError) // GetTeamStats gets a team's statistics // + // @tag Team // Minimum server version: 5.8 GetTeamStats(teamId string) (*model.TeamStats, *model.AppError) // UpdatePost updates a post. // + // @tag Post // Minimum server version: 5.2 UpdatePost(post *model.Post) (*model.Post, *model.AppError) // GetProfileImage gets user's profile image. // + // @tag User // Minimum server version: 5.6 GetProfileImage(userId string) ([]byte, *model.AppError) // SetProfileImage sets a user's profile image. // + // @tag User // Minimum server version: 5.6 SetProfileImage(userId string, data []byte) *model.AppError @@ -495,16 +618,19 @@ type API interface { // // The sortBy parameter can be: "name". // + // @tag Emoji // Minimum server version: 5.6 GetEmojiList(sortBy string, page, perPage int) ([]*model.Emoji, *model.AppError) // GetEmojiByName gets an emoji by it's name. // + // @tag Emoji // Minimum server version: 5.6 GetEmojiByName(name string) (*model.Emoji, *model.AppError) // GetEmoji returns a custom emoji based on the emojiId string. // + // @tag Emoji // Minimum server version: 5.6 GetEmoji(emojiId string) (*model.Emoji, *model.AppError) @@ -515,36 +641,45 @@ type API interface { // to CreatePost. Use this API to duplicate a post and its file attachments without // actually duplicating the uploaded files. // + // @tag File + // @tag User // Minimum server version: 5.2 CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError) // GetFileInfo gets a File Info for a specific fileId // + // @tag File // Minimum server version: 5.3 GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) // GetFile gets content of a file by it's ID // + // @tag File // Minimum server version: 5.8 GetFile(fileId string) ([]byte, *model.AppError) // GetFileLink gets the public link to a file by fileId. // + // @tag File // Minimum server version: 5.6 GetFileLink(fileId string) (string, *model.AppError) // ReadFileAtPath reads the file from the backend for a specific path // + // @tag File // Minimum server version: 5.3 ReadFile(path string) ([]byte, *model.AppError) // GetEmojiImage returns the emoji image. // + // @tag Emoji // Minimum server version: 5.6 GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) // UploadFile will upload a file to a channel using a multipart request, to be later attached to a post. // + // @tag File + // @tag Channel // Minimum server version: 5.6 UploadFile(data []byte, channelId string, filename string) (*model.FileInfo, *model.AppError) @@ -559,32 +694,38 @@ type API interface { // GetPlugins will return a list of plugin manifests for currently active plugins. // + // @tag Plugin // Minimum server version: 5.6 GetPlugins() ([]*model.Manifest, *model.AppError) // EnablePlugin will enable an plugin installed. // + // @tag Plugin // Minimum server version: 5.6 EnablePlugin(id string) *model.AppError // DisablePlugin will disable an enabled plugin. // + // @tag Plugin // Minimum server version: 5.6 DisablePlugin(id string) *model.AppError // RemovePlugin will disable and delete a plugin. // + // @tag Plugin // Minimum server version: 5.6 RemovePlugin(id string) *model.AppError // GetPluginStatus will return the status of a plugin. // + // @tag Plugin // Minimum server version: 5.6 GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) // InstallPlugin will upload another plugin with tar.gz file. // Previous version will be replaced on replace true. // + // @tag Plugin // Minimum server version: 5.18 InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) @@ -593,6 +734,7 @@ type API interface { // KVSet stores a key-value pair, unique per plugin. // Provided helper functions and internal plugin code will use the prefix `mmi_` before keys. Do not use this prefix. // + // @tag KeyValueStore // Minimum server version: 5.2 KVSet(key string, value []byte) *model.AppError @@ -602,6 +744,7 @@ type API interface { // Returns (false, nil) if current value != oldValue or key already exists when inserting // Returns (true, nil) if current value == oldValue or new key is inserted // + // @tag KeyValueStore // Minimum server version: 5.12 KVCompareAndSet(key string, oldValue, newValue []byte) (bool, *model.AppError) @@ -610,6 +753,7 @@ type API interface { // Returns (false, nil) if current value != oldValue or key does not exist when deleting // Returns (true, nil) if current value == oldValue and the key was deleted // + // @tag KeyValueStore // Minimum server version: 5.16 KVCompareAndDelete(key string, oldValue []byte) (bool, *model.AppError) @@ -624,26 +768,31 @@ type API interface { // KVSet stores a key-value pair with an expiry time, unique per plugin. // + // @tag KeyValueStore // Minimum server version: 5.6 KVSetWithExpiry(key string, value []byte, expireInSeconds int64) *model.AppError // KVGet retrieves a value based on the key, unique per plugin. Returns nil for non-existent keys. // + // @tag KeyValueStore // Minimum server version: 5.2 KVGet(key string) ([]byte, *model.AppError) // KVDelete removes a key-value pair, unique per plugin. Returns nil for non-existent keys. // + // @tag KeyValueStore // Minimum server version: 5.2 KVDelete(key string) *model.AppError // KVDeleteAll removes all key-value pairs for a plugin. // + // @tag KeyValueStore // Minimum server version: 5.6 KVDeleteAll() *model.AppError // KVList lists all keys for a plugin. // + // @tag KeyValueStore // Minimum server version: 5.6 KVList(page, perPage int) ([]string, *model.AppError) @@ -657,16 +806,21 @@ type API interface { // HasPermissionTo check if the user has the permission at system scope. // + // @tag User // Minimum server version: 5.3 HasPermissionTo(userId string, permission *model.Permission) bool // HasPermissionToTeam check if the user has the permission at team scope. // + // @tag User + // @tag Team // Minimum server version: 5.3 HasPermissionToTeam(userId, teamId string, permission *model.Permission) bool // HasPermissionToChannel check if the user has the permission at channel scope. // + // @tag User + // @tag Channel // Minimum server version: 5.3 HasPermissionToChannel(userId, channelId string, permission *model.Permission) bool @@ -674,6 +828,7 @@ type API interface { // Appropriate context such as the plugin name will already be added as fields so plugins // do not need to add that info. // + // @tag Logging // Minimum server version: 5.2 LogDebug(msg string, keyValuePairs ...interface{}) @@ -681,6 +836,7 @@ type API interface { // Appropriate context such as the plugin name will already be added as fields so plugins // do not need to add that info. // + // @tag Logging // Minimum server version: 5.2 LogInfo(msg string, keyValuePairs ...interface{}) @@ -688,6 +844,7 @@ type API interface { // Appropriate context such as the plugin name will already be added as fields so plugins // do not need to add that info. // + // @tag Logging // Minimum server version: 5.2 LogError(msg string, keyValuePairs ...interface{}) @@ -695,6 +852,7 @@ type API interface { // Appropriate context such as the plugin name will already be added as fields so plugins // do not need to add that info. // + // @tag Logging // Minimum server version: 5.2 LogWarn(msg string, keyValuePairs ...interface{}) @@ -705,47 +863,56 @@ type API interface { // CreateBot creates the given bot and corresponding user. // + // @tag Bot // Minimum server version: 5.10 CreateBot(bot *model.Bot) (*model.Bot, *model.AppError) // PatchBot applies the given patch to the bot and corresponding user. // + // @tag Bot // Minimum server version: 5.10 PatchBot(botUserId string, botPatch *model.BotPatch) (*model.Bot, *model.AppError) // GetBot returns the given bot. // + // @tag Bot // Minimum server version: 5.10 GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) // GetBots returns the requested page of bots. // + // @tag Bot // Minimum server version: 5.10 GetBots(options *model.BotGetOptions) ([]*model.Bot, *model.AppError) // UpdateBotActive marks a bot as active or inactive, along with its corresponding user. // + // @tag Bot // Minimum server version: 5.10 UpdateBotActive(botUserId string, active bool) (*model.Bot, *model.AppError) // PermanentDeleteBot permanently deletes a bot and its corresponding user. // + // @tag Bot // Minimum server version: 5.10 PermanentDeleteBot(botUserId string) *model.AppError // GetBotIconImage gets LHS bot icon image. // + // @tag Bot // Minimum server version: 5.14 GetBotIconImage(botUserId string) ([]byte, *model.AppError) // SetBotIconImage sets LHS bot icon image. // Icon image must be SVG format, all other formats are rejected. // + // @tag Bot // Minimum server version: 5.14 SetBotIconImage(botUserId string, data []byte) *model.AppError // DeleteBotIconImage deletes LHS bot icon image. // + // @tag Bot // Minimum server version: 5.14 DeleteBotIconImage(botUserId string) *model.AppError From 516017d3c9bebc029bd720dd7606e8e1619b023e Mon Sep 17 00:00:00 2001 From: scott lee davis Date: Wed, 6 Nov 2019 02:39:59 -0800 Subject: [PATCH 03/15] MM-18068 Route integration actions to plugins without hitting the network (#12156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * plugin Action requests doesn’t make external request * import formatting fix * changes requested in review by @lieut-data * wip * wip pointer * added review changes requested by @lieut-data * condense relative plugin url tests * changes based on review by @crspeller * gofmt --- app/integration_action.go | 80 +++++++++++- app/integration_action_test.go | 230 ++++++++++++++++++++++++++++++++- 2 files changed, 302 insertions(+), 8 deletions(-) diff --git a/app/integration_action.go b/app/integration_action.go index 5c95cf6377..628daba851 100644 --- a/app/integration_action.go +++ b/app/integration_action.go @@ -21,6 +21,8 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/gorilla/mux" + "io/ioutil" "net/http" "net/url" "path" @@ -242,19 +244,16 @@ func (a *App) DoPostActionWithCookie(postId, actionId, userId, selectedOption st // Perform an HTTP POST request to an integration's action endpoint. // Caller must consume and close returned http.Response as necessary. +// For internal requests, requests are routed directly to a plugin ServerHTTP hook func (a *App) DoActionRequest(rawURL string, body []byte) (*http.Response, *model.AppError) { inURL, err := url.Parse(rawURL) if err != nil { return nil, model.NewAppError("DoActionRequest", "api.post.do_action.action_integration.app_error", nil, err.Error(), http.StatusBadRequest) } - siteURL, _ := url.Parse(*a.Config().ServiceSettings.SiteURL) rawURLPath := path.Clean(rawURL) - if siteURL != nil && (strings.HasPrefix(rawURLPath, "/plugins/") || strings.HasPrefix(rawURLPath, "plugins/")) { - inURL.Scheme = siteURL.Scheme - inURL.Host = siteURL.Host - inURL.Path = path.Join("/", siteURL.Path, rawURLPath) - rawURL = inURL.String() + if strings.HasPrefix(rawURLPath, "/plugins/") || strings.HasPrefix(rawURLPath, "plugins/") { + return a.DoLocalRequest(rawURLPath, body) } req, err := http.NewRequest("POST", rawURL, bytes.NewReader(body)) @@ -267,6 +266,7 @@ func (a *App) DoActionRequest(rawURL string, body []byte) (*http.Response, *mode // Allow access to plugin routes for action buttons var httpClient *http.Client subpath, _ := utils.GetSubpathFromConfig(a.Config()) + siteURL, _ := url.Parse(*a.Config().ServiceSettings.SiteURL) if (inURL.Hostname() == "localhost" || inURL.Hostname() == "127.0.0.1" || inURL.Hostname() == siteURL.Hostname()) && strings.HasPrefix(inURL.Path, path.Join(subpath, "plugins")) { req.Header.Set(model.HEADER_AUTH, "Bearer "+a.Session.Token) httpClient = a.HTTPService.MakeClient(true) @@ -286,6 +286,74 @@ func (a *App) DoActionRequest(rawURL string, body []byte) (*http.Response, *mode return resp, nil } +type LocalResponseWriter struct { + data []byte + headers http.Header + status int +} + +func (w *LocalResponseWriter) Header() http.Header { + if w.headers == nil { + w.headers = make(http.Header) + } + return w.headers +} + +func (w *LocalResponseWriter) Write(bytes []byte) (int, error) { + w.data = make([]byte, len(bytes)) + copy(w.data, bytes) + return len(w.data), nil +} + +func (w *LocalResponseWriter) WriteHeader(statusCode int) { + w.status = statusCode +} + +func (a *App) DoLocalRequest(rawURL string, body []byte) (*http.Response, *model.AppError) { + rawURL = strings.TrimPrefix(rawURL, "/") + inURL, err := url.Parse(rawURL) + if err != nil { + return nil, model.NewAppError("DoActionRequest", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest) + } + result := strings.Split(inURL.Path, "/") + if len(result) < 2 { + return nil, model.NewAppError("DoActionRequest", "api.post.do_action.action_integration.app_error", nil, "err=Unable to find pluginId", http.StatusBadRequest) + } + if result[0] != "plugins" { + return nil, model.NewAppError("DoActionRequest", "api.post.do_action.action_integration.app_error", nil, "err=plugins not in path", http.StatusBadRequest) + } + pluginId := result[1] + + path := strings.TrimPrefix(inURL.Path, "plugins/"+pluginId) + + w := &LocalResponseWriter{} + r, err := http.NewRequest("POST", path, bytes.NewReader(body)) + if err != nil { + return nil, model.NewAppError("DoActionRequest", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest) + } + r.Header.Set("Mattermost-User-Id", a.Session.UserId) + r.Header.Set(model.HEADER_AUTH, "Bearer "+a.Session.Token) + params := make(map[string]string) + params["plugin_id"] = pluginId + r = mux.SetURLVars(r, params) + + a.ServePluginRequest(w, r) + + resp := &http.Response{ + StatusCode: w.status, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: w.headers, + Body: ioutil.NopCloser(bytes.NewReader(w.data)), + } + if resp.StatusCode == 0 { + resp.StatusCode = http.StatusOK + } + + return resp, nil +} + func (a *App) OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError { clientTriggerId, userId, err := request.DecodeAndVerifyTriggerId(a.AsymmetricSigningKey()) if err != nil { diff --git a/app/integration_action_test.go b/app/integration_action_test.go index fb5727f7ae..420cca36ae 100644 --- a/app/integration_action_test.go +++ b/app/integration_action_test.go @@ -443,6 +443,37 @@ func TestSubmitInteractiveDialog(t *testing.T) { })) defer ts.Close() + setupPluginApiTest(t, + ` + package main + + import ( + "net/http" + "github.com/mattermost/mattermost-server/plugin" + "github.com/mattermost/mattermost-server/model" + ) + + type MyPlugin struct { + plugin.MattermostPlugin + } + + func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + response := &model.SubmitDialogResponse{ + Errors: map[string]string{"name1": "some error"}, + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write(response.ToJson()) + } + + func main() { + plugin.ClientMain(&MyPlugin{}) + } + `, `{"id": "myplugin", "backend": {"executable": "backend.exe"}}`, "myplugin", th.App) + + hooks, err2 := th.App.GetPluginsEnvironment().HooksForPlugin("myplugin") + require.Nil(t, err2) + require.NotNil(t, hooks) + submit.URL = ts.URL resp, err := th.App.SubmitInteractiveDialog(submit) @@ -601,7 +632,8 @@ func TestPostActionRelativeURL(t *testing.T) { require.NotEmpty(t, attachments[0].Actions[0].Id) _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") - require.Nil(t, err) + require.NotNil(t, err) + }) t.Run("valid (but dirty) relative URL with SiteURL set", func(t *testing.T) { @@ -641,7 +673,7 @@ func TestPostActionRelativeURL(t *testing.T) { require.NotEmpty(t, attachments[0].Actions[0].Id) _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") - require.Nil(t, err) + require.NotNil(t, err) }) t.Run("valid relative URL with SiteURL set and no leading slash", func(t *testing.T) { @@ -680,6 +712,200 @@ func TestPostActionRelativeURL(t *testing.T) { require.NotEmpty(t, attachments[0].Actions) require.NotEmpty(t, attachments[0].Actions[0].Id) + _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") + require.NotNil(t, err) + }) +} + +func TestPostActionRelativePluginURL(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + setupPluginApiTest(t, + ` + package main + + import ( + "net/http" + "github.com/mattermost/mattermost-server/plugin" + "github.com/mattermost/mattermost-server/model" + ) + + type MyPlugin struct { + plugin.MattermostPlugin + } + + func (p *MyPlugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { + response := &model.PostActionIntegrationResponse{} + w.WriteHeader(http.StatusOK) + _, _ = w.Write(response.ToJson()) + } + + func main() { + plugin.ClientMain(&MyPlugin{}) + } + `, `{"id": "myplugin", "backend": {"executable": "backend.exe"}}`, "myplugin", th.App) + + hooks, err2 := th.App.GetPluginsEnvironment().HooksForPlugin("myplugin") + require.Nil(t, err2) + require.NotNil(t, hooks) + + t.Run("invalid relative URL", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "" + *cfg.ServiceSettings.SiteURL = "" + }) + + interactivePost := model.Post{ + Message: "Interactive post", + ChannelId: th.BasicChannel.Id, + PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "attachments": []*model.SlackAttachment{ + { + Text: "hello", + Actions: []*model.PostAction{ + { + Integration: &model.PostActionIntegration{ + URL: "/notaplugin/some/path", + }, + Name: "action", + Type: "some_type", + }, + }, + }, + }, + }, + } + + post, err := th.App.CreatePostAsUser(&interactivePost, "") + require.Nil(t, err) + attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) + require.True(t, ok) + require.NotEmpty(t, attachments[0].Actions) + require.NotEmpty(t, attachments[0].Actions[0].Id) + + _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") + require.NotNil(t, err) + }) + + t.Run("valid relative URL", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "" + *cfg.ServiceSettings.SiteURL = "" + }) + + interactivePost := model.Post{ + Message: "Interactive post", + ChannelId: th.BasicChannel.Id, + PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "attachments": []*model.SlackAttachment{ + { + Text: "hello", + Actions: []*model.PostAction{ + { + Integration: &model.PostActionIntegration{ + URL: "/plugins/myplugin/myaction", + }, + Name: "action", + Type: "some_type", + }, + }, + }, + }, + }, + } + + post, err := th.App.CreatePostAsUser(&interactivePost, "") + require.Nil(t, err) + attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) + require.True(t, ok) + require.NotEmpty(t, attachments[0].Actions) + require.NotEmpty(t, attachments[0].Actions[0].Id) + + _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") + require.Nil(t, err) + }) + + t.Run("valid (but dirty) relative URL", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "" + *cfg.ServiceSettings.SiteURL = "" + }) + + interactivePost := model.Post{ + Message: "Interactive post", + ChannelId: th.BasicChannel.Id, + PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "attachments": []*model.SlackAttachment{ + { + Text: "hello", + Actions: []*model.PostAction{ + { + Integration: &model.PostActionIntegration{ + URL: "//plugins/myplugin///myaction", + }, + Name: "action", + Type: "some_type", + }, + }, + }, + }, + }, + } + + post, err := th.App.CreatePostAsUser(&interactivePost, "") + require.Nil(t, err) + attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) + require.True(t, ok) + require.NotEmpty(t, attachments[0].Actions) + require.NotEmpty(t, attachments[0].Actions[0].Id) + + _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") + require.Nil(t, err) + }) + + t.Run("valid relative URL and no leading slash", func(t *testing.T) { + th.App.UpdateConfig(func(cfg *model.Config) { + *cfg.ServiceSettings.AllowedUntrustedInternalConnections = "" + *cfg.ServiceSettings.SiteURL = "" + }) + + interactivePost := model.Post{ + Message: "Interactive post", + ChannelId: th.BasicChannel.Id, + PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), + UserId: th.BasicUser.Id, + Props: model.StringInterface{ + "attachments": []*model.SlackAttachment{ + { + Text: "hello", + Actions: []*model.PostAction{ + { + Integration: &model.PostActionIntegration{ + URL: "plugins/myplugin/myaction", + }, + Name: "action", + Type: "some_type", + }, + }, + }, + }, + }, + } + + post, err := th.App.CreatePostAsUser(&interactivePost, "") + require.Nil(t, err) + attachments, ok := post.Props["attachments"].([]*model.SlackAttachment) + require.True(t, ok) + require.NotEmpty(t, attachments[0].Actions) + require.NotEmpty(t, attachments[0].Actions[0].Id) + _, err = th.App.DoPostAction(post.Id, attachments[0].Actions[0].Id, th.BasicUser.Id, "") require.Nil(t, err) }) From a85c653f879a2f3edf0c643d4432005ee2b7e9da Mon Sep 17 00:00:00 2001 From: Shobhit Gupta Date: Wed, 6 Nov 2019 03:37:36 -0800 Subject: [PATCH 04/15] =?UTF-8?q?Migrate=20tests=20from=20"store/storetest?= =?UTF-8?q?/emoji=5Fstore.go"=20to=20use=20tes=E2=80=A6=20(#12913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- store/storetest/emoji_store.go | 125 ++++++++++++++------------------- 1 file changed, 51 insertions(+), 74 deletions(-) diff --git a/store/storetest/emoji_store.go b/store/storetest/emoji_store.go index c0adfd0867..4479aa18dd 100644 --- a/store/storetest/emoji_store.go +++ b/store/storetest/emoji_store.go @@ -30,33 +30,26 @@ func testEmojiSaveDelete(t *testing.T, ss store.Store) { Name: model.NewId(), } - if _, err := ss.Emoji().Save(emoji1); err != nil { - t.Fatal(err) - } + _, err := ss.Emoji().Save(emoji1) + require.Nil(t, err) - if len(emoji1.Id) != 26 { - t.Fatal("should've set id for emoji") - } + assert.Len(t, emoji1.Id, 26, "should've set id for emoji") emoji2 := model.Emoji{ CreatorId: model.NewId(), Name: emoji1.Name, } - if _, err := ss.Emoji().Save(&emoji2); err == nil { - t.Fatal("shouldn't be able to save emoji with duplicate name") - } + _, err = ss.Emoji().Save(&emoji2) + require.NotNil(t, err, "shouldn't be able to save emoji with duplicate name") - if err := ss.Emoji().Delete(emoji1, time.Now().Unix()); err != nil { - t.Fatal(err) - } + err = ss.Emoji().Delete(emoji1, time.Now().Unix()) + require.Nil(t, err) - if _, err := ss.Emoji().Save(&emoji2); err != nil { - t.Fatal("should be able to save emoji with duplicate name now that original has been deleted", err) - } + _, err = ss.Emoji().Save(&emoji2) + require.Nil(t, err, "should be able to save emoji with duplicate name now that original has been deleted") - if err := ss.Emoji().Delete(&emoji2, time.Now().Unix()+1); err != nil { - t.Fatal(err) - } + err = ss.Emoji().Delete(&emoji2, time.Now().Unix()+1) + require.Nil(t, err) } func testEmojiGet(t *testing.T, ss store.Store) { @@ -88,15 +81,13 @@ func testEmojiGet(t *testing.T, ss store.Store) { }() for _, emoji := range emojis { - if _, err := ss.Emoji().Get(emoji.Id, false); err != nil { - t.Fatalf("failed to get emoji with id %v: %v", emoji.Id, err) - } + _, err := ss.Emoji().Get(emoji.Id, false) + require.Nilf(t, err, "failed to get emoji with id %v", emoji.Id) } for _, emoji := range emojis { - if _, err := ss.Emoji().Get(emoji.Id, true); err != nil { - t.Fatalf("failed to get emoji with id %v: %v", emoji.Id, err) - } + _, err := ss.Emoji().Get(emoji.Id, true) + require.Nilf(t, err, "failed to get emoji with id %v", emoji.Id) } } @@ -184,9 +175,8 @@ func testEmojiGetByName(t *testing.T, ss store.Store) { }() for _, emoji := range emojis { - if _, err := ss.Emoji().GetByName(emoji.Name, true); err != nil { - t.Fatalf("failed to get emoji with name %v: %v", emoji.Name, err) - } + _, err := ss.Emoji().GetByName(emoji.Name, true) + require.Nilf(t, err, "failed to get emoji with name %v", emoji.Name) } } @@ -219,35 +209,28 @@ func testEmojiGetMultipleByName(t *testing.T, ss store.Store) { }() t.Run("one emoji", func(t *testing.T) { - if received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name}); err != nil { - t.Fatal("could not get emoji", err) - } else if len(received) != 1 || *received[0] != emojis[0] { - t.Fatal("got incorrect emoji") - } + received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name}) + require.Nilf(t, err, "could not get emoji") + require.Len(t, received, 1, "got incorrect emoji") + require.Equal(t, *received[0], emojis[0], "got incorrect emoji") }) t.Run("multiple emojis", func(t *testing.T) { - if received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name, emojis[1].Name, emojis[2].Name}); err != nil { - t.Fatal("could not get emojis", err) - } else if len(received) != 3 { - t.Fatal("got incorrect emojis") - } + received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name, emojis[1].Name, emojis[2].Name}) + require.Nil(t, err, "could not get emojis") + require.Len(t, received, 3, "got incorrect emojis") }) t.Run("one nonexistent emoji", func(t *testing.T) { - if received, err := ss.Emoji().GetMultipleByName([]string{"ab"}); err != nil { - t.Fatal("could not get emoji", err) - } else if len(received) != 0 { - t.Fatal("got incorrect emoji") - } + received, err := ss.Emoji().GetMultipleByName([]string{"ab"}) + require.Nilf(t, err, "%v, could not get emoji", err) + require.Len(t, received, 0, "got incorrect emoji") }) t.Run("multiple emojis with nonexistent names", func(t *testing.T) { - if received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name, emojis[1].Name, emojis[2].Name, "abcd", "1234"}); err != nil { - t.Fatal("could not get emojis", err) - } else if len(received) != 3 { - t.Fatal("got incorrect emojis") - } + received, err := ss.Emoji().GetMultipleByName([]string{emojis[0].Name, emojis[1].Name, emojis[2].Name, "abcd", "1234"}) + require.Nil(t, err, "could not get emojis") + require.Len(t, received, 3, "got incorrect emojis") }) } @@ -292,9 +275,7 @@ func testEmojiGetList(t *testing.T, ss store.Store) { } } - if !found { - t.Fatalf("failed to get emoji with id %v", emoji.Id) - } + require.Truef(t, found, "failed to get emoji with id %v", emoji.Id) } } @@ -347,38 +328,34 @@ func testEmojiSearch(t *testing.T, ss store.Store) { shouldFind := []bool{true, false, false, false} - if result, err := ss.Emoji().Search("blargh", true, 100); err != nil { - t.Fatal(err) - } else { - for i, emoji := range emojis { - found := false + result, err := ss.Emoji().Search("blargh", true, 100) + require.Nil(t, err) + for i, emoji := range emojis { + found := false - for _, savedEmoji := range result { - if emoji.Id == savedEmoji.Id { - found = true - break - } + for _, savedEmoji := range result { + if emoji.Id == savedEmoji.Id { + found = true + break } - - assert.Equal(t, shouldFind[i], found, emoji.Name) } + + assert.Equal(t, shouldFind[i], found, emoji.Name) } shouldFind = []bool{true, true, true, false} - if result, err := ss.Emoji().Search("blargh", false, 100); err != nil { - t.Fatal(err) - } else { - for i, emoji := range emojis { - found := false + result, err = ss.Emoji().Search("blargh", false, 100) + require.Nil(t, err) + for i, emoji := range emojis { + found := false - for _, savedEmoji := range result { - if emoji.Id == savedEmoji.Id { - found = true - break - } + for _, savedEmoji := range result { + if emoji.Id == savedEmoji.Id { + found = true + break } - - assert.Equal(t, shouldFind[i], found, emoji.Name) } + + assert.Equal(t, shouldFind[i], found, emoji.Name) } } From 8febdcf2b85c686a7ff2c7a389f6920d39c2e4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 6 Nov 2019 12:43:41 +0100 Subject: [PATCH 05/15] Removing unnecesary unnused old layered store (#12999) --- app/server_app_adapters.go | 2 +- store/layered_store.go | 203 ------ store/layered_store_hints.go | 11 - store/layered_store_supplier.go | 20 - store/local_cache_supplier.go | 83 --- store/localcachelayer/layer_test.go | 2 +- store/sqlstore/integrity_test.go | 82 +-- store/sqlstore/store_test.go | 2 +- store/sqlstore/supplier.go | 192 +++--- store/sqlstore/upgrade_test.go | 4 +- .../mocks/LayeredStoreDatabaseLayer.go | 629 ------------------ store/storetest/mocks/LayeredStoreSupplier.go | 36 - testlib/helper.go | 2 +- 13 files changed, 138 insertions(+), 1130 deletions(-) delete mode 100644 store/layered_store.go delete mode 100644 store/layered_store_hints.go delete mode 100644 store/layered_store_supplier.go delete mode 100644 store/local_cache_supplier.go delete mode 100644 store/storetest/mocks/LayeredStoreDatabaseLayer.go delete mode 100644 store/storetest/mocks/LayeredStoreSupplier.go diff --git a/app/server_app_adapters.go b/app/server_app_adapters.go index 85bcdc11d2..272b488011 100644 --- a/app/server_app_adapters.go +++ b/app/server_app_adapters.go @@ -61,7 +61,7 @@ func (s *Server) RunOldAppInitalization() error { if s.FakeApp().Srv.newStore == nil { s.FakeApp().Srv.newStore = func() store.Store { - return store.NewTimerLayer(localcachelayer.NewLocalCacheLayer(store.NewLayeredStore(sqlstore.NewSqlSupplier(s.FakeApp().Config().SqlSettings, s.Metrics), s.Metrics, s.Cluster), s.Metrics, s.Cluster), s.Metrics) + return store.NewTimerLayer(localcachelayer.NewLocalCacheLayer(sqlstore.NewSqlSupplier(s.FakeApp().Config().SqlSettings, s.Metrics), s.Metrics, s.Cluster), s.Metrics) } } diff --git a/store/layered_store.go b/store/layered_store.go deleted file mode 100644 index 51672fbd4b..0000000000 --- a/store/layered_store.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - "context" - - "github.com/mattermost/mattermost-server/einterfaces" -) - -type LayeredStoreDatabaseLayer interface { - LayeredStoreSupplier - Store -} - -type LayeredStore struct { - TmpContext context.Context - DatabaseLayer LayeredStoreDatabaseLayer - LocalCacheLayer *LocalCacheSupplier - LayerChainHead LayeredStoreSupplier -} - -func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) Store { - store := &LayeredStore{ - TmpContext: context.TODO(), - DatabaseLayer: db, - LocalCacheLayer: NewLocalCacheSupplier(metrics, cluster), - } - - // Setup the chain - store.LocalCacheLayer.SetChainNext(store.DatabaseLayer) - store.LayerChainHead = store.LocalCacheLayer - - return store -} - -type QueryFunction func(LayeredStoreSupplier) *LayeredStoreSupplierResult - -func (s *LayeredStore) GetCurrentSchemaVersion() string { - return s.DatabaseLayer.GetCurrentSchemaVersion() -} - -func (s *LayeredStore) Team() TeamStore { - return s.DatabaseLayer.Team() -} - -func (s *LayeredStore) Channel() ChannelStore { - return s.DatabaseLayer.Channel() -} - -func (s *LayeredStore) Post() PostStore { - return s.DatabaseLayer.Post() -} - -func (s *LayeredStore) User() UserStore { - return s.DatabaseLayer.User() -} - -func (s *LayeredStore) Bot() BotStore { - return s.DatabaseLayer.Bot() -} - -func (s *LayeredStore) Audit() AuditStore { - return s.DatabaseLayer.Audit() -} - -func (s *LayeredStore) ClusterDiscovery() ClusterDiscoveryStore { - return s.DatabaseLayer.ClusterDiscovery() -} - -func (s *LayeredStore) Compliance() ComplianceStore { - return s.DatabaseLayer.Compliance() -} - -func (s *LayeredStore) Session() SessionStore { - return s.DatabaseLayer.Session() -} - -func (s *LayeredStore) OAuth() OAuthStore { - return s.DatabaseLayer.OAuth() -} - -func (s *LayeredStore) System() SystemStore { - return s.DatabaseLayer.System() -} - -func (s *LayeredStore) Webhook() WebhookStore { - return s.DatabaseLayer.Webhook() -} - -func (s *LayeredStore) Command() CommandStore { - return s.DatabaseLayer.Command() -} - -func (s *LayeredStore) CommandWebhook() CommandWebhookStore { - return s.DatabaseLayer.CommandWebhook() -} - -func (s *LayeredStore) Preference() PreferenceStore { - return s.DatabaseLayer.Preference() -} - -func (s *LayeredStore) License() LicenseStore { - return s.DatabaseLayer.License() -} - -func (s *LayeredStore) Token() TokenStore { - return s.DatabaseLayer.Token() -} - -func (s *LayeredStore) Emoji() EmojiStore { - return s.DatabaseLayer.Emoji() -} - -func (s *LayeredStore) Status() StatusStore { - return s.DatabaseLayer.Status() -} - -func (s *LayeredStore) FileInfo() FileInfoStore { - return s.DatabaseLayer.FileInfo() -} - -func (s *LayeredStore) Reaction() ReactionStore { - return s.DatabaseLayer.Reaction() -} - -func (s *LayeredStore) Job() JobStore { - return s.DatabaseLayer.Job() -} - -func (s *LayeredStore) UserAccessToken() UserAccessTokenStore { - return s.DatabaseLayer.UserAccessToken() -} - -func (s *LayeredStore) ChannelMemberHistory() ChannelMemberHistoryStore { - return s.DatabaseLayer.ChannelMemberHistory() -} - -func (s *LayeredStore) Plugin() PluginStore { - return s.DatabaseLayer.Plugin() -} - -func (s *LayeredStore) Role() RoleStore { - return s.DatabaseLayer.Role() -} - -func (s *LayeredStore) TermsOfService() TermsOfServiceStore { - return s.DatabaseLayer.TermsOfService() -} - -func (s *LayeredStore) UserTermsOfService() UserTermsOfServiceStore { - return s.DatabaseLayer.UserTermsOfService() -} - -func (s *LayeredStore) Scheme() SchemeStore { - return s.DatabaseLayer.Scheme() -} - -func (s *LayeredStore) Group() GroupStore { - return s.DatabaseLayer.Group() -} - -func (s *LayeredStore) LinkMetadata() LinkMetadataStore { - return s.DatabaseLayer.LinkMetadata() -} - -func (s *LayeredStore) MarkSystemRanUnitTests() { - s.DatabaseLayer.MarkSystemRanUnitTests() -} - -func (s *LayeredStore) Close() { - s.DatabaseLayer.Close() -} - -func (s *LayeredStore) LockToMaster() { - s.DatabaseLayer.LockToMaster() -} - -func (s *LayeredStore) UnlockFromMaster() { - s.DatabaseLayer.UnlockFromMaster() -} - -func (s *LayeredStore) DropAllTables() { - defer s.LocalCacheLayer.Invalidate() - s.DatabaseLayer.DropAllTables() -} - -func (s *LayeredStore) TotalMasterDbConnections() int { - return s.DatabaseLayer.TotalMasterDbConnections() -} - -func (s *LayeredStore) TotalReadDbConnections() int { - return s.DatabaseLayer.TotalReadDbConnections() -} - -func (s *LayeredStore) TotalSearchDbConnections() int { - return s.DatabaseLayer.TotalSearchDbConnections() -} - -func (s *LayeredStore) CheckIntegrity() <-chan IntegrityCheckResult { - return s.DatabaseLayer.CheckIntegrity() -} diff --git a/store/layered_store_hints.go b/store/layered_store_hints.go deleted file mode 100644 index 6154af7c9f..0000000000 --- a/store/layered_store_hints.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -type LayeredStoreHint int - -const ( - LSH_NO_CACHE LayeredStoreHint = iota - LSH_MASTER_ONLY -) diff --git a/store/layered_store_supplier.go b/store/layered_store_supplier.go deleted file mode 100644 index 0bccad868d..0000000000 --- a/store/layered_store_supplier.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -type LayeredStoreSupplierResult struct { - StoreResult -} - -func NewSupplierResult() *LayeredStoreSupplierResult { - return &LayeredStoreSupplierResult{} -} - -type LayeredStoreSupplier interface { - // - // Control - // - SetChainNext(LayeredStoreSupplier) - Next() LayeredStoreSupplier -} diff --git a/store/local_cache_supplier.go b/store/local_cache_supplier.go deleted file mode 100644 index 76f084a115..0000000000 --- a/store/local_cache_supplier.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - "context" - - "github.com/mattermost/mattermost-server/einterfaces" - "github.com/mattermost/mattermost-server/model" -) - -const ( - CLEAR_CACHE_MESSAGE_DATA = "" -) - -type LocalCacheSupplier struct { - next LayeredStoreSupplier - metrics einterfaces.MetricsInterface - cluster einterfaces.ClusterInterface -} - -// Caching Interface -type ObjectCache interface { - AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) - AddWithDefaultExpires(key, value interface{}) - Purge() - Get(key interface{}) (value interface{}, ok bool) - Remove(key interface{}) - Len() int - Name() string - GetInvalidateClusterEvent() string -} - -func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterfaces.ClusterInterface) *LocalCacheSupplier { - supplier := &LocalCacheSupplier{ - metrics: metrics, - cluster: cluster, - } - - return supplier -} - -func (s *LocalCacheSupplier) SetChainNext(next LayeredStoreSupplier) { - s.next = next -} - -func (s *LocalCacheSupplier) Next() LayeredStoreSupplier { - return s.next -} - -func (s *LocalCacheSupplier) doStandardAddToCache(ctx context.Context, cache ObjectCache, key string, result *LayeredStoreSupplierResult, hints ...LayeredStoreHint) { - if result.Err == nil && result.Data != nil { - cache.AddWithDefaultExpires(key, result.Data) - } -} - -func (s *LocalCacheSupplier) doInvalidateCacheCluster(cache ObjectCache, key string) { - cache.Remove(key) - if s.cluster != nil { - msg := &model.ClusterMessage{ - Event: cache.GetInvalidateClusterEvent(), - SendType: model.CLUSTER_SEND_BEST_EFFORT, - Data: key, - } - s.cluster.SendClusterMessage(msg) - } -} - -func (s *LocalCacheSupplier) doClearCacheCluster(cache ObjectCache) { - cache.Purge() - if s.cluster != nil { - msg := &model.ClusterMessage{ - Event: cache.GetInvalidateClusterEvent(), - SendType: model.CLUSTER_SEND_BEST_EFFORT, - Data: CLEAR_CACHE_MESSAGE_DATA, - } - s.cluster.SendClusterMessage(msg) - } -} - -func (s *LocalCacheSupplier) Invalidate() { -} diff --git a/store/localcachelayer/layer_test.go b/store/localcachelayer/layer_test.go index d180ee9bdd..6bea6f3ef7 100644 --- a/store/localcachelayer/layer_test.go +++ b/store/localcachelayer/layer_test.go @@ -58,7 +58,7 @@ func initStores() { go func() { defer wg.Done() st.SqlSupplier = sqlstore.NewSqlSupplier(*st.SqlSettings, nil) - st.Store = NewLocalCacheLayer(store.NewLayeredStore(st.SqlSupplier, nil, nil), nil, nil) + st.Store = NewLocalCacheLayer(st.SqlSupplier, nil, nil) st.Store.DropAllTables() st.Store.MarkSystemRanUnitTests() }() diff --git a/store/sqlstore/integrity_test.go b/store/sqlstore/integrity_test.go index d511f32f83..a9c0143776 100644 --- a/store/sqlstore/integrity_test.go +++ b/store/sqlstore/integrity_test.go @@ -362,7 +362,7 @@ func TestCheckIntegrity(t *testing.T) { func TestCheckParentChildIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) t.Run("should receive an error", func(t *testing.T) { config := relationalCheckConfig{ parentName: "NotValid", @@ -379,7 +379,7 @@ func TestCheckParentChildIntegrity(t *testing.T) { func TestCheckChannelsCommandWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -407,7 +407,7 @@ func TestCheckChannelsCommandWebhooksIntegrity(t *testing.T) { func TestCheckChannelsChannelMemberHistoryIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -437,7 +437,7 @@ func TestCheckChannelsChannelMemberHistoryIntegrity(t *testing.T) { func TestCheckChannelsChannelMembersIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -465,7 +465,7 @@ func TestCheckChannelsChannelMembersIntegrity(t *testing.T) { func TestCheckChannelsIncomingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -493,7 +493,7 @@ func TestCheckChannelsIncomingWebhooksIntegrity(t *testing.T) { func TestCheckChannelsOutgoingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -523,7 +523,7 @@ func TestCheckChannelsOutgoingWebhooksIntegrity(t *testing.T) { func TestCheckChannelsPostsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -550,7 +550,7 @@ func TestCheckChannelsPostsIntegrity(t *testing.T) { func TestCheckCommandsCommandWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -578,7 +578,7 @@ func TestCheckCommandsCommandWebhooksIntegrity(t *testing.T) { func TestCheckPostsFileInfoIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -605,7 +605,7 @@ func TestCheckPostsFileInfoIntegrity(t *testing.T) { func TestCheckPostsPostsParentIdIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -637,7 +637,7 @@ func TestCheckPostsPostsParentIdIntegrity(t *testing.T) { func TestCheckPostsPostsRootIdIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -667,7 +667,7 @@ func TestCheckPostsPostsRootIdIntegrity(t *testing.T) { func TestCheckPostsReactionsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -694,7 +694,7 @@ func TestCheckPostsReactionsIntegrity(t *testing.T) { func TestCheckSchemesChannelsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -725,7 +725,7 @@ func TestCheckSchemesChannelsIntegrity(t *testing.T) { func TestCheckSchemesTeamsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -756,7 +756,7 @@ func TestCheckSchemesTeamsIntegrity(t *testing.T) { func TestCheckSessionsAuditsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -787,7 +787,7 @@ func TestCheckSessionsAuditsIntegrity(t *testing.T) { func TestCheckTeamsChannelsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -814,7 +814,7 @@ func TestCheckTeamsChannelsIntegrity(t *testing.T) { func TestCheckTeamsCommandsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -842,7 +842,7 @@ func TestCheckTeamsCommandsIntegrity(t *testing.T) { func TestCheckTeamsIncomingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -870,7 +870,7 @@ func TestCheckTeamsIncomingWebhooksIntegrity(t *testing.T) { func TestCheckTeamsOutgoingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -898,7 +898,7 @@ func TestCheckTeamsOutgoingWebhooksIntegrity(t *testing.T) { func TestCheckTeamsTeamMembersIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -926,7 +926,7 @@ func TestCheckTeamsTeamMembersIntegrity(t *testing.T) { func TestCheckUsersAuditsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -956,7 +956,7 @@ func TestCheckUsersAuditsIntegrity(t *testing.T) { func TestCheckUsersCommandWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -984,7 +984,7 @@ func TestCheckUsersCommandWebhooksIntegrity(t *testing.T) { func TestCheckUsersChannelsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1011,7 +1011,7 @@ func TestCheckUsersChannelsIntegrity(t *testing.T) { func TestCheckUsersChannelMemberHistoryIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1041,7 +1041,7 @@ func TestCheckUsersChannelMemberHistoryIntegrity(t *testing.T) { func TestCheckUsersChannelMembersIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1071,7 +1071,7 @@ func TestCheckUsersChannelMembersIntegrity(t *testing.T) { func TestCheckUsersCommandsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1099,7 +1099,7 @@ func TestCheckUsersCommandsIntegrity(t *testing.T) { func TestCheckUsersCompliancesIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1129,7 +1129,7 @@ func TestCheckUsersCompliancesIntegrity(t *testing.T) { func TestCheckUsersEmojiIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1159,7 +1159,7 @@ func TestCheckUsersEmojiIntegrity(t *testing.T) { func TestCheckUsersFileInfoIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1188,7 +1188,7 @@ func TestCheckUsersFileInfoIntegrity(t *testing.T) { func TestCheckUsersIncomingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1216,7 +1216,7 @@ func TestCheckUsersIncomingWebhooksIntegrity(t *testing.T) { func TestCheckUsersOAuthAccessDataIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1246,7 +1246,7 @@ func TestCheckUsersOAuthAccessDataIntegrity(t *testing.T) { func TestCheckUsersOAuthAppsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1276,7 +1276,7 @@ func TestCheckUsersOAuthAppsIntegrity(t *testing.T) { func TestCheckUsersOAuthAuthDataIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1306,7 +1306,7 @@ func TestCheckUsersOAuthAuthDataIntegrity(t *testing.T) { func TestCheckUsersOutgoingWebhooksIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1334,7 +1334,7 @@ func TestCheckUsersOutgoingWebhooksIntegrity(t *testing.T) { func TestCheckUsersPostsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1361,7 +1361,7 @@ func TestCheckUsersPostsIntegrity(t *testing.T) { func TestCheckUsersPreferencesIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1390,7 +1390,7 @@ func TestCheckUsersPreferencesIntegrity(t *testing.T) { func TestCheckUsersReactionsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1419,7 +1419,7 @@ func TestCheckUsersReactionsIntegrity(t *testing.T) { func TestCheckUsersSessionsIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1447,7 +1447,7 @@ func TestCheckUsersSessionsIntegrity(t *testing.T) { func TestCheckUsersStatusIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1476,7 +1476,7 @@ func TestCheckUsersStatusIntegrity(t *testing.T) { func TestCheckUsersTeamMembersIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { @@ -1506,7 +1506,7 @@ func TestCheckUsersTeamMembersIntegrity(t *testing.T) { func TestCheckUsersUserAccessTokensIntegrity(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - supplier := ss.(*store.LayeredStore).DatabaseLayer.(*SqlSupplier) + supplier := ss.(*SqlSupplier) dbmap := supplier.GetMaster() t.Run("should generate a report with no records", func(t *testing.T) { diff --git a/store/sqlstore/store_test.go b/store/sqlstore/store_test.go index 850c7b740f..b5454257b1 100644 --- a/store/sqlstore/store_test.go +++ b/store/sqlstore/store_test.go @@ -70,7 +70,7 @@ func initStores() { go func() { defer wg.Done() st.SqlSupplier = NewSqlSupplier(*st.SqlSettings, nil) - st.Store = store.NewLayeredStore(st.SqlSupplier, nil, nil) + st.Store = st.SqlSupplier st.Store.DropAllTables() st.Store.MarkSystemRanUnitTests() }() diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go index 3db37c90d3..94791b3fc9 100644 --- a/store/sqlstore/supplier.go +++ b/store/sqlstore/supplier.go @@ -67,7 +67,7 @@ const ( EXIT_DOES_COLUMN_EXISTS_SQLITE = 138 ) -type SqlSupplierOldStores struct { +type SqlSupplierStores struct { team store.TeamStore channel store.ChannelStore post store.PostStore @@ -106,11 +106,10 @@ type SqlSupplier struct { // See https://github.com/mattermost/mattermost-server/pull/7281 rrCounter int64 srCounter int64 - next store.LayeredStoreSupplier master *gorp.DbMap replicas []*gorp.DbMap searchReplicas []*gorp.DbMap - oldStores SqlSupplierOldStores + stores SqlSupplierStores settings *model.SqlSettings lockedToMaster bool } @@ -124,37 +123,37 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter supplier.initConnection() - supplier.oldStores.team = NewSqlTeamStore(supplier, metrics) - supplier.oldStores.channel = NewSqlChannelStore(supplier, metrics) - supplier.oldStores.post = NewSqlPostStore(supplier, metrics) - supplier.oldStores.user = NewSqlUserStore(supplier, metrics) - supplier.oldStores.bot = NewSqlBotStore(supplier, metrics) - supplier.oldStores.audit = NewSqlAuditStore(supplier) - supplier.oldStores.cluster = NewSqlClusterDiscoveryStore(supplier) - supplier.oldStores.compliance = NewSqlComplianceStore(supplier) - supplier.oldStores.session = NewSqlSessionStore(supplier) - supplier.oldStores.oauth = NewSqlOAuthStore(supplier) - supplier.oldStores.system = NewSqlSystemStore(supplier) - supplier.oldStores.webhook = NewSqlWebhookStore(supplier, metrics) - supplier.oldStores.command = NewSqlCommandStore(supplier) - supplier.oldStores.commandWebhook = NewSqlCommandWebhookStore(supplier) - supplier.oldStores.preference = NewSqlPreferenceStore(supplier) - supplier.oldStores.license = NewSqlLicenseStore(supplier) - supplier.oldStores.token = NewSqlTokenStore(supplier) - supplier.oldStores.emoji = NewSqlEmojiStore(supplier, metrics) - supplier.oldStores.status = NewSqlStatusStore(supplier) - supplier.oldStores.fileInfo = NewSqlFileInfoStore(supplier, metrics) - supplier.oldStores.job = NewSqlJobStore(supplier) - supplier.oldStores.userAccessToken = NewSqlUserAccessTokenStore(supplier) - supplier.oldStores.channelMemberHistory = NewSqlChannelMemberHistoryStore(supplier) - supplier.oldStores.plugin = NewSqlPluginStore(supplier) - supplier.oldStores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics) - supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier) - supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier) - supplier.oldStores.reaction = NewSqlReactionStore(supplier) - supplier.oldStores.role = NewSqlRoleStore(supplier) - supplier.oldStores.scheme = NewSqlSchemeStore(supplier) - supplier.oldStores.group = NewSqlGroupStore(supplier) + supplier.stores.team = NewSqlTeamStore(supplier, metrics) + supplier.stores.channel = NewSqlChannelStore(supplier, metrics) + supplier.stores.post = NewSqlPostStore(supplier, metrics) + supplier.stores.user = NewSqlUserStore(supplier, metrics) + supplier.stores.bot = NewSqlBotStore(supplier, metrics) + supplier.stores.audit = NewSqlAuditStore(supplier) + supplier.stores.cluster = NewSqlClusterDiscoveryStore(supplier) + supplier.stores.compliance = NewSqlComplianceStore(supplier) + supplier.stores.session = NewSqlSessionStore(supplier) + supplier.stores.oauth = NewSqlOAuthStore(supplier) + supplier.stores.system = NewSqlSystemStore(supplier) + supplier.stores.webhook = NewSqlWebhookStore(supplier, metrics) + supplier.stores.command = NewSqlCommandStore(supplier) + supplier.stores.commandWebhook = NewSqlCommandWebhookStore(supplier) + supplier.stores.preference = NewSqlPreferenceStore(supplier) + supplier.stores.license = NewSqlLicenseStore(supplier) + supplier.stores.token = NewSqlTokenStore(supplier) + supplier.stores.emoji = NewSqlEmojiStore(supplier, metrics) + supplier.stores.status = NewSqlStatusStore(supplier) + supplier.stores.fileInfo = NewSqlFileInfoStore(supplier, metrics) + supplier.stores.job = NewSqlJobStore(supplier) + supplier.stores.userAccessToken = NewSqlUserAccessTokenStore(supplier) + supplier.stores.channelMemberHistory = NewSqlChannelMemberHistoryStore(supplier) + supplier.stores.plugin = NewSqlPluginStore(supplier) + supplier.stores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics) + supplier.stores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier) + supplier.stores.linkMetadata = NewSqlLinkMetadataStore(supplier) + supplier.stores.reaction = NewSqlReactionStore(supplier) + supplier.stores.role = NewSqlRoleStore(supplier) + supplier.stores.scheme = NewSqlSchemeStore(supplier) + supplier.stores.group = NewSqlGroupStore(supplier) err := supplier.GetMaster().CreateTablesIfNotExists() if err != nil { @@ -170,46 +169,37 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter os.Exit(EXIT_GENERIC_FAILURE) } - supplier.oldStores.team.(*SqlTeamStore).CreateIndexesIfNotExists() - supplier.oldStores.channel.(*SqlChannelStore).CreateIndexesIfNotExists() - supplier.oldStores.post.(*SqlPostStore).CreateIndexesIfNotExists() - supplier.oldStores.user.(*SqlUserStore).CreateIndexesIfNotExists() - supplier.oldStores.bot.(*SqlBotStore).CreateIndexesIfNotExists() - supplier.oldStores.audit.(*SqlAuditStore).CreateIndexesIfNotExists() - supplier.oldStores.compliance.(*SqlComplianceStore).CreateIndexesIfNotExists() - supplier.oldStores.session.(*SqlSessionStore).CreateIndexesIfNotExists() - supplier.oldStores.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() - supplier.oldStores.system.(*SqlSystemStore).CreateIndexesIfNotExists() - supplier.oldStores.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() - supplier.oldStores.command.(*SqlCommandStore).CreateIndexesIfNotExists() - supplier.oldStores.commandWebhook.(*SqlCommandWebhookStore).CreateIndexesIfNotExists() - supplier.oldStores.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists() - supplier.oldStores.license.(*SqlLicenseStore).CreateIndexesIfNotExists() - supplier.oldStores.token.(*SqlTokenStore).CreateIndexesIfNotExists() - supplier.oldStores.emoji.(*SqlEmojiStore).CreateIndexesIfNotExists() - supplier.oldStores.status.(*SqlStatusStore).CreateIndexesIfNotExists() - supplier.oldStores.fileInfo.(*SqlFileInfoStore).CreateIndexesIfNotExists() - supplier.oldStores.job.(*SqlJobStore).CreateIndexesIfNotExists() - supplier.oldStores.userAccessToken.(*SqlUserAccessTokenStore).CreateIndexesIfNotExists() - supplier.oldStores.plugin.(*SqlPluginStore).CreateIndexesIfNotExists() - supplier.oldStores.TermsOfService.(SqlTermsOfServiceStore).CreateIndexesIfNotExists() - supplier.oldStores.UserTermsOfService.(SqlUserTermsOfServiceStore).CreateIndexesIfNotExists() - supplier.oldStores.linkMetadata.(*SqlLinkMetadataStore).CreateIndexesIfNotExists() - supplier.oldStores.group.(*SqlGroupStore).CreateIndexesIfNotExists() - - supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures() + supplier.stores.team.(*SqlTeamStore).CreateIndexesIfNotExists() + supplier.stores.channel.(*SqlChannelStore).CreateIndexesIfNotExists() + supplier.stores.post.(*SqlPostStore).CreateIndexesIfNotExists() + supplier.stores.user.(*SqlUserStore).CreateIndexesIfNotExists() + supplier.stores.bot.(*SqlBotStore).CreateIndexesIfNotExists() + supplier.stores.audit.(*SqlAuditStore).CreateIndexesIfNotExists() + supplier.stores.compliance.(*SqlComplianceStore).CreateIndexesIfNotExists() + supplier.stores.session.(*SqlSessionStore).CreateIndexesIfNotExists() + supplier.stores.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() + supplier.stores.system.(*SqlSystemStore).CreateIndexesIfNotExists() + supplier.stores.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists() + supplier.stores.command.(*SqlCommandStore).CreateIndexesIfNotExists() + supplier.stores.commandWebhook.(*SqlCommandWebhookStore).CreateIndexesIfNotExists() + supplier.stores.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists() + supplier.stores.license.(*SqlLicenseStore).CreateIndexesIfNotExists() + supplier.stores.token.(*SqlTokenStore).CreateIndexesIfNotExists() + supplier.stores.emoji.(*SqlEmojiStore).CreateIndexesIfNotExists() + supplier.stores.status.(*SqlStatusStore).CreateIndexesIfNotExists() + supplier.stores.fileInfo.(*SqlFileInfoStore).CreateIndexesIfNotExists() + supplier.stores.job.(*SqlJobStore).CreateIndexesIfNotExists() + supplier.stores.userAccessToken.(*SqlUserAccessTokenStore).CreateIndexesIfNotExists() + supplier.stores.plugin.(*SqlPluginStore).CreateIndexesIfNotExists() + supplier.stores.TermsOfService.(SqlTermsOfServiceStore).CreateIndexesIfNotExists() + supplier.stores.UserTermsOfService.(SqlUserTermsOfServiceStore).CreateIndexesIfNotExists() + supplier.stores.linkMetadata.(*SqlLinkMetadataStore).CreateIndexesIfNotExists() + supplier.stores.group.(*SqlGroupStore).CreateIndexesIfNotExists() + supplier.stores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures() return supplier } -func (s *SqlSupplier) SetChainNext(next store.LayeredStoreSupplier) { - s.next = next -} - -func (s *SqlSupplier) Next() store.LayeredStoreSupplier { - return s.next -} - func setupConnection(con_type string, dataSource string, settings *model.SqlSettings) *gorp.DbMap { db, err := dbsql.Open(*settings.DriverName, dataSource) if err != nil { @@ -930,127 +920,127 @@ func (ss *SqlSupplier) UnlockFromMaster() { } func (ss *SqlSupplier) Team() store.TeamStore { - return ss.oldStores.team + return ss.stores.team } func (ss *SqlSupplier) Channel() store.ChannelStore { - return ss.oldStores.channel + return ss.stores.channel } func (ss *SqlSupplier) Post() store.PostStore { - return ss.oldStores.post + return ss.stores.post } func (ss *SqlSupplier) User() store.UserStore { - return ss.oldStores.user + return ss.stores.user } func (ss *SqlSupplier) Bot() store.BotStore { - return ss.oldStores.bot + return ss.stores.bot } func (ss *SqlSupplier) Session() store.SessionStore { - return ss.oldStores.session + return ss.stores.session } func (ss *SqlSupplier) Audit() store.AuditStore { - return ss.oldStores.audit + return ss.stores.audit } func (ss *SqlSupplier) ClusterDiscovery() store.ClusterDiscoveryStore { - return ss.oldStores.cluster + return ss.stores.cluster } func (ss *SqlSupplier) Compliance() store.ComplianceStore { - return ss.oldStores.compliance + return ss.stores.compliance } func (ss *SqlSupplier) OAuth() store.OAuthStore { - return ss.oldStores.oauth + return ss.stores.oauth } func (ss *SqlSupplier) System() store.SystemStore { - return ss.oldStores.system + return ss.stores.system } func (ss *SqlSupplier) Webhook() store.WebhookStore { - return ss.oldStores.webhook + return ss.stores.webhook } func (ss *SqlSupplier) Command() store.CommandStore { - return ss.oldStores.command + return ss.stores.command } func (ss *SqlSupplier) CommandWebhook() store.CommandWebhookStore { - return ss.oldStores.commandWebhook + return ss.stores.commandWebhook } func (ss *SqlSupplier) Preference() store.PreferenceStore { - return ss.oldStores.preference + return ss.stores.preference } func (ss *SqlSupplier) License() store.LicenseStore { - return ss.oldStores.license + return ss.stores.license } func (ss *SqlSupplier) Token() store.TokenStore { - return ss.oldStores.token + return ss.stores.token } func (ss *SqlSupplier) Emoji() store.EmojiStore { - return ss.oldStores.emoji + return ss.stores.emoji } func (ss *SqlSupplier) Status() store.StatusStore { - return ss.oldStores.status + return ss.stores.status } func (ss *SqlSupplier) FileInfo() store.FileInfoStore { - return ss.oldStores.fileInfo + return ss.stores.fileInfo } func (ss *SqlSupplier) Reaction() store.ReactionStore { - return ss.oldStores.reaction + return ss.stores.reaction } func (ss *SqlSupplier) Job() store.JobStore { - return ss.oldStores.job + return ss.stores.job } func (ss *SqlSupplier) UserAccessToken() store.UserAccessTokenStore { - return ss.oldStores.userAccessToken + return ss.stores.userAccessToken } func (ss *SqlSupplier) ChannelMemberHistory() store.ChannelMemberHistoryStore { - return ss.oldStores.channelMemberHistory + return ss.stores.channelMemberHistory } func (ss *SqlSupplier) Plugin() store.PluginStore { - return ss.oldStores.plugin + return ss.stores.plugin } func (ss *SqlSupplier) Role() store.RoleStore { - return ss.oldStores.role + return ss.stores.role } func (ss *SqlSupplier) TermsOfService() store.TermsOfServiceStore { - return ss.oldStores.TermsOfService + return ss.stores.TermsOfService } func (ss *SqlSupplier) UserTermsOfService() store.UserTermsOfServiceStore { - return ss.oldStores.UserTermsOfService + return ss.stores.UserTermsOfService } func (ss *SqlSupplier) Scheme() store.SchemeStore { - return ss.oldStores.scheme + return ss.stores.scheme } func (ss *SqlSupplier) Group() store.GroupStore { - return ss.oldStores.group + return ss.stores.group } func (ss *SqlSupplier) LinkMetadata() store.LinkMetadataStore { - return ss.oldStores.linkMetadata + return ss.stores.linkMetadata } func (ss *SqlSupplier) DropAllTables() { diff --git a/store/sqlstore/upgrade_test.go b/store/sqlstore/upgrade_test.go index 47c2dc3075..16b93f3a20 100644 --- a/store/sqlstore/upgrade_test.go +++ b/store/sqlstore/upgrade_test.go @@ -12,7 +12,7 @@ import ( func TestStoreUpgrade(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - sqlStore := ss.(*store.LayeredStore).DatabaseLayer.(SqlStore) + sqlStore := ss.(SqlStore) t.Run("invalid currentModelVersion", func(t *testing.T) { err := UpgradeDatabase(sqlStore, "notaversion") @@ -81,7 +81,7 @@ func TestStoreUpgrade(t *testing.T) { func TestSaveSchemaVersion(t *testing.T) { StoreTest(t, func(t *testing.T, ss store.Store) { - sqlStore := ss.(*store.LayeredStore).DatabaseLayer.(SqlStore) + sqlStore := ss.(SqlStore) t.Run("set earliest version", func(t *testing.T) { saveSchemaVersion(sqlStore, VERSION_3_0_0) diff --git a/store/storetest/mocks/LayeredStoreDatabaseLayer.go b/store/storetest/mocks/LayeredStoreDatabaseLayer.go deleted file mode 100644 index 6414b80649..0000000000 --- a/store/storetest/mocks/LayeredStoreDatabaseLayer.go +++ /dev/null @@ -1,629 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -// Regenerate this file using `make store-mocks`. - -package mocks - -import ( - store "github.com/mattermost/mattermost-server/store" - mock "github.com/stretchr/testify/mock" -) - -// LayeredStoreDatabaseLayer is an autogenerated mock type for the LayeredStoreDatabaseLayer type -type LayeredStoreDatabaseLayer struct { - mock.Mock -} - -// Audit provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Audit() store.AuditStore { - ret := _m.Called() - - var r0 store.AuditStore - if rf, ok := ret.Get(0).(func() store.AuditStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.AuditStore) - } - } - - return r0 -} - -// Bot provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Bot() store.BotStore { - ret := _m.Called() - - var r0 store.BotStore - if rf, ok := ret.Get(0).(func() store.BotStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.BotStore) - } - } - - return r0 -} - -// Channel provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Channel() store.ChannelStore { - ret := _m.Called() - - var r0 store.ChannelStore - if rf, ok := ret.Get(0).(func() store.ChannelStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.ChannelStore) - } - } - - return r0 -} - -// ChannelMemberHistory provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) ChannelMemberHistory() store.ChannelMemberHistoryStore { - ret := _m.Called() - - var r0 store.ChannelMemberHistoryStore - if rf, ok := ret.Get(0).(func() store.ChannelMemberHistoryStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.ChannelMemberHistoryStore) - } - } - - return r0 -} - -// CheckIntegrity provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) CheckIntegrity() <-chan store.IntegrityCheckResult { - ret := _m.Called() - - var r0 <-chan store.IntegrityCheckResult - if rf, ok := ret.Get(0).(func() <-chan store.IntegrityCheckResult); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan store.IntegrityCheckResult) - } - } - - return r0 -} - -// Close provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Close() { - _m.Called() -} - -// ClusterDiscovery provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) ClusterDiscovery() store.ClusterDiscoveryStore { - ret := _m.Called() - - var r0 store.ClusterDiscoveryStore - if rf, ok := ret.Get(0).(func() store.ClusterDiscoveryStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.ClusterDiscoveryStore) - } - } - - return r0 -} - -// Command provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Command() store.CommandStore { - ret := _m.Called() - - var r0 store.CommandStore - if rf, ok := ret.Get(0).(func() store.CommandStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.CommandStore) - } - } - - return r0 -} - -// CommandWebhook provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) CommandWebhook() store.CommandWebhookStore { - ret := _m.Called() - - var r0 store.CommandWebhookStore - if rf, ok := ret.Get(0).(func() store.CommandWebhookStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.CommandWebhookStore) - } - } - - return r0 -} - -// Compliance provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Compliance() store.ComplianceStore { - ret := _m.Called() - - var r0 store.ComplianceStore - if rf, ok := ret.Get(0).(func() store.ComplianceStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.ComplianceStore) - } - } - - return r0 -} - -// DropAllTables provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) DropAllTables() { - _m.Called() -} - -// Emoji provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Emoji() store.EmojiStore { - ret := _m.Called() - - var r0 store.EmojiStore - if rf, ok := ret.Get(0).(func() store.EmojiStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.EmojiStore) - } - } - - return r0 -} - -// FileInfo provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) FileInfo() store.FileInfoStore { - ret := _m.Called() - - var r0 store.FileInfoStore - if rf, ok := ret.Get(0).(func() store.FileInfoStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.FileInfoStore) - } - } - - return r0 -} - -// GetCurrentSchemaVersion provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) GetCurrentSchemaVersion() string { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// Group provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Group() store.GroupStore { - ret := _m.Called() - - var r0 store.GroupStore - if rf, ok := ret.Get(0).(func() store.GroupStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.GroupStore) - } - } - - return r0 -} - -// Job provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Job() store.JobStore { - ret := _m.Called() - - var r0 store.JobStore - if rf, ok := ret.Get(0).(func() store.JobStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.JobStore) - } - } - - return r0 -} - -// License provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) License() store.LicenseStore { - ret := _m.Called() - - var r0 store.LicenseStore - if rf, ok := ret.Get(0).(func() store.LicenseStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.LicenseStore) - } - } - - return r0 -} - -// LinkMetadata provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) LinkMetadata() store.LinkMetadataStore { - ret := _m.Called() - - var r0 store.LinkMetadataStore - if rf, ok := ret.Get(0).(func() store.LinkMetadataStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.LinkMetadataStore) - } - } - - return r0 -} - -// LockToMaster provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) LockToMaster() { - _m.Called() -} - -// MarkSystemRanUnitTests provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) MarkSystemRanUnitTests() { - _m.Called() -} - -// Next provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Next() store.LayeredStoreSupplier { - ret := _m.Called() - - var r0 store.LayeredStoreSupplier - if rf, ok := ret.Get(0).(func() store.LayeredStoreSupplier); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.LayeredStoreSupplier) - } - } - - return r0 -} - -// OAuth provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) OAuth() store.OAuthStore { - ret := _m.Called() - - var r0 store.OAuthStore - if rf, ok := ret.Get(0).(func() store.OAuthStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.OAuthStore) - } - } - - return r0 -} - -// Plugin provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Plugin() store.PluginStore { - ret := _m.Called() - - var r0 store.PluginStore - if rf, ok := ret.Get(0).(func() store.PluginStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.PluginStore) - } - } - - return r0 -} - -// Post provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Post() store.PostStore { - ret := _m.Called() - - var r0 store.PostStore - if rf, ok := ret.Get(0).(func() store.PostStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.PostStore) - } - } - - return r0 -} - -// Preference provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Preference() store.PreferenceStore { - ret := _m.Called() - - var r0 store.PreferenceStore - if rf, ok := ret.Get(0).(func() store.PreferenceStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.PreferenceStore) - } - } - - return r0 -} - -// Reaction provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Reaction() store.ReactionStore { - ret := _m.Called() - - var r0 store.ReactionStore - if rf, ok := ret.Get(0).(func() store.ReactionStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.ReactionStore) - } - } - - return r0 -} - -// Role provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore { - ret := _m.Called() - - var r0 store.RoleStore - if rf, ok := ret.Get(0).(func() store.RoleStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.RoleStore) - } - } - - return r0 -} - -// Scheme provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Scheme() store.SchemeStore { - ret := _m.Called() - - var r0 store.SchemeStore - if rf, ok := ret.Get(0).(func() store.SchemeStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.SchemeStore) - } - } - - return r0 -} - -// Session provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore { - ret := _m.Called() - - var r0 store.SessionStore - if rf, ok := ret.Get(0).(func() store.SessionStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.SessionStore) - } - } - - return r0 -} - -// SetChainNext provides a mock function with given fields: _a0 -func (_m *LayeredStoreDatabaseLayer) SetChainNext(_a0 store.LayeredStoreSupplier) { - _m.Called(_a0) -} - -// Status provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Status() store.StatusStore { - ret := _m.Called() - - var r0 store.StatusStore - if rf, ok := ret.Get(0).(func() store.StatusStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.StatusStore) - } - } - - return r0 -} - -// System provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) System() store.SystemStore { - ret := _m.Called() - - var r0 store.SystemStore - if rf, ok := ret.Get(0).(func() store.SystemStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.SystemStore) - } - } - - return r0 -} - -// Team provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Team() store.TeamStore { - ret := _m.Called() - - var r0 store.TeamStore - if rf, ok := ret.Get(0).(func() store.TeamStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.TeamStore) - } - } - - return r0 -} - -// TermsOfService provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) TermsOfService() store.TermsOfServiceStore { - ret := _m.Called() - - var r0 store.TermsOfServiceStore - if rf, ok := ret.Get(0).(func() store.TermsOfServiceStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.TermsOfServiceStore) - } - } - - return r0 -} - -// Token provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Token() store.TokenStore { - ret := _m.Called() - - var r0 store.TokenStore - if rf, ok := ret.Get(0).(func() store.TokenStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.TokenStore) - } - } - - return r0 -} - -// TotalMasterDbConnections provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) TotalMasterDbConnections() int { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - return r0 -} - -// TotalReadDbConnections provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) TotalReadDbConnections() int { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - return r0 -} - -// TotalSearchDbConnections provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) TotalSearchDbConnections() int { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - return r0 -} - -// UnlockFromMaster provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) UnlockFromMaster() { - _m.Called() -} - -// User provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) User() store.UserStore { - ret := _m.Called() - - var r0 store.UserStore - if rf, ok := ret.Get(0).(func() store.UserStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.UserStore) - } - } - - return r0 -} - -// UserAccessToken provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) UserAccessToken() store.UserAccessTokenStore { - ret := _m.Called() - - var r0 store.UserAccessTokenStore - if rf, ok := ret.Get(0).(func() store.UserAccessTokenStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.UserAccessTokenStore) - } - } - - return r0 -} - -// UserTermsOfService provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) UserTermsOfService() store.UserTermsOfServiceStore { - ret := _m.Called() - - var r0 store.UserTermsOfServiceStore - if rf, ok := ret.Get(0).(func() store.UserTermsOfServiceStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.UserTermsOfServiceStore) - } - } - - return r0 -} - -// Webhook provides a mock function with given fields: -func (_m *LayeredStoreDatabaseLayer) Webhook() store.WebhookStore { - ret := _m.Called() - - var r0 store.WebhookStore - if rf, ok := ret.Get(0).(func() store.WebhookStore); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.WebhookStore) - } - } - - return r0 -} diff --git a/store/storetest/mocks/LayeredStoreSupplier.go b/store/storetest/mocks/LayeredStoreSupplier.go deleted file mode 100644 index 18f9642bf8..0000000000 --- a/store/storetest/mocks/LayeredStoreSupplier.go +++ /dev/null @@ -1,36 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -// Regenerate this file using `make store-mocks`. - -package mocks - -import ( - store "github.com/mattermost/mattermost-server/store" - mock "github.com/stretchr/testify/mock" -) - -// LayeredStoreSupplier is an autogenerated mock type for the LayeredStoreSupplier type -type LayeredStoreSupplier struct { - mock.Mock -} - -// Next provides a mock function with given fields: -func (_m *LayeredStoreSupplier) Next() store.LayeredStoreSupplier { - ret := _m.Called() - - var r0 store.LayeredStoreSupplier - if rf, ok := ret.Get(0).(func() store.LayeredStoreSupplier); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(store.LayeredStoreSupplier) - } - } - - return r0 -} - -// SetChainNext provides a mock function with given fields: _a0 -func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) { - _m.Called(_a0) -} diff --git a/testlib/helper.go b/testlib/helper.go index 474bc1ae5a..60fb9c1923 100644 --- a/testlib/helper.go +++ b/testlib/helper.go @@ -103,7 +103,7 @@ func (h *MainHelper) setupStore() { h.ClusterInterface = &FakeClusterInterface{} h.SqlSupplier = sqlstore.NewSqlSupplier(*h.Settings, nil) h.Store = &TestStore{ - store.NewLayeredStore(h.SqlSupplier, nil, h.ClusterInterface), + h.SqlSupplier, } } From a39f4f10645705eacd0563a3cedb39356f041405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Villablanca=20V=C3=A1squez?= Date: Wed, 6 Nov 2019 12:04:50 -0300 Subject: [PATCH 06/15] Change the minor version of go to 1.13 (#13015) * Changed go minor version 12 -> 13 * REQUIREDGOVERSION set to 1.13.0 --- Makefile | 2 +- scripts/prereq-check.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4edd8b5b03..5d1a570c61 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ LDFLAGS += -X "github.com/mattermost/mattermost-server/model.BuildEnterpriseRead GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1 -MINIMUM_SUPPORTED_GO_MINOR_VERSION = 12 +MINIMUM_SUPPORTED_GO_MINOR_VERSION = 13 GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION) # GOOS/GOARCH of the build host, used to determine whether we're cross-compiling or not diff --git a/scripts/prereq-check.sh b/scripts/prereq-check.sh index 447c73ff4d..53273050dd 100755 --- a/scripts/prereq-check.sh +++ b/scripts/prereq-check.sh @@ -19,7 +19,7 @@ check_prereq() if check_version $installed_version $required_version; then echo "$dependency minimum requirement met. Required: $required_version, Found: $installed_version" - else + else echo "WARNING! Mattermost did not find the minimum supported version of '$dependency' installed. Required: $required_version, Found: $installed_version" echo "We highly recommend stopping installation and updating dependencies before continuing" read -p "Enter Y to continue anyway (not recommended)." -n 1 -r @@ -35,7 +35,7 @@ echo "Checking prerequisites" REQUIREDNODEVERSION=8.9.0 REQUIREDNPMVERSION=5.6.0 -REQUIREDGOVERSION=1.12.0 +REQUIREDGOVERSION=1.13.0 REQUIREDDOCKERVERSION=17.0 NODEVERSION=$(sed 's/v//' <<< $(node -v)) From 14b1777959fa666946f55487c6bbde603d1c61b4 Mon Sep 17 00:00:00 2001 From: Rajat Varyani <37879062+RajatVaryani@users.noreply.github.com> Date: Wed, 6 Nov 2019 22:12:46 +0530 Subject: [PATCH 07/15] [MM-19153] Add server version check in helper methods for plugins (#12675) --- plugin/helpers.go | 17 ++++++- plugin/helpers_bots.go | 5 ++ plugin/helpers_bots_test.go | 28 +++++++++++ plugin/helpers_kv.go | 34 ++++++++++++-- plugin/helpers_kv_test.go | 93 +++++++++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 5 deletions(-) diff --git a/plugin/helpers.go b/plugin/helpers.go index 726741ad68..7b62fffa80 100644 --- a/plugin/helpers.go +++ b/plugin/helpers.go @@ -3,7 +3,11 @@ package plugin -import "github.com/mattermost/mattermost-server/model" +import ( + "github.com/blang/semver" + "github.com/mattermost/mattermost-server/model" + "github.com/pkg/errors" +) type Helpers interface { // EnsureBot either returns an existing bot user matching the given bot, or creates a bot user from the given bot. @@ -54,3 +58,14 @@ type Helpers interface { type HelpersImpl struct { API API } + +func (p *HelpersImpl) ensureServerVersion(required string) error { + serverVersion := p.API.GetServerVersion() + currentVersion := semver.MustParse(serverVersion) + requiredVersion := semver.MustParse(required) + + if currentVersion.LT(requiredVersion) { + return errors.Errorf("incompatible server version for plugin, minimum required version: %s, current version: %s", required, serverVersion) + } + return nil +} diff --git a/plugin/helpers_bots.go b/plugin/helpers_bots.go index 28ed4df3f0..d7ab57ce64 100644 --- a/plugin/helpers_bots.go +++ b/plugin/helpers_bots.go @@ -10,6 +10,11 @@ import ( ) func (p *HelpersImpl) EnsureBot(bot *model.Bot) (retBotId string, retErr error) { + err := p.ensureServerVersion("5.10.0") + if err != nil { + return "", errors.Wrap(err, "failed to ensure bot") + } + // Must provide a bot with a username if bot == nil || len(bot.Username) < 1 { return "", errors.New("passed a bad bot, nil or no username") diff --git a/plugin/helpers_bots_test.go b/plugin/helpers_bots_test.go index 792f67e177..f3b985951a 100644 --- a/plugin/helpers_bots_test.go +++ b/plugin/helpers_bots_test.go @@ -24,15 +24,37 @@ func TestEnsureBot(t *testing.T) { Description: "testbotdescription", } + t.Run("server version incompatible", func(t *testing.T) { + api := setupAPI() + api.On("GetServerVersion").Return("5.9.0") + defer api.AssertExpectations(t) + + p := &plugin.HelpersImpl{} + p.API = api + + _, retErr := p.EnsureBot(nil) + + assert.NotNil(t, retErr) + assert.Equal(t, "failed to ensure bot: incompatible server version for plugin, minimum required version: 5.10.0, current version: 5.9.0", retErr.Error()) + }) + t.Run("bad parameters", func(t *testing.T) { t.Run("no bot", func(t *testing.T) { + api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") + p := &plugin.HelpersImpl{} + p.API = api botId, err := p.EnsureBot(nil) assert.Equal(t, "", botId) assert.NotNil(t, err) }) t.Run("bad username", func(t *testing.T) { + api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") + p := &plugin.HelpersImpl{} + p.API = api botId, err := p.EnsureBot(&model.Bot{ Username: "", }) @@ -46,6 +68,7 @@ func TestEnsureBot(t *testing.T) { expectedBotId := model.NewId() api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return([]byte(expectedBotId), nil) defer api.AssertExpectations(t) @@ -60,6 +83,7 @@ func TestEnsureBot(t *testing.T) { t.Run("should return an error if unable to get bot", func(t *testing.T) { api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, &model.AppError{}) defer api.AssertExpectations(t) @@ -78,6 +102,7 @@ func TestEnsureBot(t *testing.T) { expectedBotId := model.NewId() api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, nil) api.On("GetUserByUsername", testbot.Username).Return(nil, nil) api.On("CreateBot", testbot).Return(&model.Bot{ @@ -99,6 +124,7 @@ func TestEnsureBot(t *testing.T) { expectedBotId := model.NewId() api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, nil) api.On("GetUserByUsername", testbot.Username).Return(&model.User{ Id: expectedBotId, @@ -119,6 +145,7 @@ func TestEnsureBot(t *testing.T) { t.Run("should return the non-bot account but log a message if user exists with the same name and is not a bot", func(t *testing.T) { expectedBotId := model.NewId() api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, nil) api.On("GetUserByUsername", testbot.Username).Return(&model.User{ Id: expectedBotId, @@ -138,6 +165,7 @@ func TestEnsureBot(t *testing.T) { t.Run("should fail if create bot fails", func(t *testing.T) { api := setupAPI() + api.On("GetServerVersion").Return("5.10.0") api.On("KVGet", plugin.BOT_USER_KEY).Return(nil, nil) api.On("GetUserByUsername", testbot.Username).Return(nil, nil) api.On("CreateBot", testbot).Return(nil, &model.AppError{}) diff --git a/plugin/helpers_kv.go b/plugin/helpers_kv.go index 9b054502e2..3d421f47c5 100644 --- a/plugin/helpers_kv.go +++ b/plugin/helpers_kv.go @@ -11,6 +11,11 @@ import ( // KVSetJSON implements Helpers.KVSetJSON. func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error { + err := p.ensureServerVersion("5.2.0") + if err != nil { + return err + } + data, err := json.Marshal(value) if err != nil { return err @@ -26,9 +31,14 @@ func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error { // KVCompareAndSetJSON implements Helpers.KVCompareAndSetJSON. func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { - var oldData, newData []byte var err error + err = p.ensureServerVersion("5.12.0") + if err != nil { + return false, err + } + var oldData, newData []byte + if oldValue != nil { oldData, err = json.Marshal(oldValue) if err != nil { @@ -53,9 +63,15 @@ func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newV // KVCompareAndDeleteJSON implements Helpers.KVCompareAndDeleteJSON. func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) { - var oldData []byte var err error + err = p.ensureServerVersion("5.16.0") + if err != nil { + return false, err + } + + var oldData []byte + if oldValue != nil { oldData, err = json.Marshal(oldValue) if err != nil { @@ -73,6 +89,11 @@ func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) ( // KVGetJSON implements Helpers.KVGetJSON. func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) { + err := p.ensureServerVersion("5.2.0") + if err != nil { + return false, err + } + data, appErr := p.API.KVGet(key) if appErr != nil { return false, appErr @@ -81,7 +102,7 @@ func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) { return false, nil } - err := json.Unmarshal(data, value) + err = json.Unmarshal(data, value) if err != nil { return false, err } @@ -89,8 +110,13 @@ func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) { return true, nil } -// KVSetWithExpiryJSON implements Helpers.KVSetWithExpiryJSON. +// KVSetWithExpiryJSON is a wrapper around KVSetWithExpiry to simplify atomically writing a JSON object with expiry to the key value store. func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { + err := p.ensureServerVersion("5.6.0") + if err != nil { + return err + } + data, err := json.Marshal(value) if err != nil { return err diff --git a/plugin/helpers_kv_test.go b/plugin/helpers_kv_test.go index 3c63137401..2f29b587ce 100644 --- a/plugin/helpers_kv_test.go +++ b/plugin/helpers_kv_test.go @@ -10,10 +10,26 @@ import ( ) func TestKVGetJSON(t *testing.T) { + t.Run("incompatible server version", func(t *testing.T) { + api := &plugintest.API{} + api.On("GetServerVersion").Return("5.1.0") + + p := &plugin.HelpersImpl{API: api} + var dat map[string]interface{} + + ok, err := p.KVGetJSON("test-key", dat) + + api.AssertExpectations(t) + assert.False(t, ok) + assert.Error(t, err) + assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.2.0, current version: 5.1.0", err.Error()) + }) + t.Run("KVGet error", func(t *testing.T) { p := &plugin.HelpersImpl{} api := &plugintest.API{} + api.On("GetServerVersion").Return("5.2.0") api.On("KVGet", "test-key").Return(nil, &model.AppError{}) p.API = api @@ -30,6 +46,7 @@ func TestKVGetJSON(t *testing.T) { p := &plugin.HelpersImpl{} api := &plugintest.API{} + api.On("GetServerVersion").Return("5.2.0") api.On("KVGet", "test-key").Return(nil, nil) p.API = api @@ -46,6 +63,7 @@ func TestKVGetJSON(t *testing.T) { p := &plugin.HelpersImpl{} api := &plugintest.API{} + api.On("GetServerVersion").Return("5.2.0") api.On("KVGet", "test-key").Return([]byte(`{{:}"val-a": 10}`), nil) p.API = api @@ -62,6 +80,7 @@ func TestKVGetJSON(t *testing.T) { p := &plugin.HelpersImpl{} api := &plugintest.API{} + api.On("GetServerVersion").Return("5.2.0") api.On("KVGet", "test-key").Return([]byte(`{"val-a": 10}`), nil) p.API = api @@ -78,9 +97,25 @@ func TestKVGetJSON(t *testing.T) { } func TestKVSetJSON(t *testing.T) { + t.Run("incompatible server version", func(t *testing.T) { + api := &plugintest.API{} + api.On("GetServerVersion").Return("5.1.0") + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetJSON("test-key", map[string]interface{}{ + "val-a": float64(10), + }) + + api.AssertExpectations(t) + assert.Error(t, err) + assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.2.0, current version: 5.1.0", err.Error()) + }) + t.Run("JSON marshal error", func(t *testing.T) { api := &plugintest.API{} api.AssertNotCalled(t, "KVSet") + api.On("GetServerVersion").Return("5.2.0") p := &plugin.HelpersImpl{API: api} @@ -92,6 +127,7 @@ func TestKVSetJSON(t *testing.T) { t.Run("KVSet error", func(t *testing.T) { api := &plugintest.API{} api.On("KVSet", "test-key", []byte(`{"val-a":10}`)).Return(&model.AppError{}) + api.On("GetServerVersion").Return("5.2.0") p := &plugin.HelpersImpl{API: api} @@ -106,6 +142,7 @@ func TestKVSetJSON(t *testing.T) { t.Run("marshallable struct", func(t *testing.T) { api := &plugintest.API{} api.On("KVSet", "test-key", []byte(`{"val-a":10}`)).Return(nil) + api.On("GetServerVersion").Return("5.2.0") p := &plugin.HelpersImpl{API: api} @@ -119,9 +156,24 @@ func TestKVSetJSON(t *testing.T) { } func TestKVCompareAndSetJSON(t *testing.T) { + t.Run("incompatible server version", func(t *testing.T) { + api := &plugintest.API{} + api.On("GetServerVersion").Return("5.10.0") + p := &plugin.HelpersImpl{API: api} + + ok, err := p.KVCompareAndSetJSON("test-key", nil, map[string]interface{}{ + "val-b": 20, + }) + + assert.Equal(t, false, ok) + assert.Error(t, err) + assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.12.0, current version: 5.10.0", err.Error()) + }) + t.Run("old value JSON marshal error", func(t *testing.T) { api := &plugintest.API{} api.AssertNotCalled(t, "KVCompareAndSet") + api.On("GetServerVersion").Return("5.12.0") p := &plugin.HelpersImpl{API: api} ok, err := p.KVCompareAndSetJSON("test-key", func() {}, map[string]interface{}{}) @@ -133,6 +185,7 @@ func TestKVCompareAndSetJSON(t *testing.T) { t.Run("new value JSON marshal error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.12.0") api.AssertNotCalled(t, "KVCompareAndSet") p := &plugin.HelpersImpl{API: api} @@ -146,6 +199,7 @@ func TestKVCompareAndSetJSON(t *testing.T) { t.Run("KVCompareAndSet error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.12.0") api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(`{"val-b":20}`)).Return(false, &model.AppError{}) p := &plugin.HelpersImpl{API: api} @@ -162,6 +216,7 @@ func TestKVCompareAndSetJSON(t *testing.T) { t.Run("old value nil", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.12.0") api.On("KVCompareAndSet", "test-key", []byte(nil), []byte(`{"val-b":20}`)).Return(true, nil) p := &plugin.HelpersImpl{API: api} @@ -176,6 +231,7 @@ func TestKVCompareAndSetJSON(t *testing.T) { t.Run("old value non-nil", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.12.0") api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(`{"val-b":20}`)).Return(true, nil) p := &plugin.HelpersImpl{API: api} @@ -192,6 +248,7 @@ func TestKVCompareAndSetJSON(t *testing.T) { t.Run("new value nil", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.12.0") api.On("KVCompareAndSet", "test-key", []byte(`{"val-a":10}`), []byte(nil)).Return(true, nil) p := &plugin.HelpersImpl{API: api} @@ -206,8 +263,23 @@ func TestKVCompareAndSetJSON(t *testing.T) { } func TestKVCompareAndDeleteJSON(t *testing.T) { + t.Run("incompatible server version", func(t *testing.T) { + api := &plugintest.API{} + api.On("GetServerVersion").Return("5.10.0") + p := &plugin.HelpersImpl{API: api} + + ok, err := p.KVCompareAndDeleteJSON("test-key", map[string]interface{}{ + "val-a": 10, + }) + + assert.Equal(t, false, ok) + assert.Error(t, err) + assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.16.0, current version: 5.10.0", err.Error()) + }) + t.Run("old value JSON marshal error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.16.0") api.AssertNotCalled(t, "KVCompareAndDelete") p := &plugin.HelpersImpl{API: api} @@ -220,6 +292,7 @@ func TestKVCompareAndDeleteJSON(t *testing.T) { t.Run("KVCompareAndDelete error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.16.0") api.On("KVCompareAndDelete", "test-key", []byte(`{"val-a":10}`)).Return(false, &model.AppError{}) p := &plugin.HelpersImpl{API: api} @@ -234,6 +307,7 @@ func TestKVCompareAndDeleteJSON(t *testing.T) { t.Run("old value nil", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.16.0") api.On("KVCompareAndDelete", "test-key", []byte(nil)).Return(true, nil) p := &plugin.HelpersImpl{API: api} @@ -246,6 +320,7 @@ func TestKVCompareAndDeleteJSON(t *testing.T) { t.Run("old value non-nil", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.16.0") api.On("KVCompareAndDelete", "test-key", []byte(`{"val-a":10}`)).Return(true, nil) p := &plugin.HelpersImpl{API: api} @@ -260,8 +335,24 @@ func TestKVCompareAndDeleteJSON(t *testing.T) { } func TestKVSetWithExpiryJSON(t *testing.T) { + t.Run("incompatible server version", func(t *testing.T) { + api := &plugintest.API{} + api.On("GetServerVersion").Return("5.4.0") + + p := &plugin.HelpersImpl{API: api} + + err := p.KVSetWithExpiryJSON("test-key", map[string]interface{}{ + "val-a": float64(10), + }, 100) + + api.AssertExpectations(t) + assert.Error(t, err) + assert.Equal(t, "incompatible server version for plugin, minimum required version: 5.6.0, current version: 5.4.0", err.Error()) + }) + t.Run("JSON marshal error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.6.0") api.AssertNotCalled(t, "KVSetWithExpiry") p := &plugin.HelpersImpl{API: api} @@ -274,6 +365,7 @@ func TestKVSetWithExpiryJSON(t *testing.T) { t.Run("KVSetWithExpiry error", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.6.0") api.On("KVSetWithExpiry", "test-key", []byte(`{"val-a":10}`), int64(100)).Return(&model.AppError{}) p := &plugin.HelpersImpl{API: api} @@ -287,6 +379,7 @@ func TestKVSetWithExpiryJSON(t *testing.T) { t.Run("wellformed JSON", func(t *testing.T) { api := &plugintest.API{} + api.On("GetServerVersion").Return("5.6.0") api.On("KVSetWithExpiry", "test-key", []byte(`{"val-a":10}`), int64(100)).Return(nil) p := &plugin.HelpersImpl{API: api} From 6a906e91ad4be4b61857b490f0701d6eed402f76 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Wed, 6 Nov 2019 14:46:51 -0500 Subject: [PATCH 08/15] enable non-GOPATH testing with different mattermost-server folder name (#12930) * enable non-GOPATH testing Tweak the testlib package to support finding the root without assuming the folder name of the repository. Fixes: MM-19729 * fix web tests --- testlib/resources.go | 13 ++++++++++++- testlib/resources_test.go | 15 +++++++++++++++ web/web_test.go | 7 ------- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 testlib/resources_test.go diff --git a/testlib/resources.go b/testlib/resources.go index 698e20197f..df1692540e 100644 --- a/testlib/resources.go +++ b/testlib/resources.go @@ -27,6 +27,8 @@ const ( actionSymlink ) +const root = "___mattermost-server" + type testResourceDetails struct { src string dest string @@ -50,6 +52,15 @@ func findFile(path string) string { } func findDir(dir string) (string, bool) { + if dir == root { + srcPath := findFile("go.mod") + if srcPath == "" { + return "./", false + } + + return path.Dir(srcPath), true + } + found := fileutils.FindPath(dir, commonBaseSearchPaths, func(fileInfo os.FileInfo) bool { return fileInfo.IsDir() }) @@ -65,7 +76,7 @@ func getTestResourcesToSetup() []testResourceDetails { var found bool var testResourcesToSetup = []testResourceDetails{ - {"mattermost-server", "mattermost-server", resourceTypeFolder, actionSymlink}, + {root, "mattermost-server", resourceTypeFolder, actionSymlink}, {"i18n", "i18n", resourceTypeFolder, actionSymlink}, {"templates", "templates", resourceTypeFolder, actionSymlink}, {"tests", "tests", resourceTypeFolder, actionSymlink}, diff --git a/testlib/resources_test.go b/testlib/resources_test.go new file mode 100644 index 0000000000..e74752cfec --- /dev/null +++ b/testlib/resources_test.go @@ -0,0 +1,15 @@ +package testlib + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFindDir(t *testing.T) { + t.Run("find root", func(t *testing.T) { + path, found := findDir(root) + assert.True(t, found, "failed to find root") + assert.NotEmpty(t, path) + }) +} diff --git a/web/web_test.go b/web/web_test.go index 463de1d663..087942d6f7 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -12,8 +12,6 @@ import ( "path/filepath" "testing" - "github.com/mattermost/mattermost-server/testlib" - "github.com/mattermost/mattermost-server/app" "github.com/mattermost/mattermost-server/config" "github.com/mattermost/mattermost-server/model" @@ -97,11 +95,6 @@ func Setup() *TestHelper { } func (th *TestHelper) InitPlugins() *TestHelper { - - if th.tempWorkspace == "" { - th.tempWorkspace, _ = testlib.SetupTestResources() - } - pluginDir := filepath.Join(th.tempWorkspace, "plugins") webappDir := filepath.Join(th.tempWorkspace, "webapp") From 5b89cdcb6b6959b591873e01d0fdc2d41490b46a Mon Sep 17 00:00:00 2001 From: Akshay Chhajed Date: Thu, 7 Nov 2019 01:21:30 +0530 Subject: [PATCH 09/15] GH-12888 Modify api4/config_test.go to use testify (#12946) --- api4/config_test.go | 80 +++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/api4/config_test.go b/api4/config_test.go index f1bb464b60..9aef79563b 100644 --- a/api4/config_test.go +++ b/api4/config_test.go @@ -25,31 +25,26 @@ func TestGetConfig(t *testing.T) { require.NotEqual(t, "", cfg.TeamSettings.SiteName) if *cfg.LdapSettings.BindPassword != model.FAKE_SETTING && len(*cfg.LdapSettings.BindPassword) != 0 { - t.Fatal("did not sanitize properly") - } - if *cfg.FileSettings.PublicLinkSalt != model.FAKE_SETTING { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } + require.Equal(t, model.FAKE_SETTING, *cfg.FileSettings.PublicLinkSalt, "did not sanitize properly") + if *cfg.FileSettings.AmazonS3SecretAccessKey != model.FAKE_SETTING && len(*cfg.FileSettings.AmazonS3SecretAccessKey) != 0 { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } if *cfg.EmailSettings.SMTPPassword != model.FAKE_SETTING && len(*cfg.EmailSettings.SMTPPassword) != 0 { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } if *cfg.GitLabSettings.Secret != model.FAKE_SETTING && len(*cfg.GitLabSettings.Secret) != 0 { - t.Fatal("did not sanitize properly") - } - if *cfg.SqlSettings.DataSource != model.FAKE_SETTING { - t.Fatal("did not sanitize properly") - } - if *cfg.SqlSettings.AtRestEncryptKey != model.FAKE_SETTING { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } + require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.DataSource, "did not sanitize properly") + require.Equal(t, model.FAKE_SETTING, *cfg.SqlSettings.AtRestEncryptKey, "did not sanitize properly") if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceReplicas) != 0 { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } if !strings.Contains(strings.Join(cfg.SqlSettings.DataSourceSearchReplicas, " "), model.FAKE_SETTING) && len(cfg.SqlSettings.DataSourceSearchReplicas) != 0 { - t.Fatal("did not sanitize properly") + require.FailNow(t, "did not sanitize properly") } } @@ -61,17 +56,13 @@ func TestReloadConfig(t *testing.T) { t.Run("as system user", func(t *testing.T) { ok, resp := Client.ReloadConfig() CheckForbiddenStatus(t, resp) - if ok { - t.Fatal("should not Reload the config due no permission.") - } + require.False(t, ok, "should not Reload the config due no permission.") }) t.Run("as system admin", func(t *testing.T) { ok, resp := th.SystemAdminClient.ReloadConfig() CheckNoError(t, resp) - if !ok { - t.Fatal("should Reload the config") - } + require.True(t, ok, "should Reload the config") }) t.Run("as restricted system admin", func(t *testing.T) { @@ -79,9 +70,7 @@ func TestReloadConfig(t *testing.T) { ok, resp := Client.ReloadConfig() CheckForbiddenStatus(t, resp) - if ok { - t.Fatal("should not Reload the config due no permission.") - } + require.False(t, ok, "should not Reload the config due no permission.") }) } @@ -245,31 +234,28 @@ func TestGetEnvironmentConfig(t *testing.T) { envConfig, resp := SystemAdminClient.GetEnvironmentConfig() CheckNoError(t, resp) - if serviceSettings, ok := envConfig["ServiceSettings"]; !ok { - t.Fatal("should've returned ServiceSettings") - } else if serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{}); !ok { - t.Fatal("should've returned ServiceSettings as a map") - } else { - if siteURL, ok := serviceSettingsAsMap["SiteURL"]; !ok { - t.Fatal("should've returned ServiceSettings.SiteURL") - } else if siteURLAsBool, ok := siteURL.(bool); !ok { - t.Fatal("should've returned ServiceSettings.SiteURL as a boolean") - } else if !siteURLAsBool { - t.Fatal("should've returned ServiceSettings.SiteURL as true") - } + serviceSettings, ok := envConfig["ServiceSettings"] + require.True(t, ok, "should've returned ServiceSettings") - if enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"]; !ok { - t.Fatal("should've returned ServiceSettings.EnableCustomEmoji") - } else if enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool); !ok { - t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as a boolean") - } else if !enableCustomEmojiAsBool { - t.Fatal("should've returned ServiceSettings.EnableCustomEmoji as true") - } - } + serviceSettingsAsMap, ok := serviceSettings.(map[string]interface{}) + require.True(t, ok, "should've returned ServiceSettings as a map") - if _, ok := envConfig["TeamSettings"]; ok { - t.Fatal("should not have returned TeamSettings") - } + siteURL, ok := serviceSettingsAsMap["SiteURL"] + require.True(t, ok, "should've returned ServiceSettings.SiteURL") + + siteURLAsBool, ok := siteURL.(bool) + require.True(t, ok, "should've returned ServiceSettings.SiteURL as a boolean") + require.True(t, siteURLAsBool, "should've returned ServiceSettings.SiteURL as true") + + enableCustomEmoji, ok := serviceSettingsAsMap["EnableCustomEmoji"] + require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji") + + enableCustomEmojiAsBool, ok := enableCustomEmoji.(bool) + require.True(t, ok, "should've returned ServiceSettings.EnableCustomEmoji as a boolean") + require.True(t, enableCustomEmojiAsBool, "should've returned ServiceSettings.EnableCustomEmoji as true") + + _, ok = envConfig["TeamSettings"] + require.False(t, ok, "should not have returned TeamSettings") }) t.Run("as team admin", func(t *testing.T) { From ad1ab6b038a0f5b11d6b7618ebbf0317c7a3b8b4 Mon Sep 17 00:00:00 2001 From: Nikhil Ranjan Date: Wed, 6 Nov 2019 21:40:50 +0100 Subject: [PATCH 10/15] Migrate tests from "store/storetest/webhook_store.go" to use testify (#12736) * change t.Fatal calls to use testify require * fix already defined err * changes as per review comments * changes as per review * err is not nil --- store/storetest/webhook_store.go | 337 +++++++++++-------------------- 1 file changed, 113 insertions(+), 224 deletions(-) diff --git a/store/storetest/webhook_store.go b/store/storetest/webhook_store.go index 810a37224b..ee9dd9b012 100644 --- a/store/storetest/webhook_store.go +++ b/store/storetest/webhook_store.go @@ -43,13 +43,11 @@ func TestWebhookStore(t *testing.T, ss store.Store) { func testWebhookStoreSaveIncoming(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() - if _, err := ss.Webhook().SaveIncoming(o1); err != nil { - t.Fatal("couldn't save item", err) - } + _, err := ss.Webhook().SaveIncoming(o1) + require.Nil(t, err, "couldn't save item") - if _, err := ss.Webhook().SaveIncoming(o1); err == nil { - t.Fatal("shouldn't be able to update from save") - } + _, err = ss.Webhook().SaveIncoming(o1) + require.NotNil(t, err, "shouldn't be able to update from save") } func testWebhookStoreUpdateIncoming(t *testing.T, ss store.Store) { @@ -58,9 +56,7 @@ func testWebhookStoreUpdateIncoming(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") previousUpdatedAt := o1.UpdateAt @@ -70,14 +66,9 @@ func testWebhookStoreUpdateIncoming(t *testing.T, ss store.Store) { webhook, err := ss.Webhook().UpdateIncoming(o1) require.Nil(t, err) - if webhook.UpdateAt == previousUpdatedAt { - t.Fatal("should have updated the UpdatedAt of the hook") - } - - if webhook.DisplayName != "TestHook" { - t.Fatal("display name is not updated") - } + require.NotEqual(t, webhook.UpdateAt, previousUpdatedAt, "should have updated the UpdatedAt of the hook") + require.Equal(t, "TestHook", webhook.DisplayName, "display name is not updated") } func testWebhookStoreGetIncoming(t *testing.T, ss store.Store) { @@ -85,33 +76,25 @@ func testWebhookStoreGetIncoming(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") webhook, err := ss.Webhook().GetIncoming(o1.Id, false) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") webhook, err = ss.Webhook().GetIncoming(o1.Id, true) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if _, err = ss.Webhook().GetIncoming("123", false); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetIncoming("123", false) + require.NotNil(t, err, "Missing id should have failed") - if _, err = ss.Webhook().GetIncoming("123", true); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetIncoming("123", true) + require.NotNil(t, err, "Missing id should have failed") - if _, err = ss.Webhook().GetIncoming("123", true); err.StatusCode != http.StatusNotFound { - t.Fatal("Should have set the status as not found for missing id") - } + _, err = ss.Webhook().GetIncoming("123", true) + require.NotNil(t, err) + require.Equal(t, err.StatusCode, http.StatusNotFound, "Should have set the status as not found for missing id") } func testWebhookStoreGetIncomingList(t *testing.T, ss store.Store) { @@ -122,31 +105,22 @@ func testWebhookStoreGetIncomingList(t *testing.T, ss store.Store) { var err *model.AppError o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") - if hooks, err := ss.Webhook().GetIncomingList(0, 1000); err != nil { - t.Fatal(err) - } else { - found := false - for _, hook := range hooks { - if hook.Id == o1.Id { - found = true - } - } - if !found { - t.Fatal("missing webhook") - } - } + hooks, err := ss.Webhook().GetIncomingList(0, 1000) + require.Nil(t, err) - if hooks, err := ss.Webhook().GetIncomingList(0, 1); err != nil { - t.Fatal(err) - } else { - if len(hooks) != 1 { - t.Fatal("only 1 should be returned") + found := false + for _, hook := range hooks { + if hook.Id == o1.Id { + found = true } } + require.True(t, found, "missing webhook") + + hooks, err = ss.Webhook().GetIncomingList(0, 1) + require.Nil(t, err) + require.Len(t, hooks, 1, "only 1 should be returned") } func testWebhookStoreGetIncomingListByUser(t *testing.T, ss store.Store) { @@ -179,21 +153,13 @@ func testWebhookStoreGetIncomingByTeam(t *testing.T, ss store.Store) { o1, err = ss.Webhook().SaveIncoming(o1) require.Nil(t, err) - if hooks, err := ss.Webhook().GetIncomingByTeam(o1.TeamId, 0, 100); err != nil { - t.Fatal(err) - } else { - if hooks[0].CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } - } + hooks, err := ss.Webhook().GetIncomingByTeam(o1.TeamId, 0, 100) + require.Nil(t, err) + require.Equal(t, hooks[0].CreateAt, o1.CreateAt, "invalid returned webhook") - if hooks, err := ss.Webhook().GetIncomingByTeam("123", 0, 100); err != nil { - t.Fatal(err) - } else { - if len(hooks) != 0 { - t.Fatal("no webhooks should have returned") - } - } + hooks, err = ss.Webhook().GetIncomingByTeam("123", 0, 100) + require.Nil(t, err) + require.Len(t, hooks, 0, "no webhooks should have returned") } func TestWebhookStoreGetIncomingByTeamByUser(t *testing.T, ss store.Store) { @@ -232,23 +198,15 @@ func TestWebhookStoreGetIncomingByChannel(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err := ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook") - } + require.Nil(t, err, "unable to save webhook") webhooks, err := ss.Webhook().GetIncomingByChannel(o1.ChannelId) require.Nil(t, err) - if webhooks[0].CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhooks[0].CreateAt, o1.CreateAt, "invalid returned webhook") - if webhooks, err = ss.Webhook().GetIncomingByChannel("123"); err != nil { - t.Fatal(err) - } else { - if len(webhooks) != 0 { - t.Fatal("no webhooks should have returned") - } - } + webhooks, err = ss.Webhook().GetIncomingByChannel("123") + require.Nil(t, err) + require.Len(t, webhooks, 0, "no webhooks should have returned") } func testWebhookStoreDeleteIncoming(t *testing.T, ss store.Store) { @@ -256,19 +214,14 @@ func testWebhookStoreDeleteIncoming(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") webhook, err := ss.Webhook().GetIncoming(o1.Id, true) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err = ss.Webhook().DeleteIncoming(o1.Id, model.GetMillis()); err != nil { - t.Fatal(err) - } + err = ss.Webhook().DeleteIncoming(o1.Id, model.GetMillis()) + require.Nil(t, err) webhook, err = ss.Webhook().GetIncoming(o1.Id, true) require.NotNil(t, err) @@ -279,23 +232,17 @@ func testWebhookStoreDeleteIncomingByChannel(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") webhook, err := ss.Webhook().GetIncoming(o1.Id, true) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err = ss.Webhook().PermanentDeleteIncomingByChannel(o1.ChannelId); err != nil { - t.Fatal(err) - } + err = ss.Webhook().PermanentDeleteIncomingByChannel(o1.ChannelId) + require.Nil(t, err) - if _, err = ss.Webhook().GetIncoming(o1.Id, true); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetIncoming(o1.Id, true) + require.NotNil(t, err, "Missing id should have failed") } func testWebhookStoreDeleteIncomingByUser(t *testing.T, ss store.Store) { @@ -303,23 +250,17 @@ func testWebhookStoreDeleteIncomingByUser(t *testing.T, ss store.Store) { o1 := buildIncomingWebhook() o1, err = ss.Webhook().SaveIncoming(o1) - if err != nil { - t.Fatal("unable to save webhook", err) - } + require.Nil(t, err, "unable to save webhook") webhook, err := ss.Webhook().GetIncoming(o1.Id, true) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err = ss.Webhook().PermanentDeleteIncomingByUser(o1.UserId); err != nil { - t.Fatal(err) - } + err = ss.Webhook().PermanentDeleteIncomingByUser(o1.UserId) + require.Nil(t, err) - if _, err = ss.Webhook().GetIncoming(o1.Id, true); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetIncoming(o1.Id, true) + require.NotNil(t, err, "Missing id should have failed") } func buildIncomingWebhook() *model.IncomingWebhook { @@ -340,13 +281,11 @@ func testWebhookStoreSaveOutgoing(t *testing.T, ss store.Store) { o1.Username = "test-user-name" o1.IconURL = "http://nowhere.com/icon" - if _, err := ss.Webhook().SaveOutgoing(&o1); err != nil { - t.Fatal("couldn't save item", err) - } + _, err := ss.Webhook().SaveOutgoing(&o1) + require.Nil(t, err, "couldn't save item") - if _, err := ss.Webhook().SaveOutgoing(&o1); err == nil { - t.Fatal("shouldn't be able to update from save") - } + _, err = ss.Webhook().SaveOutgoing(&o1) + require.NotNil(t, err, "shouldn't be able to update from save") } func testWebhookStoreGetOutgoing(t *testing.T, ss store.Store) { @@ -362,13 +301,10 @@ func testWebhookStoreGetOutgoing(t *testing.T, ss store.Store) { webhook, err := ss.Webhook().GetOutgoing(o1.Id) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if _, err := ss.Webhook().GetOutgoing("123"); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetOutgoing("123") + require.NotNil(t, err, "Missing id should have failed") } func testWebhookStoreGetOutgoingListByUser(t *testing.T, ss store.Store) { @@ -412,38 +348,28 @@ func testWebhookStoreGetOutgoingList(t *testing.T, ss store.Store) { o2, _ = ss.Webhook().SaveOutgoing(o2) - if r1, err := ss.Webhook().GetOutgoingList(0, 1000); err != nil { - t.Fatal(err) - } else { - hooks := r1 - found1 := false - found2 := false + r1, err := ss.Webhook().GetOutgoingList(0, 1000) + require.Nil(t, err) + hooks := r1 + found1 := false + found2 := false - for _, hook := range hooks { - if hook.CreateAt != o1.CreateAt { - found1 = true - } - - if hook.CreateAt != o2.CreateAt { - found2 = true - } + for _, hook := range hooks { + if hook.CreateAt != o1.CreateAt { + found1 = true } - if !found1 { - t.Fatal("missing hook1") - } - if !found2 { - t.Fatal("missing hook2") + if hook.CreateAt != o2.CreateAt { + found2 = true } } - if result, err := ss.Webhook().GetOutgoingList(0, 2); err != nil { - t.Fatal(err) - } else { - if len(result) != 2 { - t.Fatal("wrong number of hooks returned") - } - } + require.True(t, found1, "missing hook1") + require.True(t, found2, "missing hook2") + + result, err := ss.Webhook().GetOutgoingList(0, 2) + require.Nil(t, err) + require.Len(t, result, 2, "wrong number of hooks returned") } func testWebhookStoreGetOutgoingByChannel(t *testing.T, ss store.Store) { @@ -455,21 +381,13 @@ func testWebhookStoreGetOutgoingByChannel(t *testing.T, ss store.Store) { o1, _ = ss.Webhook().SaveOutgoing(o1) - if r1, err := ss.Webhook().GetOutgoingByChannel(o1.ChannelId, 0, 100); err != nil { - t.Fatal(err) - } else { - if r1[0].CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } - } + r1, err := ss.Webhook().GetOutgoingByChannel(o1.ChannelId, 0, 100) + require.Nil(t, err) + require.Equal(t, r1[0].CreateAt, o1.CreateAt, "invalid returned webhook") - if result, err := ss.Webhook().GetOutgoingByChannel("123", -1, -1); err != nil { - t.Fatal(err) - } else { - if len(result) != 0 { - t.Fatal("no webhooks should have returned") - } - } + result, err := ss.Webhook().GetOutgoingByChannel("123", -1, -1) + require.Nil(t, err) + require.Len(t, result, 0, "no webhooks should have returned") } func testWebhookStoreGetOutgoingByChannelByUser(t *testing.T, ss store.Store) { @@ -520,21 +438,13 @@ func testWebhookStoreGetOutgoingByTeam(t *testing.T, ss store.Store) { o1, _ = ss.Webhook().SaveOutgoing(o1) - if r1, err := ss.Webhook().GetOutgoingByTeam(o1.TeamId, 0, 100); err != nil { - t.Fatal(err) - } else { - if r1[0].CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } - } + r1, err := ss.Webhook().GetOutgoingByTeam(o1.TeamId, 0, 100) + require.Nil(t, err) + require.Equal(t, r1[0].CreateAt, o1.CreateAt, "invalid returned webhook") - if result, err := ss.Webhook().GetOutgoingByTeam("123", -1, -1); err != nil { - t.Fatal(err) - } else { - if len(result) != 0 { - t.Fatal("no webhooks should have returned") - } - } + result, err := ss.Webhook().GetOutgoingByTeam("123", -1, -1) + require.Nil(t, err) + require.Len(t, result, 0, "no webhooks should have returned") } func testWebhookStoreGetOutgoingByTeamByUser(t *testing.T, ss store.Store) { @@ -589,17 +499,13 @@ func testWebhookStoreDeleteOutgoing(t *testing.T, ss store.Store) { webhook, err := ss.Webhook().GetOutgoing(o1.Id) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err := ss.Webhook().DeleteOutgoing(o1.Id, model.GetMillis()); err != nil { - t.Fatal(err) - } + err = ss.Webhook().DeleteOutgoing(o1.Id, model.GetMillis()) + require.Nil(t, err) - if _, err := ss.Webhook().GetOutgoing(o1.Id); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetOutgoing(o1.Id) + require.NotNil(t, err, "Missing id should have failed") } func testWebhookStoreDeleteOutgoingByChannel(t *testing.T, ss store.Store) { @@ -613,17 +519,13 @@ func testWebhookStoreDeleteOutgoingByChannel(t *testing.T, ss store.Store) { webhook, err := ss.Webhook().GetOutgoing(o1.Id) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err := ss.Webhook().PermanentDeleteOutgoingByChannel(o1.ChannelId); err != nil { - t.Fatal(err) - } + err = ss.Webhook().PermanentDeleteOutgoingByChannel(o1.ChannelId) + require.Nil(t, err) - if _, err := ss.Webhook().GetOutgoing(o1.Id); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetOutgoing(o1.Id) + require.NotNil(t, err, "Missing id should have failed") } func testWebhookStoreDeleteOutgoingByUser(t *testing.T, ss store.Store) { @@ -637,17 +539,13 @@ func testWebhookStoreDeleteOutgoingByUser(t *testing.T, ss store.Store) { webhook, err := ss.Webhook().GetOutgoing(o1.Id) require.Nil(t, err) - if webhook.CreateAt != o1.CreateAt { - t.Fatal("invalid returned webhook") - } + require.Equal(t, webhook.CreateAt, o1.CreateAt, "invalid returned webhook") - if err := ss.Webhook().PermanentDeleteOutgoingByUser(o1.CreatorId); err != nil { - t.Fatal(err) - } + err = ss.Webhook().PermanentDeleteOutgoingByUser(o1.CreatorId) + require.Nil(t, err) - if _, err := ss.Webhook().GetOutgoing(o1.Id); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Webhook().GetOutgoing(o1.Id) + require.NotNil(t, err, "Missing id should have failed") } func testWebhookStoreUpdateOutgoing(t *testing.T, ss store.Store) { @@ -664,9 +562,8 @@ func testWebhookStoreUpdateOutgoing(t *testing.T, ss store.Store) { o1.Token = model.NewId() o1.Username = "another-test-user-name" - if _, err := ss.Webhook().UpdateOutgoing(o1); err != nil { - t.Fatal(err) - } + _, err := ss.Webhook().UpdateOutgoing(o1) + require.Nil(t, err) } func testWebhookStoreCountIncoming(t *testing.T, ss store.Store) { @@ -678,13 +575,9 @@ func testWebhookStoreCountIncoming(t *testing.T, ss store.Store) { _, _ = ss.Webhook().SaveIncoming(o1) c, err := ss.Webhook().AnalyticsIncomingCount("") - if err != nil { - t.Fatal(err) - } + require.Nil(t, err) - if c == 0 { - t.Fatal("should have at least 1 incoming hook") - } + require.NotEqual(t, 0, c, "should have at least 1 incoming hook") } func testWebhookStoreCountOutgoing(t *testing.T, ss store.Store) { @@ -696,11 +589,7 @@ func testWebhookStoreCountOutgoing(t *testing.T, ss store.Store) { ss.Webhook().SaveOutgoing(o1) - if r, err := ss.Webhook().AnalyticsOutgoingCount(""); err != nil { - t.Fatal(err) - } else { - if r == 0 { - t.Fatal("should have at least 1 outgoing hook") - } - } + r, err := ss.Webhook().AnalyticsOutgoingCount("") + require.Nil(t, err) + require.NotEqual(t, 0, r, "should have at least 1 outgoing hook") } From a7c1514d8e5aab7efbe174cce1d770f2e0160fd4 Mon Sep 17 00:00:00 2001 From: Lev <1187448+levb@users.noreply.github.com> Date: Wed, 6 Nov 2019 13:25:52 -0800 Subject: [PATCH 11/15] Bumped Zoom plugin to v1.1.2 (#13008) * Bumped Zoom plugin version to 1.1.0 * Bumped Zoom plugin version to 1.1.1 * Bumped Zoom plugin to v1.1.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5d1a570c61..ea99a27b6c 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ TESTFLAGSEE ?= -short TE_PACKAGES=$(shell $(GO) list ./...) # Plugins Packages -PLUGIN_PACKAGES=mattermost-plugin-zoom-v1.1.1 +PLUGIN_PACKAGES=mattermost-plugin-zoom-v1.1.2 PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.1.1 PLUGIN_PACKAGES += mattermost-plugin-nps-v1.0.3 PLUGIN_PACKAGES += mattermost-plugin-custom-attributes-v1.0.2 From d99cf0864ee83dacf23390f7daaa7a8b4ec45739 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Wed, 6 Nov 2019 18:22:18 -0500 Subject: [PATCH 12/15] MM-19477: update manifest documentation example (#13016) Illustrate `homepage_url`, `support_url`, and `icon_path` in the GoDoc for model.Manifest. Relates-to: https://mattermost.atlassian.net/browse/MM-19477 --- model/manifest.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model/manifest.go b/model/manifest.go index c0eced6920..74f75a5c14 100644 --- a/model/manifest.go +++ b/model/manifest.go @@ -91,6 +91,9 @@ type PluginSettingsSchema struct { // "id": "com.mycompany.myplugin", // "name": "My Plugin", // "description": "This is my plugin", +// "homepage_url": "https://example.com", +// "support_url": "https://example.com/support", +// "icon_path": "assets/logo.svg", // "version": "0.1.0", // "min_server_version": "5.6.0", // "server": { From d37a58fe3a07e0aa5665b6921e2968f5d984561d Mon Sep 17 00:00:00 2001 From: Abu Hanifa Date: Thu, 7 Nov 2019 18:01:54 +0600 Subject: [PATCH 13/15] convert `t.fatal` into require (#12899) --- utils/lru_test.go | 48 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/utils/lru_test.go b/utils/lru_test.go index 89cff52fdf..d366e3de2e 100644 --- a/utils/lru_test.go +++ b/utils/lru_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestLRU(t *testing.T) { @@ -20,50 +21,39 @@ func TestLRU(t *testing.T) { for i := 0; i < 256; i++ { l.Add(i, i) } - if l.Len() != 128 { - t.Fatalf("bad len: %v", l.Len()) - } + require.Equalf(t, l.Len(), 128, "bad len: %v", l.Len()) for i, k := range l.Keys() { - if v, ok := l.Get(k); !ok || v != k || v != i+128 { - t.Fatalf("bad key: %v", k) - } + v, ok := l.Get(k) + require.True(t, ok, "bad key: %v", k) + require.Equalf(t, v, k, "bad key: %v", k) + require.Equalf(t, i+128, v, "bad key: %v", k) } for i := 0; i < 128; i++ { _, ok := l.Get(i) - if ok { - t.Fatalf("should be evicted") - } + require.False(t, ok, "should be evicted") } for i := 128; i < 256; i++ { _, ok := l.Get(i) - if !ok { - t.Fatalf("should not be evicted") - } + require.True(t, ok, "should not be evicted") } for i := 128; i < 192; i++ { l.Remove(i) _, ok := l.Get(i) - if ok { - t.Fatalf("should be deleted") - } + require.False(t, ok, "should be deleted") } l.Get(192) // expect 192 to be last key in l.Keys() for i, k := range l.Keys() { - if (i < 63 && k != i+193) || (i == 63 && k != 192) { - t.Fatalf("out of order key: %v", k) - } + require.Falsef(t, (i < 63 && k != i+193), "out of order key: %v", k) + require.Falsef(t, (i == 63 && k != 192), "out of order key: %v", k) } l.Purge() - if l.Len() != 0 { - t.Fatalf("bad len: %v", l.Len()) - } - if _, ok := l.Get(200); ok { - t.Fatalf("should contain nothing") - } + require.Equalf(t, l.Len(), 0, "bad len: %v", l.Len()) + _, ok := l.Get(200) + require.False(t, ok, "should contain nothing") } func TestLRUExpire(t *testing.T) { @@ -75,13 +65,11 @@ func TestLRUExpire(t *testing.T) { time.Sleep(time.Millisecond * 2100) - if r1, ok := l.Get(1); ok { - t.Fatal(r1) - } + r1, ok := l.Get(1) + require.False(t, ok, r1) - if _, ok2 := l.Get(3); !ok2 { - t.Fatal("should exist") - } + _, ok2 := l.Get(3) + require.True(t, ok2, "should exist") } func TestLRUGetOrAdd(t *testing.T) { From 5646b12686f87f6341b0a48e82bce015adb1d1da Mon Sep 17 00:00:00 2001 From: Abhishek Mane Date: Thu, 7 Nov 2019 17:32:41 +0530 Subject: [PATCH 14/15] (fixed) Refactor app/status.go to use structured logging #12723 (#12754) --- app/status.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/status.go b/app/status.go index 64d9d35e15..170d112b12 100644 --- a/app/status.go +++ b/app/status.go @@ -4,8 +4,6 @@ package app import ( - "fmt" - "github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/utils" @@ -215,11 +213,11 @@ func (a *App) SetStatusOnline(userId string, manual bool) { if status.Status != oldStatus || status.Manual != oldManual || status.LastActivityAt-oldTime > model.STATUS_MIN_UPDATE_TIME { if broadcast { if err := a.Srv.Store.Status().SaveOrUpdate(status); err != nil { - mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, err), mlog.String("user_id", userId)) + mlog.Error("Failed to save status", mlog.String("user_id", userId), mlog.Err(err), mlog.String("user_id", userId)) } } else { if err := a.Srv.Store.Status().UpdateLastActivityAt(status.UserId, status.LastActivityAt); err != nil { - mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, err), mlog.String("user_id", userId)) + mlog.Error("Failed to save status", mlog.String("user_id", userId), mlog.Err(err), mlog.String("user_id", userId)) } } } @@ -304,7 +302,7 @@ func (a *App) SaveAndBroadcastStatus(status *model.Status) { a.AddStatusCache(status) if err := a.Srv.Store.Status().SaveOrUpdate(status); err != nil { - mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", status.UserId, err)) + mlog.Error("Failed to save status", mlog.String("user_id", status.UserId), mlog.Err(err)) } a.BroadcastStatus(status) From 540cfb0239df4b203bd4aeed6d69bd9467c2892a Mon Sep 17 00:00:00 2001 From: Nikhil Ranjan Date: Thu, 7 Nov 2019 15:31:32 +0100 Subject: [PATCH 15/15] Migrate tests from store/storetest/team_store.go to use testify (#12877) * Migrate tests from store/storetest/team_store.go to use testify * fix test * fix varible declaration * fix tests * casting int64 to int for Equal check * fix int64 int Equal check * changes as per review --- store/storetest/team_store.go | 534 ++++++++++++---------------------- 1 file changed, 182 insertions(+), 352 deletions(-) diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go index 7e028ebf3a..904b0cfc92 100644 --- a/store/storetest/team_store.go +++ b/store/storetest/team_store.go @@ -69,18 +69,15 @@ func testTeamStoreSave(t *testing.T, ss store.Store) { o1.Email = MakeEmail() o1.Type = model.TEAM_OPEN - if _, err := ss.Team().Save(&o1); err != nil { - t.Fatal("couldn't save item", err) - } + _, err := ss.Team().Save(&o1) + require.Nil(t, err, "couldn't save item") - if _, err := ss.Team().Save(&o1); err == nil { - t.Fatal("shouldn't be able to update from save") - } + _, err = ss.Team().Save(&o1) + require.NotNil(t, err, "shouldn't be able to update from save") o1.Id = "" - if _, err := ss.Team().Save(&o1); err == nil { - t.Fatal("should be unique domain") - } + _, err = ss.Team().Save(&o1) + require.NotNil(t, err, "should be unique domain") } func testTeamStoreUpdate(t *testing.T, ss store.Store) { @@ -89,25 +86,21 @@ func testTeamStoreUpdate(t *testing.T, ss store.Store) { o1.Name = "z-z-z" + model.NewId() + "b" o1.Email = MakeEmail() o1.Type = model.TEAM_OPEN - if _, err := ss.Team().Save(&o1); err != nil { - t.Fatal(err) - } + _, err := ss.Team().Save(&o1) + require.Nil(t, err) time.Sleep(100 * time.Millisecond) - if _, err := ss.Team().Update(&o1); err != nil { - t.Fatal(err) - } + _, err = ss.Team().Update(&o1) + require.Nil(t, err) o1.Id = "missing" - if _, err := ss.Team().Update(&o1); err == nil { - t.Fatal("Update should have failed because of missing key") - } + _, err = ss.Team().Update(&o1) + require.NotNil(t, err, "Update should have failed because of missing key") o1.Id = model.NewId() - if _, err := ss.Team().Update(&o1); err == nil { - t.Fatal("Update should have faile because id change") - } + _, err = ss.Team().Update(&o1) + require.NotNil(t, err, "Update should have faile because id change") } func testTeamStoreGet(t *testing.T, ss store.Store) { @@ -134,21 +127,15 @@ func testTeamStoreGetByName(t *testing.T, ss store.Store) { o1.Email = MakeEmail() o1.Type = model.TEAM_OPEN - if _, err := ss.Team().Save(&o1); err != nil { - t.Fatal(err) - } + _, err := ss.Team().Save(&o1) + require.Nil(t, err) - if team, err := ss.Team().GetByName(o1.Name); err != nil { - t.Fatal(err) - } else { - if team.ToJson() != o1.ToJson() { - t.Fatal("invalid returned team") - } - } + team, err := ss.Team().GetByName(o1.Name) + require.Nil(t, err) + require.Equal(t, *team, o1, "invalid returned team") - if _, err := ss.Team().GetByName(""); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Team().GetByName("") + require.NotNil(t, err, "Missing id should have failed") } func testTeamStoreSearchAll(t *testing.T, ss store.Store) { @@ -363,9 +350,7 @@ func testTeamStoreGetByInviteId(t *testing.T, ss store.Store) { o1.InviteId = model.NewId() save1, err := ss.Team().Save(&o1) - if err != nil { - t.Fatal(err) - } + require.Nil(t, err) o2 := model.Team{} o2.DisplayName = "DisplayName" @@ -373,17 +358,12 @@ func testTeamStoreGetByInviteId(t *testing.T, ss store.Store) { o2.Email = MakeEmail() o2.Type = model.TEAM_OPEN - if r1, err := ss.Team().GetByInviteId(save1.InviteId); err != nil { - t.Fatal(err) - } else { - if r1.ToJson() != o1.ToJson() { - t.Fatal("invalid returned team") - } - } + r1, err := ss.Team().GetByInviteId(save1.InviteId) + require.Nil(t, err) + require.Equal(t, *r1, o1, "invalid returned team") - if _, err := ss.Team().GetByInviteId(""); err == nil { - t.Fatal("Missing id should have failed") - } + _, err = ss.Team().GetByInviteId("") + require.NotNil(t, err, "Missing id should have failed") } func testTeamStoreByUserId(t *testing.T, ss store.Store) { @@ -400,18 +380,10 @@ func testTeamStoreByUserId(t *testing.T, ss store.Store) { _, err = ss.Team().SaveMember(m1, -1) require.Nil(t, err) - if teams, err := ss.Team().GetTeamsByUserId(m1.UserId); err != nil { - t.Fatal(err) - } else { - if len(teams) == 0 { - t.Fatal("Should return a team") - } - - if teams[0].Id != o1.Id { - t.Fatal("should be a member") - } - - } + teams, err := ss.Team().GetTeamsByUserId(m1.UserId) + require.Nil(t, err) + require.Len(t, teams, 1, "Should return a team") + require.Equal(t, teams[0].Id, o1.Id, "should be a member") } func testGetAllTeamListing(t *testing.T, ss store.Store) { @@ -449,19 +421,13 @@ func testGetAllTeamListing(t *testing.T, ss store.Store) { _, err = ss.Team().Save(&o4) require.Nil(t, err) - if teams, err := ss.Team().GetAllTeamListing(); err != nil { - t.Fatal(err) - } else { - for _, team := range teams { - if !team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as true") - } - } - - if len(teams) == 0 { - t.Fatal("failed team listing") - } + teams, err := ss.Team().GetAllTeamListing() + require.Nil(t, err) + for _, team := range teams { + require.True(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as true") } + + require.NotEmpty(t, teams, "failed team listing") } func testGetAllTeamPageListing(t *testing.T, ss store.Store) { @@ -505,14 +471,10 @@ func testGetAllTeamPageListing(t *testing.T, ss store.Store) { require.Nil(t, err) for _, team := range teams { - if !team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as true") - } + require.True(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as true") } - if len(teams) > 10 { - t.Fatal("should have returned max of 10 teams") - } + require.LessOrEqual(t, len(teams), 10, "should have returned max of 10 teams") o5 := model.Team{} o5.DisplayName = "DisplayName" @@ -527,27 +489,19 @@ func testGetAllTeamPageListing(t *testing.T, ss store.Store) { require.Nil(t, err) for _, team := range teams { - if !team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as true") - } + require.True(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as true") } - if len(teams) > 4 { - t.Fatal("should have returned max of 4 teams") - } + require.LessOrEqual(t, len(teams), 4, "should have returned max of 4 teams") teams, err = ss.Team().GetAllTeamPageListing(1, 1) require.Nil(t, err) for _, team := range teams { - if !team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as true") - } + require.True(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as true") } - if len(teams) > 1 { - t.Fatal("should have returned max of 1 team") - } + require.LessOrEqual(t, len(teams), 1, "should have returned max of 1 team") } func testGetAllPrivateTeamListing(t *testing.T, ss store.Store) { @@ -585,18 +539,12 @@ func testGetAllPrivateTeamListing(t *testing.T, ss store.Store) { _, err = ss.Team().Save(&o4) require.Nil(t, err) - if teams, err := ss.Team().GetAllPrivateTeamListing(); err != nil { - t.Fatal(err) - } else { - for _, team := range teams { - if team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as false") - } - } + teams, err := ss.Team().GetAllPrivateTeamListing() + require.Nil(t, err) + require.NotEmpty(t, teams, "failed team listing") - if len(teams) == 0 { - t.Fatal("failed team listing") - } + for _, team := range teams { + require.False(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as false") } } @@ -637,20 +585,14 @@ func testGetAllPrivateTeamPageListing(t *testing.T, ss store.Store) { _, err = ss.Team().Save(&o4) require.Nil(t, err) - if teams, listErr := ss.Team().GetAllPrivateTeamPageListing(0, 10); listErr != nil { - t.Fatal(listErr) - } else { - for _, team := range teams { - if team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as false") - } - } - - if len(teams) > 10 { - t.Fatal("should have returned max of 10 teams") - } + teams, listErr := ss.Team().GetAllPrivateTeamPageListing(0, 10) + require.Nil(t, listErr) + for _, team := range teams { + require.False(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as false") } + require.LessOrEqual(t, len(teams), 10, "should have returned max of 10 teams") + o5 := model.Team{} o5.DisplayName = "DisplayName" o5.Name = "z-z-z" + model.NewId() + "b" @@ -660,33 +602,21 @@ func testGetAllPrivateTeamPageListing(t *testing.T, ss store.Store) { _, err = ss.Team().Save(&o5) require.Nil(t, err) - if teams, listErr := ss.Team().GetAllPrivateTeamPageListing(0, 4); listErr != nil { - t.Fatal(listErr) - } else { - for _, team := range teams { - if team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as false") - } - } - - if len(teams) > 4 { - t.Fatal("should have returned max of 4 teams") - } + teams, listErr = ss.Team().GetAllPrivateTeamPageListing(0, 4) + require.Nil(t, listErr) + for _, team := range teams { + require.False(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as false") } - if teams, listErr := ss.Team().GetAllPrivateTeamPageListing(1, 1); listErr != nil { - t.Fatal(listErr) - } else { - for _, team := range teams { - if team.AllowOpenInvite { - t.Fatal("should have returned team with AllowOpenInvite as false") - } - } + require.LessOrEqual(t, len(teams), 4, "should have returned max of 4 teams") - if len(teams) > 1 { - t.Fatal("should have returned max of 1 team") - } + teams, listErr = ss.Team().GetAllPrivateTeamPageListing(1, 1) + require.Nil(t, listErr) + for _, team := range teams { + require.False(t, team.AllowOpenInvite, "should have returned team with AllowOpenInvite as false") } + + require.LessOrEqual(t, len(teams), 1, "should have returned max of 1 team") } func testGetAllPublicTeamPageListing(t *testing.T, ss store.Store) { @@ -767,9 +697,8 @@ func testDelete(t *testing.T, ss store.Store) { _, err = ss.Team().Save(&o2) require.Nil(t, err) - if r1 := ss.Team().PermanentDelete(o1.Id); r1 != nil { - t.Fatal(r1) - } + r1 := ss.Team().PermanentDelete(o1.Id) + require.Nil(t, r1) } func testPublicTeamCount(t *testing.T, ss store.Store) { @@ -852,13 +781,9 @@ func testTeamCount(t *testing.T, ss store.Store) { _, err := ss.Team().Save(&o1) require.Nil(t, err) - if teamCount, err := ss.Team().AnalyticsTeamCount(); err != nil { - t.Fatal(err) - } else { - if teamCount == 0 { - t.Fatal("should be at least 1 team") - } - } + teamCount, err := ss.Team().AnalyticsTeamCount() + require.Nil(t, err) + require.NotEqual(t, 0, int(teamCount), "should be at least 1 team") } func testTeamMembers(t *testing.T, ss store.Store) { @@ -880,47 +805,33 @@ func testTeamMembers(t *testing.T, ss store.Store) { require.Nil(t, err) assert.Len(t, ms, 2) - if ms, err = ss.Team().GetMembers(teamId2, 0, 100, nil); err != nil { - t.Fatal(err) - } else { + ms, err = ss.Team().GetMembers(teamId2, 0, 100, nil) + require.Nil(t, err) + require.Len(t, ms, 1) + require.Equal(t, m3.UserId, ms[0].UserId) - require.Len(t, ms, 1) - require.Equal(t, m3.UserId, ms[0].UserId) - } + ms, err = ss.Team().GetTeamsForUser(m1.UserId) + require.Nil(t, err) + require.Len(t, ms, 1) + require.Equal(t, m1.TeamId, ms[0].TeamId) - if ms, err = ss.Team().GetTeamsForUser(m1.UserId); err != nil { - t.Fatal(err) - } else { + err = ss.Team().RemoveMember(teamId1, m1.UserId) + require.Nil(t, err) - require.Len(t, ms, 1) - require.Equal(t, m1.TeamId, ms[0].TeamId) - } - - if err = ss.Team().RemoveMember(teamId1, m1.UserId); err != nil { - t.Fatal(err) - } - - if ms, err = ss.Team().GetMembers(teamId1, 0, 100, nil); err != nil { - t.Fatal(err) - } else { - - require.Len(t, ms, 1) - require.Equal(t, m2.UserId, ms[0].UserId) - } + ms, err = ss.Team().GetMembers(teamId1, 0, 100, nil) + require.Nil(t, err) + require.Len(t, ms, 1) + require.Equal(t, m2.UserId, ms[0].UserId) _, err = ss.Team().SaveMember(m1, -1) require.Nil(t, err) - if err = ss.Team().RemoveAllMembersByTeam(teamId1); err != nil { - t.Fatal(err) - } + err = ss.Team().RemoveAllMembersByTeam(teamId1) + require.Nil(t, err) - if ms, err = ss.Team().GetMembers(teamId1, 0, 100, nil); err != nil { - t.Fatal(err) - } else { - - require.Len(t, ms, 0) - } + ms, err = ss.Team().GetMembers(teamId1, 0, 100, nil) + require.Nil(t, err) + require.Len(t, ms, 0) uid := model.NewId() m4 := &model.TeamMember{TeamId: teamId1, UserId: uid} @@ -930,23 +841,16 @@ func testTeamMembers(t *testing.T, ss store.Store) { _, err = ss.Team().SaveMember(m5, -1) require.Nil(t, err) - if ms, err = ss.Team().GetTeamsForUser(uid); err != nil { - t.Fatal(err) - } else { + ms, err = ss.Team().GetTeamsForUser(uid) + require.Nil(t, err) + require.Len(t, ms, 2) - require.Len(t, ms, 2) - } + err = ss.Team().RemoveAllMembersByUser(uid) + require.Nil(t, err) - if err = ss.Team().RemoveAllMembersByUser(uid); err != nil { - t.Fatal(err) - } - - if ms, err = ss.Team().GetTeamsForUser(m1.UserId); err != nil { - t.Fatal(err) - } else { - - require.Len(t, ms, 0) - } + ms, err = ss.Team().GetTeamsForUser(m1.UserId) + require.Nil(t, err) + require.Len(t, ms, 0) } func testTeamMembersWithPagination(t *testing.T, ss store.Store) { @@ -1044,11 +948,9 @@ func testSaveTeamMemberMaxMembers(t *testing.T, ss store.Store) { }(userIds[i]) } - if totalMemberCount, err := ss.Team().GetTotalMemberCount(team.Id, nil); err != nil { - t.Fatal(err) - } else if int(totalMemberCount) != maxUsersPerTeam { - t.Fatalf("should start with 5 team members, had %v instead", totalMemberCount) - } + totalMemberCount, err := ss.Team().GetTotalMemberCount(team.Id, nil) + require.Nil(t, err) + require.Equal(t, int(totalMemberCount), maxUsersPerTeam, "should start with 5 team members, had %v instead", totalMemberCount) user, err := ss.User().Save(&model.User{ Username: model.NewId(), @@ -1060,47 +962,36 @@ func testSaveTeamMemberMaxMembers(t *testing.T, ss store.Store) { ss.User().PermanentDelete(newUserId) }() - if _, err = ss.Team().SaveMember(&model.TeamMember{ + _, err = ss.Team().SaveMember(&model.TeamMember{ TeamId: team.Id, UserId: newUserId, - }, maxUsersPerTeam); err == nil { - t.Fatal("shouldn't be able to save member when at maximum members per team") - } + }, maxUsersPerTeam) + require.NotNil(t, err, "shouldn't be able to save member when at maximum members per team") - if totalMemberCount, teamErr := ss.Team().GetTotalMemberCount(team.Id, nil); teamErr != nil { - t.Fatal(teamErr) - } else if int(totalMemberCount) != maxUsersPerTeam { - t.Fatalf("should still have 5 team members, had %v instead", totalMemberCount) - } + totalMemberCount, teamErr := ss.Team().GetTotalMemberCount(team.Id, nil) + require.Nil(t, teamErr) + require.Equal(t, maxUsersPerTeam, int(totalMemberCount), "should still have 5 team members, had %v instead", totalMemberCount) // Leaving the team from the UI sets DeleteAt instead of using TeamStore.RemoveMember - if _, teamErr := ss.Team().UpdateMember(&model.TeamMember{ + _, teamErr = ss.Team().UpdateMember(&model.TeamMember{ TeamId: team.Id, UserId: userIds[0], DeleteAt: 1234, - }); teamErr != nil { - panic(teamErr) - } + }) + require.Nil(t, teamErr) - if totalMemberCount, teamErr := ss.Team().GetTotalMemberCount(team.Id, nil); teamErr != nil { - t.Fatal(teamErr) - } else if int(totalMemberCount) != maxUsersPerTeam-1 { - t.Fatalf("should now only have 4 team members, had %v instead", totalMemberCount) - } + totalMemberCount, teamErr = ss.Team().GetTotalMemberCount(team.Id, nil) + require.Nil(t, teamErr) + require.Equal(t, maxUsersPerTeam-1, int(totalMemberCount), "should now only have 4 team members, had %v instead", totalMemberCount) - if _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId}, maxUsersPerTeam); err != nil { - t.Fatal("should've been able to save new member after deleting one", err) - } else { - defer func(userId string) { - ss.Team().RemoveMember(team.Id, userId) - }(newUserId) - } + _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId}, maxUsersPerTeam) + require.Nil(t, err, "should've been able to save new member after deleting one") - if totalMemberCount, teamErr := ss.Team().GetTotalMemberCount(team.Id, nil); teamErr != nil { - t.Fatal(teamErr) - } else if int(totalMemberCount) != maxUsersPerTeam { - t.Fatalf("should have 5 team members again, had %v instead", totalMemberCount) - } + defer ss.Team().RemoveMember(team.Id, newUserId) + + totalMemberCount, teamErr = ss.Team().GetTotalMemberCount(team.Id, nil) + require.Nil(t, teamErr) + require.Equal(t, maxUsersPerTeam, int(totalMemberCount), "should have 5 team members again, had %v instead", totalMemberCount) // Deactivating a user should make them stop counting against max members user2, err := ss.User().Get(userIds[1]) @@ -1115,13 +1006,10 @@ func testSaveTeamMemberMaxMembers(t *testing.T, ss store.Store) { }) require.Nil(t, err) newUserId2 := user.Id - if _, err := ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId2}, maxUsersPerTeam); err != nil { - t.Fatal("should've been able to save new member after deleting one", err) - } else { - defer func(userId string) { - ss.Team().RemoveMember(team.Id, userId) - }(newUserId2) - } + _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId2}, maxUsersPerTeam) + require.Nil(t, err, "should've been able to save new member after deleting one") + + defer ss.Team().RemoveMember(team.Id, newUserId2) } func testGetTeamMember(t *testing.T, ss store.Store) { @@ -1132,26 +1020,18 @@ func testGetTeamMember(t *testing.T, ss store.Store) { require.Nil(t, err) var rm1 *model.TeamMember - if rm1, err = ss.Team().GetMember(m1.TeamId, m1.UserId); err != nil { - t.Fatal(err) - } else { + rm1, err = ss.Team().GetMember(m1.TeamId, m1.UserId) + require.Nil(t, err) - if rm1.TeamId != m1.TeamId { - t.Fatal("bad team id") - } + require.Equal(t, rm1.TeamId, m1.TeamId, "bad team id") - if rm1.UserId != m1.UserId { - t.Fatal("bad user id") - } - } + require.Equal(t, rm1.UserId, m1.UserId, "bad user id") - if _, err = ss.Team().GetMember(m1.TeamId, ""); err == nil { - t.Fatal("empty user id - should have failed") - } + _, err = ss.Team().GetMember(m1.TeamId, "") + require.NotNil(t, err, "empty user id - should have failed") - if _, err = ss.Team().GetMember("", m1.UserId); err == nil { - t.Fatal("empty team id - should have failed") - } + _, err = ss.Team().GetMember("", m1.UserId) + require.NotNil(t, err, "empty team id - should have failed") // Test with a custom team scheme. s2 := &model.Scheme{ @@ -1204,36 +1084,24 @@ func testGetTeamMembersByIds(t *testing.T, ss store.Store) { require.Nil(t, err) var r []*model.TeamMember - if r, err = ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId}, nil); err != nil { - t.Fatal(err) - } else { - rm1 := r[0] + r, err = ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId}, nil) + require.Nil(t, err) + rm1 := r[0] - if rm1.TeamId != m1.TeamId { - t.Fatal("bad team id") - } - - if rm1.UserId != m1.UserId { - t.Fatal("bad user id") - } - } + require.Equal(t, rm1.TeamId, m1.TeamId, "bad team id") + require.Equal(t, rm1.UserId, m1.UserId, "bad user id") m2 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} _, err = ss.Team().SaveMember(m2, -1) require.Nil(t, err) - if rm, err := ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId, m2.UserId, model.NewId()}, nil); err != nil { - t.Fatal(err) - } else { + rm, err := ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId, m2.UserId, model.NewId()}, nil) + require.Nil(t, err) - if len(rm) != 2 { - t.Fatal("return wrong number of results") - } - } + require.Len(t, rm, 2, "return wrong number of results") - if _, err := ss.Team().GetMembersByIds(m1.TeamId, []string{}, nil); err == nil { - t.Fatal("empty user ids - should have failed") - } + _, err = ss.Team().GetMembersByIds(m1.TeamId, []string{}, nil) + require.NotNil(t, err, "empty user ids - should have failed") } func testTeamStoreMemberCount(t *testing.T, ss store.Store) { @@ -1258,42 +1126,26 @@ func testTeamStoreMemberCount(t *testing.T, ss store.Store) { require.Nil(t, err) var totalMemberCount int64 - if totalMemberCount, err = ss.Team().GetTotalMemberCount(teamId1, nil); err != nil { - t.Fatal(err) - } else { - if totalMemberCount != 2 { - t.Fatal("wrong count") - } - } + totalMemberCount, err = ss.Team().GetTotalMemberCount(teamId1, nil) + require.Nil(t, err) + require.Equal(t, int(totalMemberCount), 2, "wrong count") var result int64 - if result, err = ss.Team().GetActiveMemberCount(teamId1, nil); err != nil { - t.Fatal(err) - } else { - if result != 1 { - t.Fatal("wrong count") - } - } + result, err = ss.Team().GetActiveMemberCount(teamId1, nil) + require.Nil(t, err) + require.Equal(t, 1, int(result), "wrong count") m3 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} _, err = ss.Team().SaveMember(m3, -1) require.Nil(t, err) - if totalMemberCount, err := ss.Team().GetTotalMemberCount(teamId1, nil); err != nil { - t.Fatal(err) - } else { - if totalMemberCount != 2 { - t.Fatal("wrong count") - } - } + totalMemberCount, err = ss.Team().GetTotalMemberCount(teamId1, nil) + require.Nil(t, err) + require.Equal(t, 2, int(totalMemberCount), "wrong count") - if result, err := ss.Team().GetActiveMemberCount(teamId1, nil); err != nil { - t.Fatal(err) - } else { - if result != 1 { - t.Fatal("wrong count") - } - } + result, err = ss.Team().GetActiveMemberCount(teamId1, nil) + require.Nil(t, err) + require.Equal(t, 1, int(result), "wrong count") } func testGetChannelUnreadsForAllTeams(t *testing.T, ss store.Store) { @@ -1323,48 +1175,35 @@ func testGetChannelUnreadsForAllTeams(t *testing.T, ss store.Store) { _, err = ss.Channel().SaveMember(cm2) require.Nil(t, err) - if ms1, err := ss.Team().GetChannelUnreadsForAllTeams("", uid); err != nil { - t.Fatal(err) - } else { - membersMap := make(map[string]bool) - for i := range ms1 { - id := ms1[i].TeamId - if _, ok := membersMap[id]; !ok { - membersMap[id] = true - } - } - if len(membersMap) != 2 { - t.Fatal("Should be the unreads for all the teams") + ms1, err := ss.Team().GetChannelUnreadsForAllTeams("", uid) + require.Nil(t, err) + membersMap := make(map[string]bool) + for i := range ms1 { + id := ms1[i].TeamId + if _, ok := membersMap[id]; !ok { + membersMap[id] = true } + } + require.Len(t, membersMap, 2, "Should be the unreads for all the teams") - if ms1[0].MsgCount != 10 { - t.Fatal("subtraction failed") + require.Equal(t, 10, int(ms1[0].MsgCount), "subtraction failed") + + ms2, err := ss.Team().GetChannelUnreadsForAllTeams(teamId1, uid) + require.Nil(t, err) + membersMap = make(map[string]bool) + for i := range ms2 { + id := ms2[i].TeamId + if _, ok := membersMap[id]; !ok { + membersMap[id] = true } } - if ms2, err := ss.Team().GetChannelUnreadsForAllTeams(teamId1, uid); err != nil { - t.Fatal(err) - } else { - membersMap := make(map[string]bool) - for i := range ms2 { - id := ms2[i].TeamId - if _, ok := membersMap[id]; !ok { - membersMap[id] = true - } - } + require.Len(t, membersMap, 1, "Should be the unreads for just one team") - if len(membersMap) != 1 { - t.Fatal("Should be the unreads for just one team") - } + require.Equal(t, 10, int(ms2[0].MsgCount), "subtraction failed") - if ms2[0].MsgCount != 10 { - t.Fatal("subtraction failed") - } - } - - if err := ss.Team().RemoveAllMembersByUser(uid); err != nil { - t.Fatal(err) - } + err = ss.Team().RemoveAllMembersByUser(uid) + require.Nil(t, err) } func testGetChannelUnreadsForTeam(t *testing.T, ss store.Store) { @@ -1390,17 +1229,11 @@ func testGetChannelUnreadsForTeam(t *testing.T, ss store.Store) { _, err = ss.Channel().SaveMember(cm2) require.Nil(t, err) - if ms, err := ss.Team().GetChannelUnreadsForTeam(m1.TeamId, m1.UserId); err != nil { - t.Fatal(err) - } else { - if len(ms) != 2 { - t.Fatal("wrong length") - } + ms, err := ss.Team().GetChannelUnreadsForTeam(m1.TeamId, m1.UserId) + require.Nil(t, err) + require.Len(t, ms, 2, "wrong length") - if ms[0].MsgCount != 10 { - t.Fatal("subtraction failed") - } - } + require.Equal(t, 10, int(ms[0].MsgCount), "subtraction failed") } func testUpdateLastTeamIconUpdate(t *testing.T, ss store.Store) { @@ -1419,16 +1252,13 @@ func testUpdateLastTeamIconUpdate(t *testing.T, ss store.Store) { curTime := model.GetMillis() - if err = ss.Team().UpdateLastTeamIconUpdate(o1.Id, curTime); err != nil { - t.Fatal(err) - } + err = ss.Team().UpdateLastTeamIconUpdate(o1.Id, curTime) + require.Nil(t, err) ro1, err := ss.Team().Get(o1.Id) require.Nil(t, err) - if ro1.LastTeamIconUpdate <= lastTeamIconUpdateInitial { - t.Fatal("LastTeamIconUpdate not updated") - } + require.Greater(t, ro1.LastTeamIconUpdate, lastTeamIconUpdateInitial, "LastTeamIconUpdate not updated") } func testGetTeamsByScheme(t *testing.T, ss store.Store) {