From 668518f0f49e3ef9fb9297b505a1a083596f1671 Mon Sep 17 00:00:00 2001 From: Muhammad S <841955+mhd-sln@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:29:56 +0200 Subject: [PATCH 1/8] [MM-48759] track event when redirect from cws signup with utm campaign --- api4/user.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api4/user.go b/api4/user.go index a35d1d6597..0d09f8f7eb 100644 --- a/api4/user.go +++ b/api4/user.go @@ -2017,9 +2017,15 @@ func loginCWS(c *Context, w http.ResponseWriter, r *http.Request) { redirectURL := *c.App.Config().ServiceSettings.SiteURL if campaign != "" { if url, ok := campaignToURL[campaign]; ok { + properties := map[string]any{ + "category": "acquisition", + "redirect_to": strings.TrimSuffix(url, "/"), + } + c.App.Srv().GetTelemetryService().SendTelemetry("product_start_redirect", properties) redirectURL += url } } + http.Redirect(w, r, redirectURL, http.StatusFound) } From 93f17980a113f3f476a0614be367a52b17f49f89 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Thu, 29 Dec 2022 12:21:25 +0530 Subject: [PATCH 2/8] Remove unnecessary debug log (#21959) This log is continuously spamming our community server and I see it all the time in my dev environment. Let's remove this to keep the logs clean. ```release-note NONE ``` --- api4/drafts.go | 1 - 1 file changed, 1 deletion(-) diff --git a/api4/drafts.go b/api4/drafts.go index 102c164b8f..c3640f7a84 100644 --- a/api4/drafts.go +++ b/api4/drafts.go @@ -124,7 +124,6 @@ func deleteDraft(c *Context, w http.ResponseWriter, r *http.Request) { switch { case err.StatusCode == http.StatusNotFound: // If the draft doesn't exist in the server, we don't need to delete. - mlog.Debug("Unable to find the draft", mlog.Err(err)) ReturnStatusOK(w) default: c.Err = err From babe01ee2ed4440499a699519b6407ad5de515d1 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 30 Dec 2022 14:41:31 +0530 Subject: [PATCH 3/8] remove crt feature flag (#21788) Co-authored-by: Mattermod --- api4/channel_test.go | 3 --- api4/post_test.go | 3 --- api4/user_test.go | 20 ++++---------------- app/channel_test.go | 9 ++------- app/notification.go | 2 +- app/notification_test.go | 4 +--- app/post_test.go | 16 ---------------- app/user_test.go | 3 --- model/feature_flags.go | 4 ---- 9 files changed, 8 insertions(+), 56 deletions(-) diff --git a/api4/channel_test.go b/api4/channel_test.go index 16e432bf0d..6f8f5aeabe 100644 --- a/api4/channel_test.go +++ b/api4/channel_test.go @@ -8,7 +8,6 @@ import ( "encoding/json" "fmt" "net/http" - "os" "sort" "strings" "sync" @@ -4584,8 +4583,6 @@ func TestViewChannelWithoutCollapsedThreads(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn diff --git a/api4/post_test.go b/api4/post_test.go index 8594fae136..7f3ec6c442 100644 --- a/api4/post_test.go +++ b/api4/post_test.go @@ -12,7 +12,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "reflect" "sort" "strings" @@ -2954,8 +2953,6 @@ func TestSetChannelUnread(t *testing.T) { } func TestSetPostUnreadWithoutCollapsedThreads(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th := Setup(t).InitBasic() defer th.TearDown() th.App.UpdateConfig(func(cfg *model.Config) { diff --git a/api4/user_test.go b/api4/user_test.go index affd768fd7..a20ae682a1 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -5794,8 +5794,6 @@ func TestUpdatePassword(t *testing.T) { func TestGetThreadsForUser(t *testing.T) { os.Setenv("MM_FEATUREFLAGS_POSTPRIORITY", "true") defer os.Unsetenv("MM_FEATUREFLAGS_POSTPRIORITY") - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th := Setup(t).InitBasic() defer th.TearDown() @@ -6151,8 +6149,6 @@ func TestGetThreadsForUser(t *testing.T) { func TestThreadSocketEvents(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.ConfigStore.SetReadOnlyFF(false) defer th.ConfigStore.SetReadOnlyFF(true) @@ -6533,8 +6529,6 @@ func TestMaintainUnreadRepliesInThread(t *testing.T) { defer th.UnlinkUserFromTeam(th.SystemAdminUser, th.BasicTeam) th.AddUserToChannel(th.SystemAdminUser, th.BasicChannel) defer th.RemoveUserFromChannel(th.SystemAdminUser, th.BasicChannel) - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn @@ -6590,8 +6584,7 @@ func TestMaintainUnreadRepliesInThread(t *testing.T) { func TestThreadCounts(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn @@ -6633,8 +6626,6 @@ func TestThreadCounts(t *testing.T) { func TestSingleThreadGet(t *testing.T) { os.Setenv("MM_FEATUREFLAGS_POSTPRIORITY", "true") defer os.Unsetenv("MM_FEATUREFLAGS_POSTPRIORITY") - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th := Setup(t).InitBasic() defer th.TearDown() @@ -6704,8 +6695,7 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) { th.AddUserToChannel(th.SystemAdminUser, th.BasicChannel) defer th.RemoveUserFromChannel(th.SystemAdminUser, th.BasicChannel) client := th.Client - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn @@ -6768,8 +6758,7 @@ func TestMaintainUnreadMentionsInThread(t *testing.T) { func TestReadThreads(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn @@ -6872,8 +6861,7 @@ func TestReadThreads(t *testing.T) { func TestMarkThreadUnreadMentionCount(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn diff --git a/app/channel_test.go b/app/channel_test.go index bf21274763..af162e45b9 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "net/http" - "os" "sort" "strings" "sync" @@ -2127,8 +2126,7 @@ func TestViewChannelCollapsedThreadsTurnedOff(t *testing.T) { th.AddUserToChannel(u2, c1) // Enable CRT - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn @@ -2198,8 +2196,6 @@ func TestViewChannelCollapsedThreadsTurnedOff(t *testing.T) { func TestMarkChannelAsUnreadFromPostCollapsedThreadsTurnedOff(t *testing.T) { // Enable CRT - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th := Setup(t).InitBasic() defer th.TearDown() @@ -2286,8 +2282,7 @@ func TestMarkChannelAsUnreadFromPostCollapsedThreadsTurnedOff(t *testing.T) { } func TestMarkUnreadCRTOffUpdatesThreads(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th := Setup(t).InitBasic() defer th.TearDown() th.App.UpdateConfig(func(cfg *model.Config) { diff --git a/app/notification.go b/app/notification.go index cd275134d4..215945405c 100644 --- a/app/notification.go +++ b/app/notification.go @@ -43,7 +43,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea return []string{}, nil } - isCRTAllowed := a.Config().FeatureFlags.CollapsedThreads && *a.Config().ServiceSettings.CollapsedThreads != model.CollapsedThreadsDisabled + isCRTAllowed := *a.Config().ServiceSettings.CollapsedThreads != model.CollapsedThreadsDisabled pchan := make(chan store.StoreResult, 1) go func() { diff --git a/app/notification_test.go b/app/notification_test.go index 1563be191d..f00c038dac 100644 --- a/app/notification_test.go +++ b/app/notification_test.go @@ -5,7 +5,6 @@ package app import ( "fmt" - "os" "testing" "github.com/stretchr/testify/assert" @@ -2734,8 +2733,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) { }() // Enable CRT - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn diff --git a/app/post_test.go b/app/post_test.go index 0b9c37a3f8..d3073fe302 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -2357,8 +2357,6 @@ func TestThreadMembership(t *testing.T) { func TestFollowThreadSkipsParticipants(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true @@ -2415,8 +2413,6 @@ func TestFollowThreadSkipsParticipants(t *testing.T) { func TestAutofollowBasedOnRootPost(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true @@ -2445,8 +2441,6 @@ func TestAutofollowBasedOnRootPost(t *testing.T) { func TestViewChannelShouldNotUpdateThreads(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true @@ -2528,12 +2522,6 @@ func TestCollapsedThreadFetch(t *testing.T) { }) t.Run("Should not panic on unexpected db error", func(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") - th.App.UpdateConfig(func(cfg *model.Config) { - cfg.FeatureFlags.CollapsedThreads = true - }) - channel := th.CreateChannel(th.Context, th.BasicTeam) th.AddUserToChannel(user2, channel) defer th.App.DeleteChannel(th.Context, channel, user1.Id) @@ -2765,8 +2753,6 @@ func TestSharedChannelSyncForPostActions(t *testing.T) { func TestAutofollowOnPostingAfterUnfollow(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true @@ -2834,8 +2820,6 @@ func TestGetPostIfAuthorized(t *testing.T) { func TestShouldNotRefollowOnOthersReply(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ThreadAutoFollow = true diff --git a/app/user_test.go b/app/user_test.go index 788ae11275..8a71680a20 100644 --- a/app/user_test.go +++ b/app/user_test.go @@ -8,7 +8,6 @@ import ( "context" "encoding/json" "errors" - "os" "path/filepath" "strings" "testing" @@ -1673,8 +1672,6 @@ func TestPatchUser(t *testing.T) { } func TestUpdateThreadReadForUser(t *testing.T) { - os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true") - defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS") t.Run("Ensure thread membership is created and followed", func(t *testing.T) { th := Setup(t).InitBasic() diff --git a/model/feature_flags.go b/model/feature_flags.go index 27bab4830e..47dd10378d 100644 --- a/model/feature_flags.go +++ b/model/feature_flags.go @@ -16,9 +16,6 @@ type FeatureFlags struct { // all other values as false. TestBoolFeature bool - // Toggle on and off support for Collapsed Threads - CollapsedThreads bool - // Enable the remote cluster service for shared channels. EnableRemoteClusterService bool @@ -81,7 +78,6 @@ type FeatureFlags struct { func (f *FeatureFlags) SetDefaults() { f.TestFeature = "off" f.TestBoolFeature = false - f.CollapsedThreads = true f.EnableRemoteClusterService = false f.AppsEnabled = true f.PluginApps = "" From f1e96cb83076ba5f6ad6d3f9a73264e1baaa33f7 Mon Sep 17 00:00:00 2001 From: Luis Suarez Date: Mon, 2 Jan 2023 18:07:38 -0500 Subject: [PATCH 4/8] MM-48988 Log plugins version on install and on startup (#21907) --- app/plugin_install.go | 2 ++ plugin/environment.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/plugin_install.go b/app/plugin_install.go index 431c3abebe..b35982b82c 100644 --- a/app/plugin_install.go +++ b/app/plugin_install.go @@ -402,6 +402,8 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD manifest = updatedManifest } + mlog.Debug("Installing plugin", mlog.String("plugin_id", manifest.Id), mlog.String("version", manifest.Version)) + return manifest, nil } diff --git a/plugin/environment.go b/plugin/environment.go index 4ccbbdf023..cf7553579d 100644 --- a/plugin/environment.go +++ b/plugin/environment.go @@ -330,6 +330,8 @@ func (env *Environment) Activate(id string) (manifest *model.Manifest, activated return nil, false, fmt.Errorf("unable to start plugin: must at least have a web app or server component") } + mlog.Debug("Plugin activated", mlog.String("plugin_id", pluginInfo.Manifest.Id), mlog.String("version", pluginInfo.Manifest.Version)) + return pluginInfo.Manifest, true, nil } From 1353ebc3abf34c8767bf776e618361b870145bca Mon Sep 17 00:00:00 2001 From: Nathaniel Allred Date: Tue, 3 Jan 2023 08:36:23 -0600 Subject: [PATCH 5/8] Mm 49258 - Inform admins trying to self-hosted purchase who the email listed on the JWT claim is (#21964) * add email of JWT claim to bootstrap signup response --- model/hosted_customer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/model/hosted_customer.go b/model/hosted_customer.go index 0e1373a2bc..4f1917bdaf 100644 --- a/model/hosted_customer.go +++ b/model/hosted_customer.go @@ -10,6 +10,8 @@ type BootstrapSelfHostedSignupRequest struct { type BootstrapSelfHostedSignupResponse struct { Progress string `json:"progress"` + // email listed on the JWT claim + Email string `json:"email"` } type BootstrapSelfHostedSignupResponseInternal struct { From 6ad18615352fd9b7f36fb00072a25968e1abc00a Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Tue, 3 Jan 2023 20:14:16 +0530 Subject: [PATCH 6/8] MM-48345: Fix Critical level logging (#21952) Critical wasn't added in the StdLevels slice and due to that mlog.Critical wasn't printing anything. This was a complete blindspot that was missed. We have removed all references to mlog.Critical in the codebase and pointed Critical to be Fatal in the library. In v8, we will remove Critical altogether. ```release-note Servers with an encrypted key will throw an error during startup now. ``` --- app/migrations.go | 74 ++++++++++++++--------------- app/platform/metrics.go | 2 +- app/server.go | 6 +-- services/upgrader/upgrader_linux.go | 6 +-- shared/mlog/global.go | 9 ++-- shared/mlog/global_test.go | 14 ++---- 6 files changed, 51 insertions(+), 60 deletions(-) diff --git a/app/migrations.go b/app/migrations.go index 21295aa840..70c11b75fc 100644 --- a/app/migrations.go +++ b/app/migrations.go @@ -46,7 +46,7 @@ func (s *Server) doAdvancedPermissionsMigration() { // If this failed for reasons other than the role already existing, don't mark the migration as done. fetchedRole, err := s.Store().Role().GetByName(context.Background(), role.Name) if err != nil { - mlog.Critical("Failed to migrate role to database.", mlog.Err(err)) + mlog.Fatal("Failed to migrate role to database.", mlog.Err(err)) allSucceeded = false continue } @@ -59,7 +59,7 @@ func (s *Server) doAdvancedPermissionsMigration() { role.Id = fetchedRole.Id if _, err = s.Store().Role().Save(role); err != nil { // Role is not the same, but failed to update. - mlog.Critical("Failed to migrate role to database.", mlog.Err(err)) + mlog.Fatal("Failed to migrate role to database.", mlog.Err(err)) allSucceeded = false } } @@ -81,7 +81,7 @@ func (s *Server) doAdvancedPermissionsMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark advanced permissions migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark advanced permissions migration as completed.", mlog.Err(err)) } } @@ -114,21 +114,21 @@ func (s *Server) doEmojisPermissionsMigration() { // Emoji creation is set to all by default role, err = s.GetRoleByName(context.Background(), model.SystemUserRoleId) if err != nil { - mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) + mlog.Fatal("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) return } if role != nil { role.Permissions = append(role.Permissions, model.PermissionCreateEmojis.Id, model.PermissionDeleteEmojis.Id) if _, nErr := s.Store().Role().Save(role); nErr != nil { - mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(nErr)) + mlog.Fatal("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(nErr)) return } } systemAdminRole, err = s.GetRoleByName(context.Background(), model.SystemAdminRoleId) if err != nil { - mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) + mlog.Fatal("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) return } @@ -138,7 +138,7 @@ func (s *Server) doEmojisPermissionsMigration() { model.PermissionDeleteOthersEmojis.Id, ) if _, err := s.Store().Role().Save(systemAdminRole); err != nil { - mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) + mlog.Fatal("Failed to migrate emojis creation permissions from mattermost config.", mlog.Err(err)) return } @@ -148,7 +148,7 @@ func (s *Server) doEmojisPermissionsMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark emojis permissions migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark emojis permissions migration as completed.", mlog.Err(err)) } } @@ -167,26 +167,26 @@ func (s *Server) doGuestRolesCreationMigration() { allSucceeded := true if _, err := s.Store().Role().GetByName(context.Background(), model.ChannelGuestRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.ChannelGuestRoleId]); err != nil { - mlog.Critical("Failed to create new guest role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new guest role to database.", mlog.Err(err)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.TeamGuestRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.TeamGuestRoleId]); err != nil { - mlog.Critical("Failed to create new guest role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new guest role to database.", mlog.Err(err)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.SystemGuestRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.SystemGuestRoleId]); err != nil { - mlog.Critical("Failed to create new guest role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new guest role to database.", mlog.Err(err)) allSucceeded = false } } schemes, err := s.Store().Scheme().GetAllPage("", 0, 1000000) if err != nil { - mlog.Critical("Failed to get all schemes.", mlog.Err(err)) + mlog.Fatal("Failed to get all schemes.", mlog.Err(err)) allSucceeded = false } for _, scheme := range schemes { @@ -201,7 +201,7 @@ func (s *Server) doGuestRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(teamGuestRole); err != nil { - mlog.Critical("Failed to create new guest role for custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new guest role for custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultTeamGuestRole = savedRole.Name @@ -217,7 +217,7 @@ func (s *Server) doGuestRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(channelGuestRole); err != nil { - mlog.Critical("Failed to create new guest role for custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new guest role for custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultChannelGuestRole = savedRole.Name @@ -225,7 +225,7 @@ func (s *Server) doGuestRolesCreationMigration() { _, err := s.Store().Scheme().Save(scheme) if err != nil { - mlog.Critical("Failed to update custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to update custom scheme.", mlog.Err(err)) allSucceeded = false } } @@ -241,7 +241,7 @@ func (s *Server) doGuestRolesCreationMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark guest roles creation migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark guest roles creation migration as completed.", mlog.Err(err)) } } @@ -260,19 +260,19 @@ func (s *Server) doSystemConsoleRolesCreationMigration() { allSucceeded := true if _, err := s.Store().Role().GetByName(context.Background(), model.SystemManagerRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.SystemManagerRoleId]); err != nil { - mlog.Critical("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemManagerRoleId)) + mlog.Fatal("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemManagerRoleId)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.SystemReadOnlyAdminRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.SystemReadOnlyAdminRoleId]); err != nil { - mlog.Critical("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemReadOnlyAdminRoleId)) + mlog.Fatal("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemReadOnlyAdminRoleId)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.SystemUserManagerRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.SystemUserManagerRoleId]); err != nil { - mlog.Critical("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemUserManagerRoleId)) + mlog.Fatal("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemUserManagerRoleId)) allSucceeded = false } } @@ -287,7 +287,7 @@ func (s *Server) doSystemConsoleRolesCreationMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark system console roles creation migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark system console roles creation migration as completed.", mlog.Err(err)) } } @@ -302,7 +302,7 @@ func (s *Server) doCustomGroupAdminRoleCreationMigration() { allSucceeded := true if _, err := s.Store().Role().GetByName(context.Background(), model.SystemCustomGroupAdminRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.SystemCustomGroupAdminRoleId]); err != nil { - mlog.Critical("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemCustomGroupAdminRoleId)) + mlog.Fatal("Failed to create new role.", mlog.Err(err), mlog.String("role", model.SystemCustomGroupAdminRoleId)) allSucceeded = false } } @@ -317,7 +317,7 @@ func (s *Server) doCustomGroupAdminRoleCreationMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark custom group admin role creation migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark custom group admin role creation migration as completed.", mlog.Err(err)) } } @@ -337,7 +337,7 @@ func (s *Server) doContentExtractionConfigDefaultTrueMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark content extraction config migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark content extraction config migration as completed.", mlog.Err(err)) } } @@ -352,31 +352,31 @@ func (s *Server) doPlaybooksRolesCreationMigration() { allSucceeded := true if _, err := s.Store().Role().GetByName(context.Background(), model.PlaybookAdminRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.PlaybookAdminRoleId]); err != nil { - mlog.Critical("Failed to create new playbook admin role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new playbook admin role to database.", mlog.Err(err)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.PlaybookMemberRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.PlaybookMemberRoleId]); err != nil { - mlog.Critical("Failed to create new playbook member role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new playbook member role to database.", mlog.Err(err)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.RunAdminRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.RunAdminRoleId]); err != nil { - mlog.Critical("Failed to create new run admin role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new run admin role to database.", mlog.Err(err)) allSucceeded = false } } if _, err := s.Store().Role().GetByName(context.Background(), model.RunMemberRoleId); err != nil { if _, err := s.Store().Role().Save(roles[model.RunMemberRoleId]); err != nil { - mlog.Critical("Failed to create new run member role to database.", mlog.Err(err)) + mlog.Fatal("Failed to create new run member role to database.", mlog.Err(err)) allSucceeded = false } } schemes, err := s.Store().Scheme().GetAllPage(model.SchemeScopeTeam, 0, 1000000) if err != nil { - mlog.Critical("Failed to get all schemes.", mlog.Err(err)) + mlog.Fatal("Failed to get all schemes.", mlog.Err(err)) allSucceeded = false } @@ -391,7 +391,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(playbookAdminRole); err != nil { - mlog.Critical("Failed to create new playbook admin role for existing custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new playbook admin role for existing custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultPlaybookAdminRole = savedRole.Name @@ -406,7 +406,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(playbookMember); err != nil { - mlog.Critical("Failed to create new playbook member role for existing custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new playbook member role for existing custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultPlaybookMemberRole = savedRole.Name @@ -422,7 +422,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(runAdminRole); err != nil { - mlog.Critical("Failed to create new run admin role for existing custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new run admin role for existing custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultRunAdminRole = savedRole.Name @@ -438,7 +438,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } if savedRole, err := s.Store().Role().Save(runMemberRole); err != nil { - mlog.Critical("Failed to create new run member role for existing custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to create new run member role for existing custom scheme.", mlog.Err(err)) allSucceeded = false } else { scheme.DefaultRunMemberRole = savedRole.Name @@ -446,7 +446,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } _, err := s.Store().Scheme().Save(scheme) if err != nil { - mlog.Critical("Failed to update custom scheme.", mlog.Err(err)) + mlog.Fatal("Failed to update custom scheme.", mlog.Err(err)) allSucceeded = false } } @@ -462,7 +462,7 @@ func (s *Server) doPlaybooksRolesCreationMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark playbook roles creation migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark playbook roles creation migration as completed.", mlog.Err(err)) } } @@ -507,7 +507,7 @@ func (s *Server) doFirstAdminSetupCompleteMigration() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark first admin setup migration as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark first admin setup migration as completed.", mlog.Err(err)) } } @@ -534,7 +534,7 @@ func (s *Server) doRemainingSchemaMigrations() { } if err := s.Store().System().Save(&system); err != nil { - mlog.Critical("Failed to mark the remaining schema migrations as completed.", mlog.Err(err)) + mlog.Fatal("Failed to mark the remaining schema migrations as completed.", mlog.Err(err)) } } @@ -552,7 +552,7 @@ func (s *Server) doAppMigrations() { // migrations. For example, it needs the guest roles migration. err := s.doPermissionsMigrations() if err != nil { - mlog.Critical("(app.App).DoPermissionsMigrations failed", mlog.Err(err)) + mlog.Fatal("(app.App).DoPermissionsMigrations failed", mlog.Err(err)) } s.doContentExtractionConfigDefaultTrueMigration() s.doPlaybooksRolesCreationMigration() diff --git a/app/platform/metrics.go b/app/platform/metrics.go index 2253594011..f30edb5093 100644 --- a/app/platform/metrics.go +++ b/app/platform/metrics.go @@ -111,7 +111,7 @@ func (pm *platformMetrics) startMetricsServer() error { go func() { close(notify) if err := pm.server.Serve(l); err != nil && err != http.ErrServerClosed { - pm.logger.Critical(err.Error()) + pm.logger.Fatal(err.Error()) } }() diff --git a/app/server.go b/app/server.go index fbd433a6db..dc66353ee4 100644 --- a/app/server.go +++ b/app/server.go @@ -1059,7 +1059,7 @@ func (s *Server) Start() error { } if err != nil && err != http.ErrServerClosed { - mlog.Critical("Error starting server", mlog.Err(err)) + mlog.Fatal("Error starting server", mlog.Err(err)) time.Sleep(time.Second) } @@ -1068,7 +1068,7 @@ func (s *Server) Start() error { if *s.platform.Config().ServiceSettings.EnableLocalMode { if err := s.startLocalModeServer(); err != nil { - mlog.Critical(err.Error()) + mlog.Fatal(err.Error()) } } @@ -1100,7 +1100,7 @@ func (s *Server) startLocalModeServer() error { go func() { err = s.localModeServer.Serve(unixListener) if err != nil && err != http.ErrServerClosed { - mlog.Critical("Error starting unix socket server", mlog.Err(err)) + mlog.Fatal("Error starting unix socket server", mlog.Err(err)) } }() return nil diff --git a/services/upgrader/upgrader_linux.go b/services/upgrader/upgrader_linux.go index 1f895480ea..04ad1bcf82 100644 --- a/services/upgrader/upgrader_linux.go +++ b/services/upgrader/upgrader_linux.go @@ -333,7 +333,7 @@ func extractBinary(executablePath string, filename string) error { if err != nil { err2 := os.Rename(tmpFileName, executablePath) if err2 != nil { - mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") + mlog.Fatal("Unable to restore the backup of the executable file. Restore the executable file manually.") return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") } return err @@ -342,13 +342,13 @@ func extractBinary(executablePath string, filename string) error { if _, err = io.Copy(outFile, tarReader); err != nil { err2 := os.Remove(executablePath) if err2 != nil { - mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") + mlog.Fatal("Unable to restore the backup of the executable file. Restore the executable file manually.") return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") } err2 = os.Rename(tmpFileName, executablePath) if err2 != nil { - mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") + mlog.Fatal("Unable to restore the backup of the executable file. Restore the executable file manually.") return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") } return err diff --git a/shared/mlog/global.go b/shared/mlog/global.go index de346123aa..71d7430a42 100644 --- a/shared/mlog/global.go +++ b/shared/mlog/global.go @@ -113,13 +113,10 @@ func Error(msg string, fields ...Field) { // Convenience method equivalent to calling `Log` with the `Critical` level. // DEPRECATED: Either use Error or Fatal. +// Critical level isn't added in mlog/levels.go:StdAll so calling this doesn't +// really work. For now we just call Fatal to atleast print something. func Critical(msg string, fields ...Field) { - logger := getGlobalLogger() - if logger == nil { - defaultLog(LvlCritical, msg, fields...) - return - } - logger.Critical(msg, fields...) + Fatal(msg, fields...) } func Fatal(msg string, fields ...Field) { diff --git a/shared/mlog/global_test.go b/shared/mlog/global_test.go index d97b3b15a3..486387e5f6 100644 --- a/shared/mlog/global_test.go +++ b/shared/mlog/global_test.go @@ -24,7 +24,6 @@ func TestLoggingBeforeInitialized(t *testing.T) { mlog.Debug("debug log") mlog.Warn("warning log") mlog.Error("error log") - mlog.Critical("critical log") }) } @@ -40,14 +39,13 @@ func TestLoggingAfterInitialized(t *testing.T) { Type: "file", Format: "json", FormatOptions: json.RawMessage(`{"enable_caller":true}`), - Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, + Levels: []mlog.Level{mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, }, []string{ `{"timestamp":0,"level":"debug","msg":"real debug log","caller":"mlog/global_test.go:0"}`, `{"timestamp":0,"level":"info","msg":"real info log","caller":"mlog/global_test.go:0"}`, `{"timestamp":0,"level":"warn","msg":"real warning log","caller":"mlog/global_test.go:0"}`, `{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`, - `{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`, }, }, { @@ -56,11 +54,10 @@ func TestLoggingAfterInitialized(t *testing.T) { Type: "file", Format: "json", FormatOptions: json.RawMessage(`{"enable_caller":true}`), - Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError}, + Levels: []mlog.Level{mlog.LvlError}, }, []string{ `{"timestamp":0,"level":"error","msg":"real error log","caller":"mlog/global_test.go:0"}`, - `{"timestamp":0,"level":"critical","msg":"real critical log","caller":"mlog/global_test.go:0"}`, }, }, { @@ -69,14 +66,13 @@ func TestLoggingAfterInitialized(t *testing.T) { Type: "file", Format: "plain", FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`), - Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, + Levels: []mlog.Level{mlog.LvlError, mlog.LvlWarn, mlog.LvlInfo, mlog.LvlDebug}, }, []string{ `debug | TIME | real debug log | caller="mlog/global_test.go:0"`, `info | TIME | real info log | caller="mlog/global_test.go:0"`, `warn | TIME | real warning log | caller="mlog/global_test.go:0"`, `error | TIME | real error log | caller="mlog/global_test.go:0"`, - `critical | TIME | real critical log | caller="mlog/global_test.go:0"`, }, }, { @@ -85,11 +81,10 @@ func TestLoggingAfterInitialized(t *testing.T) { Type: "file", Format: "plain", FormatOptions: json.RawMessage(`{"delim":" | ", "enable_caller":true}`), - Levels: []mlog.Level{mlog.LvlCritical, mlog.LvlError}, + Levels: []mlog.Level{mlog.LvlError}, }, []string{ `error | TIME | real error log | caller="mlog/global_test.go:0"`, - `critical | TIME | real critical log | caller="mlog/global_test.go:0"`, }, }, } @@ -116,7 +111,6 @@ func TestLoggingAfterInitialized(t *testing.T) { mlog.Info("real info log") mlog.Warn("real warning log") mlog.Error("real error log") - mlog.Critical("real critical log") logger.Shutdown() From 356188de2884ae51f8c0747ecfe85793f171ae65 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Tue, 3 Jan 2023 20:14:45 +0530 Subject: [PATCH 7/8] Clean untranslated logs (#21956) Remove the word "" from appearing in the logs. ```release-note NONE ``` --- model/utils.go | 8 ++++++-- model/utils_test.go | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/model/utils.go b/model/utils.go index a8d82e3669..4ed85e2b32 100644 --- a/model/utils.go +++ b/model/utils.go @@ -256,11 +256,15 @@ func (er *AppError) Error() string { // render the error information sb.WriteString(er.Where) sb.WriteString(": ") - sb.WriteString(er.Message) + if er.Message != NoTranslation { + sb.WriteString(er.Message) + } // only render the detailed error when it's present if er.DetailedError != "" { - sb.WriteString(", ") + if er.Message != NoTranslation { + sb.WriteString(", ") + } sb.WriteString(er.DetailedError) } diff --git a/model/utils_test.go b/model/utils_test.go index 1b96f44516..606477d750 100644 --- a/model/utils_test.go +++ b/model/utils_test.go @@ -81,6 +81,11 @@ func TestAppError(t *testing.T) { t.Log(appErr.Error()) } +func TestAppErrorNoTranslation(t *testing.T) { + appErr := NewAppError("TestAppError", NoTranslation, nil, "test error", http.StatusBadRequest) + require.Equal(t, "TestAppError: test error", appErr.Error()) +} + func TestAppErrorJunk(t *testing.T) { rerr := AppErrorFromJSON(strings.NewReader("This is a broken test")) require.Equal(t, "body: This is a broken test", rerr.DetailedError) From e4b050693db226304939fe2b171d33f19487766e Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Tue, 3 Jan 2023 21:30:51 +0530 Subject: [PATCH 8/8] Return 404 when thread membership not found (#21960) We were incorrectly returning by checking for `err != nil` when a 404 error was included in that condition. ```release-note NONE ``` --- app/post_test.go | 5 +++++ app/user.go | 31 +++++++++++++++++++------------ app/user_test.go | 5 +++++ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/post_test.go b/app/post_test.go index d3073fe302..4630eb75af 100644 --- a/app/post_test.go +++ b/app/post_test.go @@ -2408,6 +2408,11 @@ func TestFollowThreadSkipsParticipants(t *testing.T) { for _, p := range thread.Participants { require.True(t, p.Id == sysadmin.Id || p.Id == user.Id) } + + threadMembership.PostId = "notfound" + _, err = th.App.GetThreadForUser(threadMembership, false) + require.NotNil(t, err) + assert.Equal(t, http.StatusNotFound, err.StatusCode) } func TestAutofollowBasedOnRootPost(t *testing.T) { diff --git a/app/user.go b/app/user.go index d259439859..991457f7ba 100644 --- a/app/user.go +++ b/app/user.go @@ -2484,24 +2484,31 @@ func (a *App) GetThreadsForUser(userID, teamID string, options model.GetUserThre } func (a *App) GetThreadMembershipForUser(userId, threadId string) (*model.ThreadMembership, *model.AppError) { - threadMembership, err := a.Srv().Store().Thread().GetMembershipForUser(userId, threadId) - if err != nil { - return nil, model.NewAppError("GetThreadMembershipForUser", "app.user.get_thread_membership_for_user.app_error", nil, "", http.StatusInternalServerError).Wrap(err) - } - if threadMembership == nil { - return nil, model.NewAppError("GetThreadMembershipForUser", "app.user.get_thread_membership_for_user.not_found", nil, "thread membership not found/followed", http.StatusNotFound) + threadMembership, nErr := a.Srv().Store().Thread().GetMembershipForUser(userId, threadId) + if nErr != nil { + var nfErr *store.ErrNotFound + switch { + case errors.As(nErr, &nfErr): + return nil, model.NewAppError("GetThreadMembershipForUser", "app.user.get_thread_membership_for_user.not_found", nil, "", http.StatusNotFound).Wrap(nErr) + default: + return nil, model.NewAppError("GetThreadMembershipForUser", "app.user.get_thread_membership_for_user.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + } } return threadMembership, nil } func (a *App) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, *model.AppError) { - thread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, extended, a.isPostPriorityEnabled()) - if err != nil { - return nil, model.NewAppError("GetThreadForUser", "app.user.get_threads_for_user.app_error", nil, "", http.StatusInternalServerError).Wrap(err) - } - if thread == nil { - return nil, model.NewAppError("GetThreadForUser", "app.user.get_threads_for_user.not_found", nil, "thread not found/followed", http.StatusNotFound) + thread, nErr := a.Srv().Store().Thread().GetThreadForUser(threadMembership, extended, a.isPostPriorityEnabled()) + if nErr != nil { + var nfErr *store.ErrNotFound + switch { + case errors.As(nErr, &nfErr): + return nil, model.NewAppError("GetThreadForUser", "app.user.get_threads_for_user.not_found", nil, "thread not found/followed", http.StatusNotFound) + default: + return nil, model.NewAppError("GetThreadForUser", "app.user.get_threads_for_user.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr) + } } + a.sanitizeProfiles(thread.Participants, false) thread.Post.SanitizeProps() return thread, nil diff --git a/app/user_test.go b/app/user_test.go index 8a71680a20..086e71e25f 100644 --- a/app/user_test.go +++ b/app/user_test.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "errors" + "net/http" "path/filepath" "strings" "testing" @@ -1700,6 +1701,10 @@ func TestUpdateThreadReadForUser(t *testing.T) { require.Nil(t, appErr) require.NotNil(t, threadMembership) assert.True(t, threadMembership.Following) + + _, appErr = th.App.GetThreadMembershipForUser(th.BasicUser.Id, "notfound") + require.NotNil(t, appErr) + assert.Equal(t, http.StatusNotFound, appErr.StatusCode) }) t.Run("Ensure no panic on error", func(t *testing.T) {