diff options
Diffstat (limited to 'store/storetest')
-rw-r--r-- | store/storetest/audit_store.go | 91 | ||||
-rw-r--r-- | store/storetest/channel_store.go | 1967 | ||||
-rw-r--r-- | store/storetest/cluster_discovery_store.go | 200 | ||||
-rw-r--r-- | store/storetest/command_store.go | 251 | ||||
-rw-r--r-- | store/storetest/command_webhook_store.go | 68 | ||||
-rw-r--r-- | store/storetest/compliance_store.go | 318 | ||||
-rw-r--r-- | store/storetest/emoji_store.go | 175 | ||||
-rw-r--r-- | store/storetest/file_info_store.go | 296 | ||||
-rw-r--r-- | store/storetest/job_store.go | 507 | ||||
-rw-r--r-- | store/storetest/license_store.go | 56 | ||||
-rw-r--r-- | store/storetest/oauth_store.go | 435 | ||||
-rw-r--r-- | store/storetest/post_store.go | 1684 | ||||
-rw-r--r-- | store/storetest/preference_store.go | 447 | ||||
-rw-r--r-- | store/storetest/reaction_store.go | 350 | ||||
-rw-r--r-- | store/storetest/session_store.go | 250 | ||||
-rw-r--r-- | store/storetest/status_store.go | 106 | ||||
-rw-r--r-- | store/storetest/system_store.go | 58 | ||||
-rw-r--r-- | store/storetest/team_store.go | 1010 | ||||
-rw-r--r-- | store/storetest/user_access_token_store.go | 89 | ||||
-rw-r--r-- | store/storetest/user_store.go | 2074 | ||||
-rw-r--r-- | store/storetest/webhook_store.go | 507 |
21 files changed, 10939 insertions, 0 deletions
diff --git a/store/storetest/audit_store.go b/store/storetest/audit_store.go new file mode 100644 index 000000000..2e107dba8 --- /dev/null +++ b/store/storetest/audit_store.go @@ -0,0 +1,91 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestAuditStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testAuditStore(t, ss) }) + t.Run("PermanentDeleteBatch", func(t *testing.T) { testAuditStorePermanentDeleteBatch(t, ss) }) +} + +func testAuditStore(t *testing.T, ss store.Store) { + audit := &model.Audit{UserId: model.NewId(), IpAddress: "ipaddress", Action: "Action"} + store.Must(ss.Audit().Save(audit)) + time.Sleep(100 * time.Millisecond) + store.Must(ss.Audit().Save(audit)) + time.Sleep(100 * time.Millisecond) + store.Must(ss.Audit().Save(audit)) + time.Sleep(100 * time.Millisecond) + audit.ExtraInfo = "extra" + time.Sleep(100 * time.Millisecond) + store.Must(ss.Audit().Save(audit)) + + time.Sleep(100 * time.Millisecond) + + c := ss.Audit().Get(audit.UserId, 0, 100) + result := <-c + audits := result.Data.(model.Audits) + + if len(audits) != 4 { + t.Fatal("Failed to save and retrieve 4 audit logs") + } + + if audits[0].ExtraInfo != "extra" { + t.Fatal("Failed to save property for extra info") + } + + c = ss.Audit().Get("missing", 0, 100) + result = <-c + audits = result.Data.(model.Audits) + + if len(audits) != 0 { + t.Fatal("Should have returned empty because user_id is missing") + } + + c = ss.Audit().Get("", 0, 100) + result = <-c + audits = result.Data.(model.Audits) + + if len(audits) < 4 { + t.Fatal("Failed to save and retrieve 4 audit logs") + } + + if r2 := <-ss.Audit().PermanentDeleteByUser(audit.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } +} + +func testAuditStorePermanentDeleteBatch(t *testing.T, ss store.Store) { + a1 := &model.Audit{UserId: model.NewId(), IpAddress: "ipaddress", Action: "Action"} + store.Must(ss.Audit().Save(a1)) + time.Sleep(10 * time.Millisecond) + a2 := &model.Audit{UserId: a1.UserId, IpAddress: "ipaddress", Action: "Action"} + store.Must(ss.Audit().Save(a2)) + time.Sleep(10 * time.Millisecond) + cutoff := model.GetMillis() + time.Sleep(10 * time.Millisecond) + a3 := &model.Audit{UserId: a1.UserId, IpAddress: "ipaddress", Action: "Action"} + store.Must(ss.Audit().Save(a3)) + + if r := <-ss.Audit().Get(a1.UserId, 0, 100); len(r.Data.(model.Audits)) != 3 { + t.Fatal("Expected 3 audits. Got ", len(r.Data.(model.Audits))) + } + + store.Must(ss.Audit().PermanentDeleteBatch(cutoff, 1000000)) + + if r := <-ss.Audit().Get(a1.UserId, 0, 100); len(r.Data.(model.Audits)) != 1 { + t.Fatal("Expected 1 audit. Got ", len(r.Data.(model.Audits))) + } + + if r2 := <-ss.Audit().PermanentDeleteByUser(a1.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } +} diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go new file mode 100644 index 000000000..53d81bdc0 --- /dev/null +++ b/store/storetest/channel_store.go @@ -0,0 +1,1967 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestChannelStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) }) + t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) }) + t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) }) + t.Run("Update", func(t *testing.T) { testChannelStoreUpdate(t, ss) }) + t.Run("GetChannelUnread", func(t *testing.T) { testGetChannelUnread(t, ss) }) + t.Run("Get", func(t *testing.T) { testChannelStoreGet(t, ss) }) + t.Run("GetForPost", func(t *testing.T) { testChannelStoreGetForPost(t, ss) }) + t.Run("Restore", func(t *testing.T) { testChannelStoreRestore(t, ss) }) + t.Run("Delete", func(t *testing.T) { testChannelStoreDelete(t, ss) }) + t.Run("GetByName", func(t *testing.T) { testChannelStoreGetByName(t, ss) }) + t.Run("GetDeletedByName", func(t *testing.T) { testChannelStoreGetDeletedByName(t, ss) }) + t.Run("GetDeleted", func(t *testing.T) { testChannelStoreGetDeleted(t, ss) }) + t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) }) + t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) }) + t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) }) + t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) }) + t.Run("GetPublicChannelsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsForTeam(t, ss) }) + t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) }) + t.Run("GetChannelCounts", func(t *testing.T) { testChannelStoreGetChannelCounts(t, ss) }) + t.Run("GetMembersForUser", func(t *testing.T) { testChannelStoreGetMembersForUser(t, ss) }) + t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) }) + t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) }) + t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) }) + t.Run("GetMember", func(t *testing.T) { testGetMember(t, ss) }) + t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, ss) }) + t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) }) + t.Run("UpdateExtrasByUser", func(t *testing.T) { testUpdateExtrasByUser(t, ss) }) + t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) }) + t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) }) + t.Run("GetMembersByIds", func(t *testing.T) { testChannelStoreGetMembersByIds(t, ss) }) + t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) }) + t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) }) +} + +func testChannelStoreSave(t *testing.T, ss store.Store) { + teamId := model.NewId() + + o1 := model.Channel{} + o1.TeamId = teamId + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + + if err := (<-ss.Channel().Save(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Channel().Save(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } + + o1.Id = "" + if err := (<-ss.Channel().Save(&o1)).Err; err == nil { + t.Fatal("should be unique name") + } + + o1.Id = "" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_DIRECT + if err := (<-ss.Channel().Save(&o1)).Err; err == nil { + t.Fatal("Should not be able to save direct channel") + } +} + +func testChannelStoreSaveDirectChannel(t *testing.T, ss store.Store) { + teamId := model.NewId() + + o1 := model.Channel{} + o1.TeamId = teamId + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_DIRECT + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + if err := (<-ss.Channel().SaveDirectChannel(&o1, &m1, &m2)).Err; err != nil { + t.Fatal("couldn't save direct channel", err) + } + + members := (<-ss.Channel().GetMembers(o1.Id, 0, 100)).Data.(*model.ChannelMembers) + if len(*members) != 2 { + t.Fatal("should have saved 2 members") + } + + if err := (<-ss.Channel().SaveDirectChannel(&o1, &m1, &m2)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } + + // Attempt to save a direct channel that already exists + o1a := model.Channel{ + TeamId: o1.TeamId, + DisplayName: o1.DisplayName, + Name: o1.Name, + Type: o1.Type, + } + + if result := <-ss.Channel().SaveDirectChannel(&o1a, &m1, &m2); result.Err == nil { + t.Fatal("should've failed to save a duplicate direct channel") + } else if result.Err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("should've returned CHANNEL_EXISTS_ERROR") + } else if returned := result.Data.(*model.Channel); returned.Id != o1.Id { + t.Fatal("should've returned original channel when saving a duplicate direct channel") + } + + // Attempt to save a non-direct channel + o1.Id = "" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + if err := (<-ss.Channel().SaveDirectChannel(&o1, &m1, &m2)).Err; err == nil { + t.Fatal("Should not be able to save non-direct channel") + } +} + +func testChannelStoreCreateDirectChannel(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + res := <-ss.Channel().CreateDirectChannel(u1.Id, u2.Id) + if res.Err != nil { + t.Fatal("couldn't create direct channel", res.Err) + } + + c1 := res.Data.(*model.Channel) + + members := (<-ss.Channel().GetMembers(c1.Id, 0, 100)).Data.(*model.ChannelMembers) + if len(*members) != 2 { + t.Fatal("should have saved 2 members") + } +} + +func testChannelStoreUpdate(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = o1.TeamId + o2.DisplayName = "Name" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + time.Sleep(100 * time.Millisecond) + + if err := (<-ss.Channel().Update(&o1)).Err; err != nil { + t.Fatal(err) + } + + o1.Id = "missing" + if err := (<-ss.Channel().Update(&o1)).Err; err == nil { + t.Fatal("Update should have failed because of missing key") + } + + o1.Id = model.NewId() + if err := (<-ss.Channel().Update(&o1)).Err; err == nil { + t.Fatal("Update should have faile because id change") + } + + o2.Name = o1.Name + if err := (<-ss.Channel().Update(&o2)).Err; err == nil { + t.Fatal("Update should have failed because of existing name") + } +} + +func testGetChannelUnread(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + teamId2 := model.NewId() + + uid := model.NewId() + m1 := &model.TeamMember{TeamId: teamId1, UserId: uid} + m2 := &model.TeamMember{TeamId: teamId2, UserId: uid} + store.Must(ss.Team().SaveMember(m1)) + store.Must(ss.Team().SaveMember(m2)) + notifyPropsModel := model.GetDefaultChannelNotifyProps() + + // Setup Channel 1 + c1 := &model.Channel{TeamId: m1.TeamId, Name: model.NewId(), DisplayName: "Downtown", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c1)) + cm1 := &model.ChannelMember{ChannelId: c1.Id, UserId: m1.UserId, NotifyProps: notifyPropsModel, MsgCount: 90} + store.Must(ss.Channel().SaveMember(cm1)) + + // Setup Channel 2 + c2 := &model.Channel{TeamId: m2.TeamId, Name: model.NewId(), DisplayName: "Cultural", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c2)) + cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m2.UserId, NotifyProps: notifyPropsModel, MsgCount: 90, MentionCount: 5} + store.Must(ss.Channel().SaveMember(cm2)) + + // Check for Channel 1 + if resp := <-ss.Channel().GetChannelUnread(c1.Id, uid); resp.Err != nil { + t.Fatal(resp.Err) + } else { + ch := resp.Data.(*model.ChannelUnread) + if c1.Id != ch.ChannelId { + t.Fatal("wrong channel id") + } + + if teamId1 != ch.TeamId { + t.Fatal("wrong team id for channel 1") + } + + if ch.NotifyProps == nil { + t.Fatal("wrong props for channel 1") + } + + if ch.MentionCount != 0 { + t.Fatal("wrong MentionCount for channel 1") + } + + if ch.MsgCount != 10 { + t.Fatal("wrong MsgCount for channel 1") + } + } + + // Check for Channel 2 + if resp2 := <-ss.Channel().GetChannelUnread(c2.Id, uid); resp2.Err != nil { + t.Fatal(resp2.Err) + } else { + ch2 := resp2.Data.(*model.ChannelUnread) + if c2.Id != ch2.ChannelId { + t.Fatal("wrong channel id") + } + + if teamId2 != ch2.TeamId { + t.Fatal("wrong team id") + } + + if ch2.MentionCount != 5 { + t.Fatal("wrong MentionCount for channel 2") + } + + if ch2.MsgCount != 10 { + t.Fatal("wrong MsgCount for channel 2") + } + } +} + +func testChannelStoreGet(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + if r1 := <-ss.Channel().Get(o1.Id, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Channel).ToJson() != o1.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if err := (<-ss.Channel().Get("", false)).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(&u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Direct Name" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_DIRECT + + m1 := model.ChannelMember{} + m1.ChannelId = o2.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = o2.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + store.Must(ss.Channel().SaveDirectChannel(&o2, &m1, &m2)) + + if r2 := <-ss.Channel().Get(o2.Id, false); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if r2.Data.(*model.Channel).ToJson() != o2.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if r4 := <-ss.Channel().Get(o2.Id, true); r4.Err != nil { + t.Fatal(r4.Err) + } else { + if r4.Data.(*model.Channel).ToJson() != o2.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if r3 := <-ss.Channel().GetAll(o1.TeamId); r3.Err != nil { + t.Fatal(r3.Err) + } else { + channels := r3.Data.([]*model.Channel) + if len(channels) == 0 { + t.Fatal("too little") + } + } + + if r3 := <-ss.Channel().GetTeamChannels(o1.TeamId); r3.Err != nil { + t.Fatal(r3.Err) + } else { + channels := r3.Data.(*model.ChannelList) + if len(*channels) == 0 { + t.Fatal("too little") + } + } +} + +func testChannelStoreGetForPost(t *testing.T, ss store.Store) { + o1 := store.Must(ss.Channel().Save(&model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + })).(*model.Channel) + + p1 := store.Must(ss.Post().Save(&model.Post{ + UserId: model.NewId(), + ChannelId: o1.Id, + Message: "test", + })).(*model.Post) + + if r1 := <-ss.Channel().GetForPost(p1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else if r1.Data.(*model.Channel).Id != o1.Id { + t.Fatal("incorrect channel returned") + } +} + +func testChannelStoreRestore(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + if r := <-ss.Channel().Delete(o1.Id, model.GetMillis()); r.Err != nil { + t.Fatal(r.Err) + } + + if r := <-ss.Channel().Get(o1.Id, false); r.Data.(*model.Channel).DeleteAt == 0 { + t.Fatal("should have been deleted") + } + + if r := <-ss.Channel().Restore(o1.Id, model.GetMillis()); r.Err != nil { + t.Fatal(r.Err) + } + + if r := <-ss.Channel().Get(o1.Id, false); r.Data.(*model.Channel).DeleteAt != 0 { + t.Fatal("should have been restored") + } + +} + +func testChannelStoreDelete(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = o1.TeamId + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "Channel3" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o3)) + + o4 := model.Channel{} + o4.TeamId = o1.TeamId + o4.DisplayName = "Channel4" + o4.Name = "zz" + model.NewId() + "b" + o4.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o4)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o2.Id + m2.UserId = m1.UserId + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + if r := <-ss.Channel().Delete(o1.Id, model.GetMillis()); r.Err != nil { + t.Fatal(r.Err) + } + + if r := <-ss.Channel().Get(o1.Id, false); r.Data.(*model.Channel).DeleteAt == 0 { + t.Fatal("should have been deleted") + } + + if r := <-ss.Channel().Delete(o3.Id, model.GetMillis()); r.Err != nil { + t.Fatal(r.Err) + } + + cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId) + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("invalid number of channels") + } + + cresult = <-ss.Channel().GetMoreChannels(o1.TeamId, m1.UserId, 0, 100) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("invalid number of channels") + } + + <-ss.Channel().PermanentDelete(o2.Id) + + cresult = <-ss.Channel().GetChannels(o1.TeamId, m1.UserId) + t.Log(cresult.Err) + if cresult.Err.Id != "store.sql_channel.get_channels.not_found.app_error" { + t.Fatal("no channels should be found") + } + + if r := <-ss.Channel().PermanentDeleteByTeam(o1.TeamId); r.Err != nil { + t.Fatal(r.Err) + } +} + +func testChannelStoreGetByName(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + r1 := <-ss.Channel().GetByName(o1.TeamId, o1.Name, true) + if r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Channel).ToJson() != o1.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if err := (<-ss.Channel().GetByName(o1.TeamId, "", true)).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + if r1 := <-ss.Channel().GetByName(o1.TeamId, o1.Name, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Channel).ToJson() != o1.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if err := (<-ss.Channel().GetByName(o1.TeamId, "", false)).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + store.Must(ss.Channel().Delete(r1.Data.(*model.Channel).Id, model.GetMillis())) + + if err := (<-ss.Channel().GetByName(o1.TeamId, "", false)).Err; err == nil { + t.Fatal("Deleted channel should not be returned by GetByName()") + } +} + +func testChannelStoreGetDeletedByName(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Name" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + o1.DeleteAt = model.GetMillis() + store.Must(ss.Channel().Save(&o1)) + + if r1 := <-ss.Channel().GetDeletedByName(o1.TeamId, o1.Name); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Channel).ToJson() != o1.ToJson() { + t.Fatal("invalid returned channel") + } + } + + if err := (<-ss.Channel().GetDeletedByName(o1.TeamId, "")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testChannelStoreGetDeleted(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + o1.DeleteAt = model.GetMillis() + store.Must(ss.Channel().Save(&o1)) + + cresult := <-ss.Channel().GetDeleted(o1.TeamId, 0, 100) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list") + } + + if (*list)[0].Name != o1.Name { + t.Fatal("missing channel") + } + + o2 := model.Channel{} + o2.TeamId = o1.TeamId + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + cresult = <-ss.Channel().GetDeleted(o1.TeamId, 0, 100) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list") + } + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "Channel3" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_OPEN + o3.DeleteAt = model.GetMillis() + store.Must(ss.Channel().Save(&o3)) + + cresult = <-ss.Channel().GetDeleted(o1.TeamId, 0, 100) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 2 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetDeleted(o1.TeamId, 0, 1) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetDeleted(o1.TeamId, 1, 1) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + +} + +func testChannelMemberStore(t *testing.T, ss store.Store) { + c1 := model.Channel{} + c1.TeamId = model.NewId() + c1.DisplayName = "NameName" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = *store.Must(ss.Channel().Save(&c1)).(*model.Channel) + + c1t1 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t1 := c1t1.ExtraUpdateAt + + u1 := model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(&u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(&u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + o1 := model.ChannelMember{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&o1)) + + o2 := model.ChannelMember{} + o2.ChannelId = c1.Id + o2.UserId = u2.Id + o2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&o2)) + + c1t2 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t2 := c1t2.ExtraUpdateAt + + if t2 <= t1 { + t.Fatal("Member update time incorrect") + } + + count := (<-ss.Channel().GetMemberCount(o1.ChannelId, true)).Data.(int64) + if count != 2 { + t.Fatal("should have saved 2 members") + } + + count = (<-ss.Channel().GetMemberCount(o1.ChannelId, true)).Data.(int64) + if count != 2 { + t.Fatal("should have saved 2 members") + } + + if ss.Channel().GetMemberCountFromCache(o1.ChannelId) != 2 { + t.Fatal("should have saved 2 members") + } + + if ss.Channel().GetMemberCountFromCache("junk") != 0 { + t.Fatal("should have saved 0 members") + } + + count = (<-ss.Channel().GetMemberCount(o1.ChannelId, false)).Data.(int64) + if count != 2 { + t.Fatal("should have saved 2 members") + } + + store.Must(ss.Channel().RemoveMember(o2.ChannelId, o2.UserId)) + + count = (<-ss.Channel().GetMemberCount(o1.ChannelId, false)).Data.(int64) + if count != 1 { + t.Fatal("should have removed 1 member") + } + + c1t3 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t3 := c1t3.ExtraUpdateAt + + if t3 <= t2 || t3 <= t1 { + t.Fatal("Member update time incorrect on delete") + } + + member := (<-ss.Channel().GetMember(o1.ChannelId, o1.UserId)).Data.(*model.ChannelMember) + if member.ChannelId != o1.ChannelId { + t.Fatal("should have go member") + } + + if err := (<-ss.Channel().SaveMember(&o1)).Err; err == nil { + t.Fatal("Should have been a duplicate") + } + + c1t4 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t4 := c1t4.ExtraUpdateAt + if t4 != t3 { + t.Fatal("Should not update time upon failure") + } +} + +func testChannelDeleteMemberStore(t *testing.T, ss store.Store) { + c1 := model.Channel{} + c1.TeamId = model.NewId() + c1.DisplayName = "NameName" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = *store.Must(ss.Channel().Save(&c1)).(*model.Channel) + + c1t1 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t1 := c1t1.ExtraUpdateAt + + u1 := model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(&u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(&u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + o1 := model.ChannelMember{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&o1)) + + o2 := model.ChannelMember{} + o2.ChannelId = c1.Id + o2.UserId = u2.Id + o2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&o2)) + + c1t2 := (<-ss.Channel().Get(c1.Id, false)).Data.(*model.Channel) + t2 := c1t2.ExtraUpdateAt + + if t2 <= t1 { + t.Fatal("Member update time incorrect") + } + + count := (<-ss.Channel().GetMemberCount(o1.ChannelId, false)).Data.(int64) + if count != 2 { + t.Fatal("should have saved 2 members") + } + + store.Must(ss.Channel().PermanentDeleteMembersByUser(o2.UserId)) + + count = (<-ss.Channel().GetMemberCount(o1.ChannelId, false)).Data.(int64) + if count != 1 { + t.Fatal("should have removed 1 member") + } + + if r1 := <-ss.Channel().PermanentDeleteMembersByChannel(o1.ChannelId); r1.Err != nil { + t.Fatal(r1.Err) + } + + count = (<-ss.Channel().GetMemberCount(o1.ChannelId, false)).Data.(int64) + if count != 0 { + t.Fatal("should have removed all members") + } +} + +func testChannelStoreGetChannels(t *testing.T, ss store.Store) { + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + m3 := model.ChannelMember{} + m3.ChannelId = o2.Id + m3.UserId = model.NewId() + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m3)) + + cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId) + list := cresult.Data.(*model.ChannelList) + + if (*list)[0].Id != o1.Id { + t.Fatal("missing channel") + } + + acresult := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, false) + ids := acresult.Data.(map[string]string) + if _, ok := ids[o1.Id]; !ok { + t.Fatal("missing channel") + } + + acresult2 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true) + ids2 := acresult2.Data.(map[string]string) + if _, ok := ids2[o1.Id]; !ok { + t.Fatal("missing channel") + } + + acresult3 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true) + ids3 := acresult3.Data.(map[string]string) + if _, ok := ids3[o1.Id]; !ok { + t.Fatal("missing channel") + } + + if !ss.Channel().IsUserInChannelUseCache(m1.UserId, o1.Id) { + t.Fatal("missing channel") + } + + if ss.Channel().IsUserInChannelUseCache(m1.UserId, o2.Id) { + t.Fatal("missing channel") + } + + if ss.Channel().IsUserInChannelUseCache(m1.UserId, "blahblah") { + t.Fatal("missing channel") + } + + if ss.Channel().IsUserInChannelUseCache("blahblah", "blahblah") { + t.Fatal("missing channel") + } + + ss.Channel().InvalidateAllChannelMembersForUser(m1.UserId) +} + +func testChannelStoreGetMoreChannels(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + m3 := model.ChannelMember{} + m3.ChannelId = o2.Id + m3.UserId = model.NewId() + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m3)) + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "ChannelA" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o3)) + + o4 := model.Channel{} + o4.TeamId = o1.TeamId + o4.DisplayName = "ChannelB" + o4.Name = "zz" + model.NewId() + "b" + o4.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o4)) + + o5 := model.Channel{} + o5.TeamId = o1.TeamId + o5.DisplayName = "ChannelC" + o5.Name = "zz" + model.NewId() + "b" + o5.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o5)) + + cresult := <-ss.Channel().GetMoreChannels(o1.TeamId, m1.UserId, 0, 100) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list") + } + + if (*list)[0].Name != o3.Name { + t.Fatal("missing channel") + } + + o6 := model.Channel{} + o6.TeamId = o1.TeamId + o6.DisplayName = "ChannelA" + o6.Name = "zz" + model.NewId() + "b" + o6.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o6)) + + cresult = <-ss.Channel().GetMoreChannels(o1.TeamId, m1.UserId, 0, 100) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 2 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetMoreChannels(o1.TeamId, m1.UserId, 0, 1) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetMoreChannels(o1.TeamId, m1.UserId, 1, 1) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + + if r1 := <-ss.Channel().AnalyticsTypeCount(o1.TeamId, model.CHANNEL_OPEN); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) != 3 { + t.Log(r1.Data) + t.Fatal("wrong value") + } + } + + if r1 := <-ss.Channel().AnalyticsTypeCount(o1.TeamId, model.CHANNEL_PRIVATE); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) != 2 { + t.Log(r1.Data) + t.Fatal("wrong value") + } + } +} + +func testChannelStoreGetPublicChannelsForTeam(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "OpenChannel1Team1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "OpenChannel1Team2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "PrivateChannel1Team1" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o3)) + + cresult := <-ss.Channel().GetPublicChannelsForTeam(o1.TeamId, 0, 100) + if cresult.Err != nil { + t.Fatal(cresult.Err) + } + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list") + } + + if (*list)[0].Name != o1.Name { + t.Fatal("missing channel") + } + + o4 := model.Channel{} + o4.TeamId = o1.TeamId + o4.DisplayName = "OpenChannel2Team1" + o4.Name = "zz" + model.NewId() + "b" + o4.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o4)) + + cresult = <-ss.Channel().GetPublicChannelsForTeam(o1.TeamId, 0, 100) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 2 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetPublicChannelsForTeam(o1.TeamId, 0, 1) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + + cresult = <-ss.Channel().GetPublicChannelsForTeam(o1.TeamId, 1, 1) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("wrong list length") + } + + if r1 := <-ss.Channel().AnalyticsTypeCount(o1.TeamId, model.CHANNEL_OPEN); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) != 2 { + t.Log(r1.Data) + t.Fatal("wrong value") + } + } + + if r1 := <-ss.Channel().AnalyticsTypeCount(o1.TeamId, model.CHANNEL_PRIVATE); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) != 1 { + t.Log(r1.Data) + t.Fatal("wrong value") + } + } +} + +func testChannelStoreGetPublicChannelsByIdsForTeam(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + + oc1 := model.Channel{} + oc1.TeamId = teamId1 + oc1.DisplayName = "OpenChannel1Team1" + oc1.Name = "zz" + model.NewId() + "b" + oc1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&oc1)) + + oc2 := model.Channel{} + oc2.TeamId = model.NewId() + oc2.DisplayName = "OpenChannel2TeamOther" + oc2.Name = "zz" + model.NewId() + "b" + oc2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&oc2)) + + pc3 := model.Channel{} + pc3.TeamId = teamId1 + pc3.DisplayName = "PrivateChannel3Team1" + pc3.Name = "zz" + model.NewId() + "b" + pc3.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&pc3)) + + cids := []string{oc1.Id} + cresult := <-ss.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list := cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("should return 1 channel") + } + + if (*list)[0].Id != oc1.Id { + t.Fatal("missing channel") + } + + cids = append(cids, oc2.Id) + cids = append(cids, model.NewId()) + cids = append(cids, pc3.Id) + cresult = <-ss.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 1 { + t.Fatal("should return 1 channel") + } + + oc4 := model.Channel{} + oc4.TeamId = teamId1 + oc4.DisplayName = "OpenChannel4Team1" + oc4.Name = "zz" + model.NewId() + "b" + oc4.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&oc4)) + + cids = append(cids, oc4.Id) + cresult = <-ss.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 2 { + t.Fatal("should return 2 channels") + } + + if (*list)[0].Id != oc1.Id { + t.Fatal("missing channel") + } + + if (*list)[1].Id != oc4.Id { + t.Fatal("missing channel") + } + + cids = cids[:0] + cids = append(cids, model.NewId()) + cresult = <-ss.Channel().GetPublicChannelsByIdsForTeam(teamId1, cids) + list = cresult.Data.(*model.ChannelList) + + if len(*list) != 0 { + t.Fatal("should not return a channel") + } +} + +func testChannelStoreGetChannelCounts(t *testing.T, ss store.Store) { + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + m3 := model.ChannelMember{} + m3.ChannelId = o2.Id + m3.UserId = model.NewId() + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m3)) + + cresult := <-ss.Channel().GetChannelCounts(o1.TeamId, m1.UserId) + counts := cresult.Data.(*model.ChannelCounts) + + if len(counts.Counts) != 1 { + t.Fatal("wrong number of counts") + } + + if len(counts.UpdateTimes) != 1 { + t.Fatal("wrong number of update times") + } +} + +func testChannelStoreGetMembersForUser(t *testing.T, ss store.Store) { + t1 := model.Team{} + t1.DisplayName = "Name" + t1.Name = model.NewId() + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + store.Must(ss.Team().Save(&t1)) + + o1 := model.Channel{} + o1.TeamId = t1.Id + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = o1.TeamId + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o2.Id + m2.UserId = m1.UserId + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + cresult := <-ss.Channel().GetMembersForUser(o1.TeamId, m1.UserId) + members := cresult.Data.(*model.ChannelMembers) + + // no unread messages + if len(*members) != 2 { + t.Fatal("wrong number of members") + } +} + +func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + o1.TotalMsgCount = 25 + o1.LastPostAt = 12345 + store.Must(ss.Channel().Save(&o1)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel1" + o2.Name = "zz" + model.NewId() + "c" + o2.Type = model.CHANNEL_OPEN + o2.TotalMsgCount = 26 + o2.LastPostAt = 123456 + store.Must(ss.Channel().Save(&o2)) + + m2 := model.ChannelMember{} + m2.ChannelId = o2.Id + m2.UserId = m1.UserId + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, m1.UserId); result.Err != nil { + t.Fatal("failed to update", result.Err) + } else if result.Data.(map[string]int64)[o1.Id] != o1.LastPostAt { + t.Fatal("last viewed at time incorrect") + } + + if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId, m2.ChannelId}, m1.UserId); result.Err != nil { + t.Fatal("failed to update", result.Err) + } else if result.Data.(map[string]int64)[o2.Id] != o2.LastPostAt { + t.Fatal("last viewed at time incorrect") + } + + rm1 := store.Must(ss.Channel().GetMember(m1.ChannelId, m1.UserId)).(*model.ChannelMember) + assert.Equal(t, rm1.LastViewedAt, o1.LastPostAt) + assert.Equal(t, rm1.LastUpdateAt, o1.LastPostAt) + assert.Equal(t, rm1.MsgCount, o1.TotalMsgCount) + + rm2 := store.Must(ss.Channel().GetMember(m2.ChannelId, m2.UserId)).(*model.ChannelMember) + assert.Equal(t, rm2.LastViewedAt, o2.LastPostAt) + assert.Equal(t, rm2.LastUpdateAt, o2.LastPostAt) + assert.Equal(t, rm2.MsgCount, o2.TotalMsgCount) + + if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, "missing id"); result.Err != nil { + t.Fatal("failed to update") + } +} + +func testChannelStoreIncrementMentionCount(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "Channel1" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + o1.TotalMsgCount = 25 + store.Must(ss.Channel().Save(&o1)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + err := (<-ss.Channel().IncrementMentionCount(m1.ChannelId, m1.UserId)).Err + if err != nil { + t.Fatal("failed to update") + } + + err = (<-ss.Channel().IncrementMentionCount(m1.ChannelId, "missing id")).Err + if err != nil { + t.Fatal("failed to update") + } + + err = (<-ss.Channel().IncrementMentionCount("missing id", m1.UserId)).Err + if err != nil { + t.Fatal("failed to update") + } + + err = (<-ss.Channel().IncrementMentionCount("missing id", "missing id")).Err + if err != nil { + t.Fatal("failed to update") + } +} + +func testUpdateChannelMember(t *testing.T, ss store.Store) { + userId := model.NewId() + + c1 := &model.Channel{ + TeamId: model.NewId(), + DisplayName: model.NewId(), + Name: model.NewId(), + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(c1)) + + m1 := &model.ChannelMember{ + ChannelId: c1.Id, + UserId: userId, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(m1)) + + m1.NotifyProps["test"] = "sometext" + if result := <-ss.Channel().UpdateMember(m1); result.Err != nil { + t.Fatal(result.Err) + } + + m1.UserId = "" + if result := <-ss.Channel().UpdateMember(m1); result.Err == nil { + t.Fatal("bad user id - should fail") + } +} + +func testGetMember(t *testing.T, ss store.Store) { + userId := model.NewId() + + c1 := &model.Channel{ + TeamId: model.NewId(), + DisplayName: model.NewId(), + Name: model.NewId(), + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(c1)) + + c2 := &model.Channel{ + TeamId: c1.TeamId, + DisplayName: model.NewId(), + Name: model.NewId(), + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(c2)) + + m1 := &model.ChannelMember{ + ChannelId: c1.Id, + UserId: userId, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(m1)) + + m2 := &model.ChannelMember{ + ChannelId: c2.Id, + UserId: userId, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(m2)) + + if result := <-ss.Channel().GetMember(model.NewId(), userId); result.Err == nil { + t.Fatal("should've failed to get member for non-existant channel") + } + + if result := <-ss.Channel().GetMember(c1.Id, model.NewId()); result.Err == nil { + t.Fatal("should've failed to get member for non-existant user") + } + + if result := <-ss.Channel().GetMember(c1.Id, userId); result.Err != nil { + t.Fatal("shouldn't have errored when getting member", result.Err) + } else if member := result.Data.(*model.ChannelMember); member.ChannelId != c1.Id { + t.Fatal("should've gotten member of channel 1") + } else if member.UserId != userId { + t.Fatal("should've gotten member for user") + } + + if result := <-ss.Channel().GetMember(c2.Id, userId); result.Err != nil { + t.Fatal("shouldn't have errored when getting member", result.Err) + } else if member := result.Data.(*model.ChannelMember); member.ChannelId != c2.Id { + t.Fatal("should've gotten member of channel 2") + } else if member.UserId != userId { + t.Fatal("should've gotten member for user") + } + + if result := <-ss.Channel().GetAllChannelMembersNotifyPropsForChannel(c2.Id, false); result.Err != nil { + t.Fatal(result.Err) + } else { + props := result.Data.(map[string]model.StringMap) + if len(props) == 0 { + t.Fatal("should not be empty") + } + } + + if result := <-ss.Channel().GetAllChannelMembersNotifyPropsForChannel(c2.Id, true); result.Err != nil { + t.Fatal(result.Err) + } else { + props := result.Data.(map[string]model.StringMap) + if len(props) == 0 { + t.Fatal("should not be empty") + } + } + + ss.Channel().InvalidateCacheForChannelMembersNotifyProps(c2.Id) +} + +func testChannelStoreGetMemberForPost(t *testing.T, ss store.Store) { + o1 := store.Must(ss.Channel().Save(&model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + })).(*model.Channel) + + m1 := store.Must(ss.Channel().SaveMember(&model.ChannelMember{ + ChannelId: o1.Id, + UserId: model.NewId(), + NotifyProps: model.GetDefaultChannelNotifyProps(), + })).(*model.ChannelMember) + + p1 := store.Must(ss.Post().Save(&model.Post{ + UserId: model.NewId(), + ChannelId: o1.Id, + Message: "test", + })).(*model.Post) + + if r1 := <-ss.Channel().GetMemberForPost(p1.Id, m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } else if r1.Data.(*model.ChannelMember).ToJson() != m1.ToJson() { + t.Fatal("invalid returned channel member") + } + + if r2 := <-ss.Channel().GetMemberForPost(p1.Id, model.NewId()); r2.Err == nil { + t.Fatal("shouldn't have returned a member") + } +} + +func testGetMemberCount(t *testing.T, ss store.Store) { + teamId := model.NewId() + + c1 := model.Channel{ + TeamId: teamId, + DisplayName: "Channel1", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(&c1)) + + c2 := model.Channel{ + TeamId: teamId, + DisplayName: "Channel2", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(&c2)) + + u1 := &model.User{ + Email: model.NewId(), + DeleteAt: 0, + } + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + m1 := model.ChannelMember{ + ChannelId: c1.Id, + UserId: u1.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(&m1)) + + if result := <-ss.Channel().GetMemberCount(c1.Id, false); result.Err != nil { + t.Fatalf("failed to get member count: %v", result.Err) + } else if result.Data.(int64) != 1 { + t.Fatalf("got incorrect member count %v", result.Data) + } + + u2 := model.User{ + Email: model.NewId(), + DeleteAt: 0, + } + store.Must(ss.User().Save(&u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + m2 := model.ChannelMember{ + ChannelId: c1.Id, + UserId: u2.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(&m2)) + + if result := <-ss.Channel().GetMemberCount(c1.Id, false); result.Err != nil { + t.Fatalf("failed to get member count: %v", result.Err) + } else if result.Data.(int64) != 2 { + t.Fatalf("got incorrect member count %v", result.Data) + } + + // make sure members of other channels aren't counted + u3 := model.User{ + Email: model.NewId(), + DeleteAt: 0, + } + store.Must(ss.User().Save(&u3)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id})) + + m3 := model.ChannelMember{ + ChannelId: c2.Id, + UserId: u3.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(&m3)) + + if result := <-ss.Channel().GetMemberCount(c1.Id, false); result.Err != nil { + t.Fatalf("failed to get member count: %v", result.Err) + } else if result.Data.(int64) != 2 { + t.Fatalf("got incorrect member count %v", result.Data) + } + + // make sure inactive users aren't counted + u4 := &model.User{ + Email: model.NewId(), + DeleteAt: 10000, + } + store.Must(ss.User().Save(u4)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u4.Id})) + + m4 := model.ChannelMember{ + ChannelId: c1.Id, + UserId: u4.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(&m4)) + + if result := <-ss.Channel().GetMemberCount(c1.Id, false); result.Err != nil { + t.Fatalf("failed to get member count: %v", result.Err) + } else if result.Data.(int64) != 2 { + t.Fatalf("got incorrect member count %v", result.Data) + } +} + +func testUpdateExtrasByUser(t *testing.T, ss store.Store) { + teamId := model.NewId() + + c1 := model.Channel{ + TeamId: teamId, + DisplayName: "Channel1", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(&c1)) + + c2 := model.Channel{ + TeamId: teamId, + DisplayName: "Channel2", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + } + store.Must(ss.Channel().Save(&c2)) + + u1 := &model.User{ + Email: model.NewId(), + DeleteAt: 0, + } + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + m1 := model.ChannelMember{ + ChannelId: c1.Id, + UserId: u1.Id, + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + store.Must(ss.Channel().SaveMember(&m1)) + + u1.DeleteAt = model.GetMillis() + store.Must(ss.User().Update(u1, true)) + + if result := <-ss.Channel().ExtraUpdateByUser(u1.Id, u1.DeleteAt); result.Err != nil { + t.Fatalf("failed to update extras by user: %v", result.Err) + } + + u1.DeleteAt = 0 + store.Must(ss.User().Update(u1, true)) + + if result := <-ss.Channel().ExtraUpdateByUser(u1.Id, u1.DeleteAt); result.Err != nil { + t.Fatalf("failed to update extras by user: %v", result.Err) + } +} + +func testChannelStoreSearchMore(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "ChannelA" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + m3 := model.ChannelMember{} + m3.ChannelId = o2.Id + m3.UserId = model.NewId() + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m3)) + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "ChannelA" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o3)) + + o4 := model.Channel{} + o4.TeamId = o1.TeamId + o4.DisplayName = "ChannelB" + o4.Name = "zz" + model.NewId() + "b" + o4.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o4)) + + o5 := model.Channel{} + o5.TeamId = o1.TeamId + o5.DisplayName = "ChannelC" + o5.Name = "zz" + model.NewId() + "b" + o5.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o5)) + + if result := <-ss.Channel().SearchMore(m1.UserId, o1.TeamId, "ChannelA"); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) == 0 { + t.Fatal("should not be empty") + } + + if (*channels)[0].Name != o3.Name { + t.Fatal("wrong channel returned") + } + } + + if result := <-ss.Channel().SearchMore(m1.UserId, o1.TeamId, o4.Name); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) != 0 { + t.Fatal("should be empty") + } + } + + if result := <-ss.Channel().SearchMore(m1.UserId, o1.TeamId, o3.Name); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) == 0 { + t.Fatal("should not be empty") + } + + if (*channels)[0].Name != o3.Name { + t.Fatal("wrong channel returned") + } + } + +} + +func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "ChannelA" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + m1 := model.ChannelMember{} + m1.ChannelId = o1.Id + m1.UserId = model.NewId() + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + m2 := model.ChannelMember{} + m2.ChannelId = o1.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m2)) + + m3 := model.ChannelMember{} + m3.ChannelId = o2.Id + m3.UserId = model.NewId() + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m3)) + + o3 := model.Channel{} + o3.TeamId = o1.TeamId + o3.DisplayName = "ChannelA" + o3.Name = "zz" + model.NewId() + "b" + o3.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o3)) + + o4 := model.Channel{} + o4.TeamId = o1.TeamId + o4.DisplayName = "ChannelB" + o4.Name = "zz" + model.NewId() + "b" + o4.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o4)) + + o5 := model.Channel{} + o5.TeamId = o1.TeamId + o5.DisplayName = "ChannelC" + o5.Name = "zz" + model.NewId() + "b" + o5.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&o5)) + + if result := <-ss.Channel().SearchInTeam(o1.TeamId, "ChannelA"); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) != 2 { + t.Fatal("wrong length") + } + } + + if result := <-ss.Channel().SearchInTeam(o1.TeamId, ""); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) == 0 { + t.Fatal("should not be empty") + } + } + + if result := <-ss.Channel().SearchInTeam(o1.TeamId, "blargh"); result.Err != nil { + t.Fatal(result.Err) + } else { + channels := result.Data.(*model.ChannelList) + if len(*channels) != 0 { + t.Fatal("should be empty") + } + } +} + +func testChannelStoreGetMembersByIds(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "ChannelA" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + m1 := &model.ChannelMember{ChannelId: o1.Id, UserId: model.NewId(), NotifyProps: model.GetDefaultChannelNotifyProps()} + store.Must(ss.Channel().SaveMember(m1)) + + if r := <-ss.Channel().GetMembersByIds(m1.ChannelId, []string{m1.UserId}); r.Err != nil { + t.Fatal(r.Err) + } else { + rm1 := (*r.Data.(*model.ChannelMembers))[0] + + if rm1.ChannelId != m1.ChannelId { + t.Fatal("bad team id") + } + + if rm1.UserId != m1.UserId { + t.Fatal("bad user id") + } + } + + m2 := &model.ChannelMember{ChannelId: o1.Id, UserId: model.NewId(), NotifyProps: model.GetDefaultChannelNotifyProps()} + store.Must(ss.Channel().SaveMember(m2)) + + if r := <-ss.Channel().GetMembersByIds(m1.ChannelId, []string{m1.UserId, m2.UserId, model.NewId()}); r.Err != nil { + t.Fatal(r.Err) + } else { + rm := (*r.Data.(*model.ChannelMembers)) + + if len(rm) != 2 { + t.Fatal("return wrong number of results") + } + } + + if r := <-ss.Channel().GetMembersByIds(m1.ChannelId, []string{}); r.Err == nil { + t.Fatal("empty user ids - should have failed") + } +} + +func testChannelStoreAnalyticsDeletedTypeCount(t *testing.T, ss store.Store) { + o1 := model.Channel{} + o1.TeamId = model.NewId() + o1.DisplayName = "ChannelA" + o1.Name = "zz" + model.NewId() + "b" + o1.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o1)) + + o2 := model.Channel{} + o2.TeamId = model.NewId() + o2.DisplayName = "Channel2" + o2.Name = "zz" + model.NewId() + "b" + o2.Type = model.CHANNEL_OPEN + store.Must(ss.Channel().Save(&o2)) + + p3 := model.Channel{} + p3.TeamId = model.NewId() + p3.DisplayName = "Channel3" + p3.Name = "zz" + model.NewId() + "b" + p3.Type = model.CHANNEL_PRIVATE + store.Must(ss.Channel().Save(&p3)) + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Nickname = model.NewId() + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Nickname = model.NewId() + store.Must(ss.User().Save(u2)) + + var d4 *model.Channel + if result := <-ss.Channel().CreateDirectChannel(u1.Id, u2.Id); result.Err != nil { + t.Fatalf(result.Err.Error()) + } else { + d4 = result.Data.(*model.Channel) + } + + var openStartCount int64 + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "O"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + openStartCount = result.Data.(int64) + } + + var privateStartCount int64 + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "P"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + privateStartCount = result.Data.(int64) + } + + var directStartCount int64 + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "D"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + directStartCount = result.Data.(int64) + } + + store.Must(ss.Channel().Delete(o1.Id, model.GetMillis())) + store.Must(ss.Channel().Delete(o2.Id, model.GetMillis())) + store.Must(ss.Channel().Delete(p3.Id, model.GetMillis())) + store.Must(ss.Channel().Delete(d4.Id, model.GetMillis())) + + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "O"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + if result.Data.(int64) != openStartCount+2 { + t.Fatalf("Wrong open channel deleted count.") + } + } + + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "P"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + if result.Data.(int64) != privateStartCount+1 { + t.Fatalf("Wrong private channel deleted count.") + } + } + + if result := <-ss.Channel().AnalyticsDeletedTypeCount("", "D"); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + if result.Data.(int64) != directStartCount+1 { + t.Fatalf("Wrong direct channel deleted count.") + } + } +} + +func testChannelStoreGetPinnedPosts(t *testing.T, ss store.Store) { + o1 := store.Must(ss.Channel().Save(&model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + })).(*model.Channel) + + p1 := store.Must(ss.Post().Save(&model.Post{ + UserId: model.NewId(), + ChannelId: o1.Id, + Message: "test", + IsPinned: true, + })).(*model.Post) + + if r1 := <-ss.Channel().GetPinnedPosts(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else if r1.Data.(*model.PostList).Posts[p1.Id] == nil { + t.Fatal("didn't return relevant pinned posts") + } + + o2 := store.Must(ss.Channel().Save(&model.Channel{ + TeamId: model.NewId(), + DisplayName: "Name", + Name: "zz" + model.NewId() + "b", + Type: model.CHANNEL_OPEN, + })).(*model.Channel) + + store.Must(ss.Post().Save(&model.Post{ + UserId: model.NewId(), + ChannelId: o2.Id, + Message: "test", + })) + + if r2 := <-ss.Channel().GetPinnedPosts(o2.Id); r2.Err != nil { + t.Fatal(r2.Err) + } else if len(r2.Data.(*model.PostList).Posts) != 0 { + t.Fatal("wasn't supposed to return posts") + } +} diff --git a/store/storetest/cluster_discovery_store.go b/store/storetest/cluster_discovery_store.go new file mode 100644 index 000000000..478f2e5f8 --- /dev/null +++ b/store/storetest/cluster_discovery_store.go @@ -0,0 +1,200 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestClusterDiscoveryStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testClusterDiscoveryStore(t, ss) }) + t.Run("Delete", func(t *testing.T) { testClusterDiscoveryStoreDelete(t, ss) }) + t.Run("LastPing", func(t *testing.T) { testClusterDiscoveryStoreLastPing(t, ss) }) + t.Run("Exists", func(t *testing.T) { testClusterDiscoveryStoreExists(t, ss) }) + t.Run("ClusterDiscoveryGetStore", func(t *testing.T) { testClusterDiscoveryGetStore(t, ss) }) +} + +func testClusterDiscoveryStore(t *testing.T, ss store.Store) { + discovery := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname" + model.NewId(), + Type: "test_test", + } + + if result := <-ss.ClusterDiscovery().Save(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.ClusterDiscovery().Cleanup(); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testClusterDiscoveryStoreDelete(t *testing.T, ss store.Store) { + discovery := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname" + model.NewId(), + Type: "test_test", + } + + if result := <-ss.ClusterDiscovery().Save(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.ClusterDiscovery().Delete(discovery); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testClusterDiscoveryStoreLastPing(t *testing.T, ss store.Store) { + discovery := &model.ClusterDiscovery{ + ClusterName: "cluster_name_lastPing", + Hostname: "hostname" + model.NewId(), + Type: "test_test_lastPing" + model.NewId(), + } + + if result := <-ss.ClusterDiscovery().Save(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.ClusterDiscovery().SetLastPingAt(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + ttime := model.GetMillis() + + time.Sleep(1 * time.Second) + + if result := <-ss.ClusterDiscovery().SetLastPingAt(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.ClusterDiscovery().GetAll(discovery.Type, "cluster_name_lastPing"); result.Err != nil { + t.Fatal(result.Err) + } else { + list := result.Data.([]*model.ClusterDiscovery) + + if len(list) != 1 { + t.Fatal("should only be 1 items") + return + } + + if list[0].LastPingAt-ttime < 500 { + t.Fatal("failed to set time") + } + } + + discovery2 := &model.ClusterDiscovery{ + ClusterName: "cluster_name_missing", + Hostname: "hostname" + model.NewId(), + Type: "test_test_missing", + } + + if result := <-ss.ClusterDiscovery().SetLastPingAt(discovery2); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testClusterDiscoveryStoreExists(t *testing.T, ss store.Store) { + discovery := &model.ClusterDiscovery{ + ClusterName: "cluster_name_Exists", + Hostname: "hostname" + model.NewId(), + Type: "test_test_Exists" + model.NewId(), + } + + if result := <-ss.ClusterDiscovery().Save(discovery); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.ClusterDiscovery().Exists(discovery); result.Err != nil { + t.Fatal(result.Err) + } else { + val := result.Data.(bool) + if !val { + t.Fatal("should be true") + } + } + + discovery.ClusterName = "cluster_name_Exists2" + + if result := <-ss.ClusterDiscovery().Exists(discovery); result.Err != nil { + t.Fatal(result.Err) + } else { + val := result.Data.(bool) + if val { + t.Fatal("should be true") + } + } +} + +func testClusterDiscoveryGetStore(t *testing.T, ss store.Store) { + testType1 := model.NewId() + + discovery1 := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname1", + Type: testType1, + } + store.Must(ss.ClusterDiscovery().Save(discovery1)) + + discovery2 := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname2", + Type: testType1, + } + store.Must(ss.ClusterDiscovery().Save(discovery2)) + + discovery3 := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname3", + Type: testType1, + CreateAt: 1, + LastPingAt: 1, + } + store.Must(ss.ClusterDiscovery().Save(discovery3)) + + testType2 := model.NewId() + + discovery4 := &model.ClusterDiscovery{ + ClusterName: "cluster_name", + Hostname: "hostname1", + Type: testType2, + } + store.Must(ss.ClusterDiscovery().Save(discovery4)) + + if result := <-ss.ClusterDiscovery().GetAll(testType1, "cluster_name"); result.Err != nil { + t.Fatal(result.Err) + } else { + list := result.Data.([]*model.ClusterDiscovery) + + if len(list) != 2 { + t.Fatal("Should only have returned 2") + } + } + + if result := <-ss.ClusterDiscovery().GetAll(testType2, "cluster_name"); result.Err != nil { + t.Fatal(result.Err) + } else { + list := result.Data.([]*model.ClusterDiscovery) + + if len(list) != 1 { + t.Fatal("Should only have returned 1") + } + } + + if result := <-ss.ClusterDiscovery().GetAll(model.NewId(), "cluster_name"); result.Err != nil { + t.Fatal(result.Err) + } else { + list := result.Data.([]*model.ClusterDiscovery) + + if len(list) != 0 { + t.Fatal("shouldn't be any") + } + } +} diff --git a/store/storetest/command_store.go b/store/storetest/command_store.go new file mode 100644 index 000000000..92d981660 --- /dev/null +++ b/store/storetest/command_store.go @@ -0,0 +1,251 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestCommandStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testCommandStoreSave(t, ss) }) + t.Run("Get", func(t *testing.T) { testCommandStoreGet(t, ss) }) + t.Run("GetByTeam", func(t *testing.T) { testCommandStoreGetByTeam(t, ss) }) + t.Run("GetByTrigger", func(t *testing.T) { testCommandStoreGetByTrigger(t, ss) }) + t.Run("Delete", func(t *testing.T) { testCommandStoreDelete(t, ss) }) + t.Run("DeleteByTeam", func(t *testing.T) { testCommandStoreDeleteByTeam(t, ss) }) + t.Run("DeleteByUser", func(t *testing.T) { testCommandStoreDeleteByUser(t, ss) }) + t.Run("Update", func(t *testing.T) { testCommandStoreUpdate(t, ss) }) + t.Run("CommandCount", func(t *testing.T) { testCommandCount(t, ss) }) +} + +func testCommandStoreSave(t *testing.T, ss store.Store) { + o1 := model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + if err := (<-ss.Command().Save(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Command().Save(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func testCommandStoreGet(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if err := (<-ss.Command().Get("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testCommandStoreGetByTeam(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().GetByTeam(o1.TeamId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.Command)[0].CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if result := <-ss.Command().GetByTeam("123"); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.Command)) != 0 { + t.Fatal("no commands should have returned") + } + } +} + +func testCommandStoreGetByTrigger(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger1" + + o2 := &model.Command{} + o2.CreatorId = model.NewId() + o2.Method = model.COMMAND_METHOD_POST + o2.TeamId = model.NewId() + o2.URL = "http://nowhere.com/" + o2.Trigger = "trigger1" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + o2 = (<-ss.Command().Save(o2)).Data.(*model.Command) + + if r1 := <-ss.Command().GetByTrigger(o1.TeamId, o1.Trigger); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).Id != o1.Id { + t.Fatal("invalid returned command") + } + } + + store.Must(ss.Command().Delete(o1.Id, model.GetMillis())) + + if result := <-ss.Command().GetByTrigger(o1.TeamId, o1.Trigger); result.Err == nil { + t.Fatal("no commands should have returned") + } +} + +func testCommandStoreDelete(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if r2 := <-ss.Command().Delete(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Command().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testCommandStoreDeleteByTeam(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if r2 := <-ss.Command().PermanentDeleteByTeam(o1.TeamId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Command().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testCommandStoreDeleteByUser(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Command).CreateAt != o1.CreateAt { + t.Fatal("invalid returned command") + } + } + + if r2 := <-ss.Command().PermanentDeleteByUser(o1.CreatorId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Command().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testCommandStoreUpdate(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + o1.Token = model.NewId() + + if r2 := <-ss.Command().Update(o1); r2.Err != nil { + t.Fatal(r2.Err) + } +} + +func testCommandCount(t *testing.T, ss store.Store) { + o1 := &model.Command{} + o1.CreatorId = model.NewId() + o1.Method = model.COMMAND_METHOD_POST + o1.TeamId = model.NewId() + o1.URL = "http://nowhere.com/" + o1.Trigger = "trigger" + + o1 = (<-ss.Command().Save(o1)).Data.(*model.Command) + + if r1 := <-ss.Command().AnalyticsCommandCount(""); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) == 0 { + t.Fatal("should be at least 1 command") + } + } + + if r2 := <-ss.Command().AnalyticsCommandCount(o1.TeamId); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if r2.Data.(int64) != 1 { + t.Fatal("should be 1 command") + } + } +} diff --git a/store/storetest/command_webhook_store.go b/store/storetest/command_webhook_store.go new file mode 100644 index 000000000..a1f97b979 --- /dev/null +++ b/store/storetest/command_webhook_store.go @@ -0,0 +1,68 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "net/http" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestCommandWebhookStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testCommandWebhookStore(t, ss) }) +} + +func testCommandWebhookStore(t *testing.T, ss store.Store) { + cws := ss.CommandWebhook() + + h1 := &model.CommandWebhook{} + h1.CommandId = model.NewId() + h1.UserId = model.NewId() + h1.ChannelId = model.NewId() + h1 = (<-cws.Save(h1)).Data.(*model.CommandWebhook) + + if r1 := <-cws.Get(h1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if *r1.Data.(*model.CommandWebhook) != *h1 { + t.Fatal("invalid returned webhook") + } + } + + if err := (<-cws.Get("123")).Err; err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for missing id") + } + + h2 := &model.CommandWebhook{} + h2.CreateAt = model.GetMillis() - 2*model.COMMAND_WEBHOOK_LIFETIME + h2.CommandId = model.NewId() + h2.UserId = model.NewId() + h2.ChannelId = model.NewId() + h2 = (<-cws.Save(h2)).Data.(*model.CommandWebhook) + + if err := (<-cws.Get(h2.Id)).Err; err == nil || err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for expired webhook") + } + + cws.Cleanup() + + if err := (<-cws.Get(h1.Id)).Err; err != nil { + t.Fatal("Should have no error getting unexpired webhook") + } + + if err := (<-cws.Get(h2.Id)).Err; err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for expired webhook") + } + + if err := (<-cws.TryUse(h1.Id, 1)).Err; err != nil { + t.Fatal("Should be able to use webhook once") + } + + if err := (<-cws.TryUse(h1.Id, 1)).Err; err == nil || err.StatusCode != http.StatusBadRequest { + t.Fatal("Should be able to use webhook once") + } +} diff --git a/store/storetest/compliance_store.go b/store/storetest/compliance_store.go new file mode 100644 index 000000000..1069bb88b --- /dev/null +++ b/store/storetest/compliance_store.go @@ -0,0 +1,318 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestComplianceStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testComplianceStore(t, ss) }) + t.Run("ComplianceExport", func(t *testing.T) { testComplianceExport(t, ss) }) + t.Run("ComplianceExportDirectMessages", func(t *testing.T) { testComplianceExportDirectMessages(t, ss) }) +} + +func testComplianceStore(t *testing.T, ss store.Store) { + compliance1 := &model.Compliance{Desc: "Audit for federal subpoena case #22443", UserId: model.NewId(), Status: model.COMPLIANCE_STATUS_FAILED, StartAt: model.GetMillis() - 1, EndAt: model.GetMillis() + 1, Type: model.COMPLIANCE_TYPE_ADHOC} + store.Must(ss.Compliance().Save(compliance1)) + time.Sleep(100 * time.Millisecond) + + compliance2 := &model.Compliance{Desc: "Audit for federal subpoena case #11458", UserId: model.NewId(), Status: model.COMPLIANCE_STATUS_RUNNING, StartAt: model.GetMillis() - 1, EndAt: model.GetMillis() + 1, Type: model.COMPLIANCE_TYPE_ADHOC} + store.Must(ss.Compliance().Save(compliance2)) + time.Sleep(100 * time.Millisecond) + + c := ss.Compliance().GetAll(0, 1000) + result := <-c + compliances := result.Data.(model.Compliances) + + if compliances[0].Status != model.COMPLIANCE_STATUS_RUNNING && compliance2.Id != compliances[0].Id { + t.Fatal() + } + + compliance2.Status = model.COMPLIANCE_STATUS_FAILED + store.Must(ss.Compliance().Update(compliance2)) + + c = ss.Compliance().GetAll(0, 1000) + result = <-c + compliances = result.Data.(model.Compliances) + + if compliances[0].Status != model.COMPLIANCE_STATUS_FAILED && compliance2.Id != compliances[0].Id { + t.Fatal() + } + + c = ss.Compliance().GetAll(0, 1) + result = <-c + compliances = result.Data.(model.Compliances) + + if len(compliances) != 1 { + t.Fatal("should only have returned 1") + } + + c = ss.Compliance().GetAll(1, 1) + result = <-c + compliances = result.Data.(model.Compliances) + + if len(compliances) != 1 { + t.Fatal("should only have returned 1") + } + + rc2 := (<-ss.Compliance().Get(compliance2.Id)).Data.(*model.Compliance) + if rc2.Status != compliance2.Status { + t.Fatal() + } +} + +func testComplianceExport(t *testing.T, ss store.Store) { + time.Sleep(100 * time.Millisecond) + + t1 := &model.Team{} + t1.DisplayName = "DisplayName" + t1.Name = "zz" + model.NewId() + "b" + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + t1 = store.Must(ss.Team().Save(t1)).(*model.Team) + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Username = model.NewId() + u1 = store.Must(ss.User().Save(u1)).(*model.User) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: t1.Id, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Username = model.NewId() + u2 = store.Must(ss.User().Save(u2)).(*model.User) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: t1.Id, UserId: u2.Id})) + + c1 := &model.Channel{} + c1.TeamId = t1.Id + c1.DisplayName = "Channel2" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = store.Must(ss.Channel().Save(c1)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.CreateAt = model.GetMillis() + o1.Message = "zz" + model.NewId() + "b" + o1 = store.Must(ss.Post().Save(o1)).(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = u1.Id + o1a.CreateAt = o1.CreateAt + 10 + o1a.Message = "zz" + model.NewId() + "b" + o1a = store.Must(ss.Post().Save(o1a)).(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = u1.Id + o2.CreateAt = o1.CreateAt + 20 + o2.Message = "zz" + model.NewId() + "b" + o2 = store.Must(ss.Post().Save(o2)).(*model.Post) + + o2a := &model.Post{} + o2a.ChannelId = c1.Id + o2a.UserId = u2.Id + o2a.CreateAt = o1.CreateAt + 30 + o2a.Message = "zz" + model.NewId() + "b" + o2a = store.Must(ss.Post().Save(o2a)).(*model.Post) + + time.Sleep(100 * time.Millisecond) + + cr1 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1} + if r1 := <-ss.Compliance().ComplianceExport(cr1); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 4 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + + if cposts[3].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + } + + cr2 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email} + if r1 := <-ss.Compliance().ComplianceExport(cr2); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 1 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + } + + cr3 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email + ", " + u1.Email} + if r1 := <-ss.Compliance().ComplianceExport(cr3); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 4 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + + if cposts[3].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + } + + cr4 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Keywords: o2a.Message} + if r1 := <-ss.Compliance().ComplianceExport(cr4); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 1 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + } + + cr5 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Keywords: o2a.Message + " " + o1.Message} + if r1 := <-ss.Compliance().ComplianceExport(cr5); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 2 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + } + + cr6 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o2a.CreateAt + 1, Emails: u2.Email + ", " + u1.Email, Keywords: o2a.Message + " " + o1.Message} + if r1 := <-ss.Compliance().ComplianceExport(cr6); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 2 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + + if cposts[1].PostId != o2a.Id { + t.Fatal("Wrong sort") + } + } +} + +func testComplianceExportDirectMessages(t *testing.T, ss store.Store) { + time.Sleep(100 * time.Millisecond) + + t1 := &model.Team{} + t1.DisplayName = "DisplayName" + t1.Name = "zz" + model.NewId() + "b" + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + t1 = store.Must(ss.Team().Save(t1)).(*model.Team) + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Username = model.NewId() + u1 = store.Must(ss.User().Save(u1)).(*model.User) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: t1.Id, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Username = model.NewId() + u2 = store.Must(ss.User().Save(u2)).(*model.User) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: t1.Id, UserId: u2.Id})) + + c1 := &model.Channel{} + c1.TeamId = t1.Id + c1.DisplayName = "Channel2" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = store.Must(ss.Channel().Save(c1)).(*model.Channel) + + cDM := store.Must(ss.Channel().CreateDirectChannel(u1.Id, u2.Id)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = u1.Id + o1.CreateAt = model.GetMillis() + o1.Message = "zz" + model.NewId() + "b" + o1 = store.Must(ss.Post().Save(o1)).(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = u1.Id + o1a.CreateAt = o1.CreateAt + 10 + o1a.Message = "zz" + model.NewId() + "b" + o1a = store.Must(ss.Post().Save(o1a)).(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = u1.Id + o2.CreateAt = o1.CreateAt + 20 + o2.Message = "zz" + model.NewId() + "b" + o2 = store.Must(ss.Post().Save(o2)).(*model.Post) + + o2a := &model.Post{} + o2a.ChannelId = c1.Id + o2a.UserId = u2.Id + o2a.CreateAt = o1.CreateAt + 30 + o2a.Message = "zz" + model.NewId() + "b" + o2a = store.Must(ss.Post().Save(o2a)).(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = cDM.Id + o3.UserId = u1.Id + o3.CreateAt = o1.CreateAt + 40 + o3.Message = "zz" + model.NewId() + "b" + o3 = store.Must(ss.Post().Save(o3)).(*model.Post) + + time.Sleep(100 * time.Millisecond) + + cr1 := &model.Compliance{Desc: "test" + model.NewId(), StartAt: o1.CreateAt - 1, EndAt: o3.CreateAt + 1, Emails: u1.Email} + if r1 := <-ss.Compliance().ComplianceExport(cr1); r1.Err != nil { + t.Fatal(r1.Err) + } else { + cposts := r1.Data.([]*model.CompliancePost) + + if len(cposts) != 4 { + t.Fatal("return wrong results length") + } + + if cposts[0].PostId != o1.Id { + t.Fatal("Wrong sort") + } + + if cposts[len(cposts)-1].PostId != o3.Id { + t.Fatal("Wrong sort") + } + } +} diff --git a/store/storetest/emoji_store.go b/store/storetest/emoji_store.go new file mode 100644 index 000000000..fc5a31a43 --- /dev/null +++ b/store/storetest/emoji_store.go @@ -0,0 +1,175 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestEmojiStore(t *testing.T, ss store.Store) { + t.Run("EmojiSaveDelete", func(t *testing.T) { testEmojiSaveDelete(t, ss) }) + t.Run("EmojiGet", func(t *testing.T) { testEmojiGet(t, ss) }) + t.Run("EmojiGetByName", func(t *testing.T) { testEmojiGetByName(t, ss) }) + t.Run("EmojiGetList", func(t *testing.T) { testEmojiGetList(t, ss) }) +} + +func testEmojiSaveDelete(t *testing.T, ss store.Store) { + emoji1 := &model.Emoji{ + CreatorId: model.NewId(), + Name: model.NewId(), + } + + if result := <-ss.Emoji().Save(emoji1); result.Err != nil { + t.Fatal(result.Err) + } + + if len(emoji1.Id) != 26 { + t.Fatal("should've set id for emoji") + } + + emoji2 := model.Emoji{ + CreatorId: model.NewId(), + Name: emoji1.Name, + } + if result := <-ss.Emoji().Save(&emoji2); result.Err == nil { + t.Fatal("shouldn't be able to save emoji with duplicate name") + } + + if result := <-ss.Emoji().Delete(emoji1.Id, time.Now().Unix()); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.Emoji().Save(&emoji2); result.Err != nil { + t.Fatal("should be able to save emoji with duplicate name now that original has been deleted", result.Err) + } + + if result := <-ss.Emoji().Delete(emoji2.Id, time.Now().Unix()+1); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testEmojiGet(t *testing.T, ss store.Store) { + emojis := []model.Emoji{ + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + } + + for i, emoji := range emojis { + emojis[i] = *store.Must(ss.Emoji().Save(&emoji)).(*model.Emoji) + } + defer func() { + for _, emoji := range emojis { + store.Must(ss.Emoji().Delete(emoji.Id, time.Now().Unix())) + } + }() + + for _, emoji := range emojis { + if result := <-ss.Emoji().Get(emoji.Id, false); result.Err != nil { + t.Fatalf("failed to get emoji with id %v: %v", emoji.Id, result.Err) + } + } + + for _, emoji := range emojis { + if result := <-ss.Emoji().Get(emoji.Id, true); result.Err != nil { + t.Fatalf("failed to get emoji with id %v: %v", emoji.Id, result.Err) + } + } + + for _, emoji := range emojis { + if result := <-ss.Emoji().Get(emoji.Id, true); result.Err != nil { + t.Fatalf("failed to get emoji with id %v: %v", emoji.Id, result.Err) + } + } +} + +func testEmojiGetByName(t *testing.T, ss store.Store) { + emojis := []model.Emoji{ + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + } + + for i, emoji := range emojis { + emojis[i] = *store.Must(ss.Emoji().Save(&emoji)).(*model.Emoji) + } + defer func() { + for _, emoji := range emojis { + store.Must(ss.Emoji().Delete(emoji.Id, time.Now().Unix())) + } + }() + + for _, emoji := range emojis { + if result := <-ss.Emoji().GetByName(emoji.Name); result.Err != nil { + t.Fatalf("failed to get emoji with name %v: %v", emoji.Name, result.Err) + } + } +} + +func testEmojiGetList(t *testing.T, ss store.Store) { + emojis := []model.Emoji{ + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + { + CreatorId: model.NewId(), + Name: model.NewId(), + }, + } + + for i, emoji := range emojis { + emojis[i] = *store.Must(ss.Emoji().Save(&emoji)).(*model.Emoji) + } + defer func() { + for _, emoji := range emojis { + store.Must(ss.Emoji().Delete(emoji.Id, time.Now().Unix())) + } + }() + + if result := <-ss.Emoji().GetList(0, 100); result.Err != nil { + t.Fatal(result.Err) + } else { + for _, emoji := range emojis { + found := false + + for _, savedEmoji := range result.Data.([]*model.Emoji) { + if emoji.Id == savedEmoji.Id { + found = true + break + } + } + + if !found { + t.Fatalf("failed to get emoji with id %v", emoji.Id) + } + } + } +} diff --git a/store/storetest/file_info_store.go b/store/storetest/file_info_store.go new file mode 100644 index 000000000..9b3388db5 --- /dev/null +++ b/store/storetest/file_info_store.go @@ -0,0 +1,296 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "fmt" + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestFileInfoStore(t *testing.T, ss store.Store) { + t.Run("FileInfoSaveGet", func(t *testing.T) { testFileInfoSaveGet(t, ss) }) + t.Run("FileInfoSaveGetByPath", func(t *testing.T) { testFileInfoSaveGetByPath(t, ss) }) + t.Run("FileInfoGetForPost", func(t *testing.T) { testFileInfoGetForPost(t, ss) }) + t.Run("FileInfoAttachToPost", func(t *testing.T) { testFileInfoAttachToPost(t, ss) }) + t.Run("FileInfoDeleteForPost", func(t *testing.T) { testFileInfoDeleteForPost(t, ss) }) + t.Run("FileInfoPermanentDelete", func(t *testing.T) { testFileInfoPermanentDelete(t, ss) }) + t.Run("FileInfoPermanentDeleteBatch", func(t *testing.T) { testFileInfoPermanentDeleteBatch(t, ss) }) +} + +func testFileInfoSaveGet(t *testing.T, ss store.Store) { + info := &model.FileInfo{ + CreatorId: model.NewId(), + Path: "file.txt", + } + + if result := <-ss.FileInfo().Save(info); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.(*model.FileInfo); len(returned.Id) == 0 { + t.Fatal("should've assigned an id to FileInfo") + } else { + info = returned + } + defer func() { + <-ss.FileInfo().PermanentDelete(info.Id) + }() + + if result := <-ss.FileInfo().Get(info.Id); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.(*model.FileInfo); returned.Id != info.Id { + t.Log(info) + t.Log(returned) + t.Fatal("should've returned correct FileInfo") + } + + info2 := store.Must(ss.FileInfo().Save(&model.FileInfo{ + CreatorId: model.NewId(), + Path: "file.txt", + DeleteAt: 123, + })).(*model.FileInfo) + + if result := <-ss.FileInfo().Get(info2.Id); result.Err == nil { + t.Fatal("shouldn't have gotten deleted file") + } + defer func() { + <-ss.FileInfo().PermanentDelete(info2.Id) + }() +} + +func testFileInfoSaveGetByPath(t *testing.T, ss store.Store) { + info := &model.FileInfo{ + CreatorId: model.NewId(), + Path: fmt.Sprintf("%v/file.txt", model.NewId()), + } + + if result := <-ss.FileInfo().Save(info); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.(*model.FileInfo); len(returned.Id) == 0 { + t.Fatal("should've assigned an id to FileInfo") + } else { + info = returned + } + defer func() { + <-ss.FileInfo().PermanentDelete(info.Id) + }() + + if result := <-ss.FileInfo().GetByPath(info.Path); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.(*model.FileInfo); returned.Id != info.Id { + t.Log(info) + t.Log(returned) + t.Fatal("should've returned correct FileInfo") + } + + info2 := store.Must(ss.FileInfo().Save(&model.FileInfo{ + CreatorId: model.NewId(), + Path: "file.txt", + DeleteAt: 123, + })).(*model.FileInfo) + + if result := <-ss.FileInfo().GetByPath(info2.Id); result.Err == nil { + t.Fatal("shouldn't have gotten deleted file") + } + defer func() { + <-ss.FileInfo().PermanentDelete(info2.Id) + }() +} + +func testFileInfoGetForPost(t *testing.T, ss store.Store) { + userId := model.NewId() + postId := model.NewId() + + infos := []*model.FileInfo{ + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + }, + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + }, + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + DeleteAt: 123, + }, + { + PostId: model.NewId(), + CreatorId: userId, + Path: "file.txt", + }, + } + + for i, info := range infos { + infos[i] = store.Must(ss.FileInfo().Save(info)).(*model.FileInfo) + defer func(id string) { + <-ss.FileInfo().PermanentDelete(id) + }(infos[i].Id) + } + + if result := <-ss.FileInfo().GetForPost(postId, true, false); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.([]*model.FileInfo); len(returned) != 2 { + t.Fatal("should've returned exactly 2 file infos") + } + + if result := <-ss.FileInfo().GetForPost(postId, false, false); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.([]*model.FileInfo); len(returned) != 2 { + t.Fatal("should've returned exactly 2 file infos") + } + + if result := <-ss.FileInfo().GetForPost(postId, true, true); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.([]*model.FileInfo); len(returned) != 2 { + t.Fatal("should've returned exactly 2 file infos") + } +} + +func testFileInfoAttachToPost(t *testing.T, ss store.Store) { + userId := model.NewId() + postId := model.NewId() + + info1 := store.Must(ss.FileInfo().Save(&model.FileInfo{ + CreatorId: userId, + Path: "file.txt", + })).(*model.FileInfo) + defer func() { + <-ss.FileInfo().PermanentDelete(info1.Id) + }() + + if len(info1.PostId) != 0 { + t.Fatal("file shouldn't have a PostId") + } + + if result := <-ss.FileInfo().AttachToPost(info1.Id, postId); result.Err != nil { + t.Fatal(result.Err) + } else { + info1 = store.Must(ss.FileInfo().Get(info1.Id)).(*model.FileInfo) + } + + if len(info1.PostId) == 0 { + t.Fatal("file should now have a PostId") + } + + info2 := store.Must(ss.FileInfo().Save(&model.FileInfo{ + CreatorId: userId, + Path: "file.txt", + })).(*model.FileInfo) + defer func() { + <-ss.FileInfo().PermanentDelete(info2.Id) + }() + + if result := <-ss.FileInfo().AttachToPost(info2.Id, postId); result.Err != nil { + t.Fatal(result.Err) + } else { + info2 = store.Must(ss.FileInfo().Get(info2.Id)).(*model.FileInfo) + } + + if result := <-ss.FileInfo().GetForPost(postId, true, false); result.Err != nil { + t.Fatal(result.Err) + } else if infos := result.Data.([]*model.FileInfo); len(infos) != 2 { + t.Fatal("should've returned exactly 2 file infos") + } +} + +func testFileInfoDeleteForPost(t *testing.T, ss store.Store) { + userId := model.NewId() + postId := model.NewId() + + infos := []*model.FileInfo{ + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + }, + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + }, + { + PostId: postId, + CreatorId: userId, + Path: "file.txt", + DeleteAt: 123, + }, + { + PostId: model.NewId(), + CreatorId: userId, + Path: "file.txt", + }, + } + + for i, info := range infos { + infos[i] = store.Must(ss.FileInfo().Save(info)).(*model.FileInfo) + defer func(id string) { + <-ss.FileInfo().PermanentDelete(id) + }(infos[i].Id) + } + + if result := <-ss.FileInfo().DeleteForPost(postId); result.Err != nil { + t.Fatal(result.Err) + } + + if infos := store.Must(ss.FileInfo().GetForPost(postId, true, false)).([]*model.FileInfo); len(infos) != 0 { + t.Fatal("shouldn't have returned any file infos") + } +} + +func testFileInfoPermanentDelete(t *testing.T, ss store.Store) { + info := store.Must(ss.FileInfo().Save(&model.FileInfo{ + PostId: model.NewId(), + CreatorId: model.NewId(), + Path: "file.txt", + })).(*model.FileInfo) + + if result := <-ss.FileInfo().PermanentDelete(info.Id); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testFileInfoPermanentDeleteBatch(t *testing.T, ss store.Store) { + postId := model.NewId() + + store.Must(ss.FileInfo().Save(&model.FileInfo{ + PostId: postId, + CreatorId: model.NewId(), + Path: "file.txt", + CreateAt: 1000, + })) + + store.Must(ss.FileInfo().Save(&model.FileInfo{ + PostId: postId, + CreatorId: model.NewId(), + Path: "file.txt", + CreateAt: 1200, + })) + + store.Must(ss.FileInfo().Save(&model.FileInfo{ + PostId: postId, + CreatorId: model.NewId(), + Path: "file.txt", + CreateAt: 2000, + })) + + if result := <-ss.FileInfo().GetForPost(postId, true, false); result.Err != nil { + t.Fatal(result.Err) + } else if len(result.Data.([]*model.FileInfo)) != 3 { + t.Fatal("Expected 3 fileInfos") + } + + store.Must(ss.FileInfo().PermanentDeleteBatch(1500, 1000)) + + if result := <-ss.FileInfo().GetForPost(postId, true, false); result.Err != nil { + t.Fatal(result.Err) + } else if len(result.Data.([]*model.FileInfo)) != 1 { + t.Fatal("Expected 3 fileInfos") + } +} diff --git a/store/storetest/job_store.go b/store/storetest/job_store.go new file mode 100644 index 000000000..631df08fd --- /dev/null +++ b/store/storetest/job_store.go @@ -0,0 +1,507 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/stretchr/testify/assert" +) + +func TestJobStore(t *testing.T, ss store.Store) { + t.Run("JobSaveGet", func(t *testing.T) { testJobSaveGet(t, ss) }) + t.Run("JobGetAllByType", func(t *testing.T) { testJobGetAllByType(t, ss) }) + t.Run("JobGetAllByTypePage", func(t *testing.T) { testJobGetAllByTypePage(t, ss) }) + t.Run("JobGetAllPage", func(t *testing.T) { testJobGetAllPage(t, ss) }) + t.Run("JobGetAllByStatus", func(t *testing.T) { testJobGetAllByStatus(t, ss) }) + t.Run("GetNewestJobByStatusAndType", func(t *testing.T) { testJobStoreGetNewestJobByStatusAndType(t, ss) }) + t.Run("GetCountByStatusAndType", func(t *testing.T) { testJobStoreGetCountByStatusAndType(t, ss) }) + t.Run("JobUpdateOptimistically", func(t *testing.T) { testJobUpdateOptimistically(t, ss) }) + t.Run("JobUpdateStatusUpdateStatusOptimistically", func(t *testing.T) { testJobUpdateStatusUpdateStatusOptimistically(t, ss) }) + t.Run("JobDelete", func(t *testing.T) { testJobDelete(t, ss) }) +} + +func testJobSaveGet(t *testing.T, ss store.Store) { + job := &model.Job{ + Id: model.NewId(), + Type: model.NewId(), + Status: model.NewId(), + Data: map[string]string{ + "Processed": "0", + "Total": "12345", + "LastProcessed": "abcd", + }, + } + + if result := <-ss.Job().Save(job); result.Err != nil { + t.Fatal(result.Err) + } + + defer func() { + <-ss.Job().Delete(job.Id) + }() + + if result := <-ss.Job().Get(job.Id); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.(*model.Job); received.Id != job.Id { + t.Fatal("received incorrect job after save") + } else if received.Data["Total"] != "12345" { + t.Fatal("data field was not retrieved successfully:", received.Data) + } +} + +func testJobGetAllByType(t *testing.T, ss store.Store) { + jobType := model.NewId() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType, + }, + { + Id: model.NewId(), + Type: jobType, + }, + { + Id: model.NewId(), + Type: model.NewId(), + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + if result := <-ss.Job().GetAllByType(jobType); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) != 2 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[0].Id && received[1].Id != jobs[0].Id { + t.Fatal("should've received first jobs") + } else if received[0].Id != jobs[1].Id && received[1].Id != jobs[1].Id { + t.Fatal("should've received second jobs") + } +} + +func testJobGetAllByTypePage(t *testing.T, ss store.Store) { + jobType := model.NewId() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType, + CreateAt: 1000, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: 999, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: 1001, + }, + { + Id: model.NewId(), + Type: model.NewId(), + CreateAt: 1002, + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + if result := <-ss.Job().GetAllByTypePage(jobType, 0, 2); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) != 2 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[2].Id { + t.Fatal("should've received newest job first") + } else if received[1].Id != jobs[0].Id { + t.Fatal("should've received second newest job second") + } + + if result := <-ss.Job().GetAllByTypePage(jobType, 2, 2); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) != 1 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[1].Id { + t.Fatal("should've received oldest job last") + } +} + +func testJobGetAllPage(t *testing.T, ss store.Store) { + jobType := model.NewId() + createAtTime := model.GetMillis() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType, + CreateAt: createAtTime + 1, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: createAtTime, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: createAtTime + 2, + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + if result := <-ss.Job().GetAllPage(0, 2); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) != 2 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[2].Id { + t.Fatal("should've received newest job first") + } else if received[1].Id != jobs[0].Id { + t.Fatal("should've received second newest job second") + } + + if result := <-ss.Job().GetAllPage(2, 2); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) < 1 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[1].Id { + t.Fatal("should've received oldest job last") + } +} + +func testJobGetAllByStatus(t *testing.T, ss store.Store) { + jobType := model.NewId() + status := model.NewId() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType, + CreateAt: 1000, + Status: status, + Data: map[string]string{ + "test": "data", + }, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: 999, + Status: status, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: 1001, + Status: status, + }, + { + Id: model.NewId(), + Type: jobType, + CreateAt: 1002, + Status: model.NewId(), + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + if result := <-ss.Job().GetAllByStatus(status); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.Job); len(received) != 3 { + t.Fatal("received wrong number of jobs") + } else if received[0].Id != jobs[1].Id || received[1].Id != jobs[0].Id || received[2].Id != jobs[2].Id { + t.Fatal("should've received jobs ordered by CreateAt time") + } else if received[1].Data["test"] != "data" { + t.Fatal("should've received job data field back as saved") + } +} + +func testJobStoreGetNewestJobByStatusAndType(t *testing.T, ss store.Store) { + jobType1 := model.NewId() + jobType2 := model.NewId() + status1 := model.NewId() + status2 := model.NewId() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 1001, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 1000, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType2, + CreateAt: 1003, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 1004, + Status: status2, + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + result := <-ss.Job().GetNewestJobByStatusAndType(status1, jobType1) + assert.Nil(t, result.Err) + assert.EqualValues(t, jobs[0].Id, result.Data.(*model.Job).Id) + + result = <-ss.Job().GetNewestJobByStatusAndType(model.NewId(), model.NewId()) + assert.Nil(t, result.Err) + assert.Nil(t, result.Data.(*model.Job)) +} + +func testJobStoreGetCountByStatusAndType(t *testing.T, ss store.Store) { + jobType1 := model.NewId() + jobType2 := model.NewId() + status1 := model.NewId() + status2 := model.NewId() + + jobs := []*model.Job{ + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 1000, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 999, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType2, + CreateAt: 1001, + Status: status1, + }, + { + Id: model.NewId(), + Type: jobType1, + CreateAt: 1002, + Status: status2, + }, + } + + for _, job := range jobs { + store.Must(ss.Job().Save(job)) + defer ss.Job().Delete(job.Id) + } + + result := <-ss.Job().GetCountByStatusAndType(status1, jobType1) + assert.Nil(t, result.Err) + assert.EqualValues(t, 2, result.Data.(int64)) + + result = <-ss.Job().GetCountByStatusAndType(status2, jobType2) + assert.Nil(t, result.Err) + assert.EqualValues(t, 0, result.Data.(int64)) + + result = <-ss.Job().GetCountByStatusAndType(status1, jobType2) + assert.Nil(t, result.Err) + assert.EqualValues(t, 1, result.Data.(int64)) + + result = <-ss.Job().GetCountByStatusAndType(status2, jobType1) + assert.Nil(t, result.Err) + assert.EqualValues(t, 1, result.Data.(int64)) +} + +func testJobUpdateOptimistically(t *testing.T, ss store.Store) { + job := &model.Job{ + Id: model.NewId(), + Type: model.JOB_TYPE_DATA_RETENTION, + CreateAt: model.GetMillis(), + Status: model.JOB_STATUS_PENDING, + } + + if result := <-ss.Job().Save(job); result.Err != nil { + t.Fatal(result.Err) + } + defer ss.Job().Delete(job.Id) + + job.LastActivityAt = model.GetMillis() + job.Status = model.JOB_STATUS_IN_PROGRESS + job.Progress = 50 + job.Data = map[string]string{ + "Foo": "Bar", + } + + if result := <-ss.Job().UpdateOptimistically(job, model.JOB_STATUS_SUCCESS); result.Err != nil { + if result.Data.(bool) { + t.Fatal("should have failed due to incorrect old status") + } + } + + time.Sleep(2 * time.Millisecond) + + if result := <-ss.Job().UpdateOptimistically(job, model.JOB_STATUS_PENDING); result.Err != nil { + t.Fatal(result.Err) + } else { + if !result.Data.(bool) { + t.Fatal("Should have successfully updated") + } + + var updatedJob *model.Job + + if result := <-ss.Job().Get(job.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + updatedJob = result.Data.(*model.Job) + } + + if updatedJob.Type != job.Type || updatedJob.CreateAt != job.CreateAt || updatedJob.Status != job.Status || updatedJob.LastActivityAt <= job.LastActivityAt || updatedJob.Progress != job.Progress || updatedJob.Data["Foo"] != job.Data["Foo"] { + t.Fatal("Some update property was not as expected") + } + } + +} + +func testJobUpdateStatusUpdateStatusOptimistically(t *testing.T, ss store.Store) { + job := &model.Job{ + Id: model.NewId(), + Type: model.JOB_TYPE_DATA_RETENTION, + CreateAt: model.GetMillis(), + Status: model.JOB_STATUS_SUCCESS, + } + + var lastUpdateAt int64 + if result := <-ss.Job().Save(job); result.Err != nil { + t.Fatal(result.Err) + } else { + lastUpdateAt = result.Data.(*model.Job).LastActivityAt + } + + defer ss.Job().Delete(job.Id) + + time.Sleep(2 * time.Millisecond) + + if result := <-ss.Job().UpdateStatus(job.Id, model.JOB_STATUS_PENDING); result.Err != nil { + t.Fatal(result.Err) + } else { + received := result.Data.(*model.Job) + if received.Status != model.JOB_STATUS_PENDING { + t.Fatal("status wasn't updated") + } + if received.LastActivityAt <= lastUpdateAt { + t.Fatal("lastActivityAt wasn't updated") + } + lastUpdateAt = received.LastActivityAt + } + + time.Sleep(2 * time.Millisecond) + + if result := <-ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_SUCCESS); result.Err != nil { + t.Fatal(result.Err) + } else { + if result.Data.(bool) { + t.Fatal("should be false due to incorrect original status") + } + } + + if result := <-ss.Job().Get(job.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + received := result.Data.(*model.Job) + if received.Status != model.JOB_STATUS_PENDING { + t.Fatal("should still be pending") + } + if received.LastActivityAt != lastUpdateAt { + t.Fatal("last activity at shouldn't have changed") + } + } + + time.Sleep(2 * time.Millisecond) + + if result := <-ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS); result.Err != nil { + t.Fatal(result.Err) + } else { + if !result.Data.(bool) { + t.Fatal("should have succeeded") + } + } + + var startAtSet int64 + if result := <-ss.Job().Get(job.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + received := result.Data.(*model.Job) + if received.Status != model.JOB_STATUS_IN_PROGRESS { + t.Fatal("should be in progress") + } + if received.StartAt == 0 { + t.Fatal("received should have start at set") + } + if received.LastActivityAt <= lastUpdateAt { + t.Fatal("lastActivityAt wasn't updated") + } + lastUpdateAt = received.LastActivityAt + startAtSet = received.StartAt + } + + time.Sleep(2 * time.Millisecond) + + if result := <-ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_SUCCESS); result.Err != nil { + t.Fatal(result.Err) + } else { + if !result.Data.(bool) { + t.Fatal("should have succeeded") + } + } + + if result := <-ss.Job().Get(job.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + received := result.Data.(*model.Job) + if received.Status != model.JOB_STATUS_SUCCESS { + t.Fatal("should be success status") + } + if received.StartAt != startAtSet { + t.Fatal("startAt should not have changed") + } + if received.LastActivityAt <= lastUpdateAt { + t.Fatal("lastActivityAt wasn't updated") + } + lastUpdateAt = received.LastActivityAt + } +} + +func testJobDelete(t *testing.T, ss store.Store) { + job := store.Must(ss.Job().Save(&model.Job{ + Id: model.NewId(), + })).(*model.Job) + + if result := <-ss.Job().Delete(job.Id); result.Err != nil { + t.Fatal(result.Err) + } +} diff --git a/store/storetest/license_store.go b/store/storetest/license_store.go new file mode 100644 index 000000000..452d37e7b --- /dev/null +++ b/store/storetest/license_store.go @@ -0,0 +1,56 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestLicenseStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testLicenseStoreSave(t, ss) }) + t.Run("Get", func(t *testing.T) { testLicenseStoreGet(t, ss) }) +} + +func testLicenseStoreSave(t *testing.T, ss store.Store) { + l1 := model.LicenseRecord{} + l1.Id = model.NewId() + l1.Bytes = "junk" + + if err := (<-ss.License().Save(&l1)).Err; err != nil { + t.Fatal("couldn't save license record", err) + } + + if err := (<-ss.License().Save(&l1)).Err; err != nil { + t.Fatal("shouldn't fail on trying to save existing license record", err) + } + + l1.Id = "" + + if err := (<-ss.License().Save(&l1)).Err; err == nil { + t.Fatal("should fail on invalid license", err) + } +} + +func testLicenseStoreGet(t *testing.T, ss store.Store) { + l1 := model.LicenseRecord{} + l1.Id = model.NewId() + l1.Bytes = "junk" + + store.Must(ss.License().Save(&l1)) + + if r := <-ss.License().Get(l1.Id); r.Err != nil { + t.Fatal("couldn't get license", r.Err) + } else { + if r.Data.(*model.LicenseRecord).Bytes != l1.Bytes { + t.Fatal("license bytes didn't match") + } + } + + if err := (<-ss.License().Get("missing")).Err; err == nil { + t.Fatal("should fail on get license", err) + } +} diff --git a/store/storetest/oauth_store.go b/store/storetest/oauth_store.go new file mode 100644 index 000000000..cd5df18da --- /dev/null +++ b/store/storetest/oauth_store.go @@ -0,0 +1,435 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestOAuthStore(t *testing.T, ss store.Store) { + t.Run("SaveApp", func(t *testing.T) { testOAuthStoreSaveApp(t, ss) }) + t.Run("GetApp", func(t *testing.T) { testOAuthStoreGetApp(t, ss) }) + t.Run("UpdateApp", func(t *testing.T) { testOAuthStoreUpdateApp(t, ss) }) + t.Run("SaveAccessData", func(t *testing.T) { testOAuthStoreSaveAccessData(t, ss) }) + t.Run("OAuthUpdateAccessData", func(t *testing.T) { testOAuthUpdateAccessData(t, ss) }) + t.Run("GetAccessData", func(t *testing.T) { testOAuthStoreGetAccessData(t, ss) }) + t.Run("RemoveAccessData", func(t *testing.T) { testOAuthStoreRemoveAccessData(t, ss) }) + t.Run("SaveAuthData", func(t *testing.T) { testOAuthStoreSaveAuthData(t, ss) }) + t.Run("GetAuthData", func(t *testing.T) { testOAuthStoreGetAuthData(t, ss) }) + t.Run("RemoveAuthData", func(t *testing.T) { testOAuthStoreRemoveAuthData(t, ss) }) + t.Run("RemoveAuthDataByUser", func(t *testing.T) { testOAuthStoreRemoveAuthDataByUser(t, ss) }) + t.Run("OAuthGetAuthorizedApps", func(t *testing.T) { testOAuthGetAuthorizedApps(t, ss) }) + t.Run("OAuthGetAccessDataByUserForApp", func(t *testing.T) { testOAuthGetAccessDataByUserForApp(t, ss) }) + t.Run("DeleteApp", func(t *testing.T) { testOAuthStoreDeleteApp(t, ss) }) +} + +func testOAuthStoreSaveApp(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + + // Try to save an app that already has an Id + a1.Id = model.NewId() + if err := (<-ss.OAuth().SaveApp(&a1)).Err; err == nil { + t.Fatal("Should have failed, cannot add an OAuth app cannot be save with an Id, it has to be updated") + } + + // Try to save an Invalid App + a1.Id = "" + if err := (<-ss.OAuth().SaveApp(&a1)).Err; err == nil { + t.Fatal("Should have failed, app should be invalid cause it doesn' have a name set") + } + + // Save the app + a1.Id = "" + a1.Name = "TestApp" + model.NewId() + if err := (<-ss.OAuth().SaveApp(&a1)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthStoreGetApp(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.Name = "TestApp" + model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + store.Must(ss.OAuth().SaveApp(&a1)) + + // Lets try to get and app that does not exists + if err := (<-ss.OAuth().GetApp("fake0123456789abcderfgret1")).Err; err == nil { + t.Fatal("Should have failed. App does not exists") + } + + if err := (<-ss.OAuth().GetApp(a1.Id)).Err; err != nil { + t.Fatal(err) + } + + // Lets try and get the app from a user that hasn't created any apps + if result := (<-ss.OAuth().GetAppByUser("fake0123456789abcderfgret1", 0, 1000)); result.Err == nil { + if len(result.Data.([]*model.OAuthApp)) > 0 { + t.Fatal("Should have failed. Fake user hasn't created any apps") + } + } else { + t.Fatal(result.Err) + } + + if err := (<-ss.OAuth().GetAppByUser(a1.CreatorId, 0, 1000)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.OAuth().GetApps(0, 1000)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthStoreUpdateApp(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.Name = "TestApp" + model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + store.Must(ss.OAuth().SaveApp(&a1)) + + // temporarily save the created app id + id := a1.Id + + a1.CreateAt = 1 + a1.ClientSecret = "pwd" + a1.CreatorId = "12345678901234567890123456" + + // Lets update the app by removing the name + a1.Name = "" + if result := <-ss.OAuth().UpdateApp(&a1); result.Err == nil { + t.Fatal("Should have failed. App name is not set") + } + + // Lets not find the app that we are trying to update + a1.Id = "fake0123456789abcderfgret1" + a1.Name = "NewName" + if result := <-ss.OAuth().UpdateApp(&a1); result.Err == nil { + t.Fatal("Should have failed. Not able to find the app") + } + + a1.Id = id + if result := <-ss.OAuth().UpdateApp(&a1); result.Err != nil { + t.Fatal(result.Err) + } else { + ua1 := (result.Data.([2]*model.OAuthApp)[0]) + if ua1.Name != "NewName" { + t.Fatal("name did not update") + } + if ua1.CreateAt == 1 { + t.Fatal("create at should not have updated") + } + if ua1.CreatorId == "12345678901234567890123456" { + t.Fatal("creator id should not have updated") + } + } +} + +func testOAuthStoreSaveAccessData(t *testing.T, ss store.Store) { + a1 := model.AccessData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + + // Lets try and save an incomplete access data + if err := (<-ss.OAuth().SaveAccessData(&a1)).Err; err == nil { + t.Fatal("Should have failed. Access data needs the token") + } + + a1.Token = model.NewId() + a1.RefreshToken = model.NewId() + a1.RedirectUri = "http://example.com" + + if err := (<-ss.OAuth().SaveAccessData(&a1)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthUpdateAccessData(t *testing.T, ss store.Store) { + a1 := model.AccessData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Token = model.NewId() + a1.RefreshToken = model.NewId() + a1.ExpiresAt = model.GetMillis() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAccessData(&a1)) + + //Try to update to invalid Refresh Token + refreshToken := a1.RefreshToken + a1.RefreshToken = model.NewId() + "123" + if err := (<-ss.OAuth().UpdateAccessData(&a1)).Err; err == nil { + t.Fatal("Should have failed with invalid token") + } + + //Try to update to invalid RedirectUri + a1.RefreshToken = model.NewId() + a1.RedirectUri = "" + if err := (<-ss.OAuth().UpdateAccessData(&a1)).Err; err == nil { + t.Fatal("Should have failed with invalid Redirect URI") + } + + // Should update fine + a1.RedirectUri = "http://example.com" + if result := <-ss.OAuth().UpdateAccessData(&a1); result.Err != nil { + t.Fatal(result.Err) + } else { + ra1 := result.Data.(*model.AccessData) + if ra1.RefreshToken == refreshToken { + t.Fatal("refresh tokens didn't match") + } + } +} + +func testOAuthStoreGetAccessData(t *testing.T, ss store.Store) { + a1 := model.AccessData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Token = model.NewId() + a1.RefreshToken = model.NewId() + a1.ExpiresAt = model.GetMillis() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAccessData(&a1)) + + if err := (<-ss.OAuth().GetAccessData("invalidToken")).Err; err == nil { + t.Fatal("Should have failed. There is no data with an invalid token") + } + + if result := <-ss.OAuth().GetAccessData(a1.Token); result.Err != nil { + t.Fatal(result.Err) + } else { + ra1 := result.Data.(*model.AccessData) + if a1.Token != ra1.Token { + t.Fatal("tokens didn't match") + } + } + + if err := (<-ss.OAuth().GetPreviousAccessData(a1.UserId, a1.ClientId)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.OAuth().GetPreviousAccessData("user", "junk")).Err; err != nil { + t.Fatal(err) + } + + // Try to get the Access data using an invalid refresh token + if err := (<-ss.OAuth().GetAccessDataByRefreshToken(a1.Token)).Err; err == nil { + t.Fatal("Should have failed. There is no data with an invalid token") + } + + // Get the Access Data using the refresh token + if result := <-ss.OAuth().GetAccessDataByRefreshToken(a1.RefreshToken); result.Err != nil { + t.Fatal(result.Err) + } else { + ra1 := result.Data.(*model.AccessData) + if a1.RefreshToken != ra1.RefreshToken { + t.Fatal("tokens didn't match") + } + } +} + +func testOAuthStoreRemoveAccessData(t *testing.T, ss store.Store) { + a1 := model.AccessData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Token = model.NewId() + a1.RefreshToken = model.NewId() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAccessData(&a1)) + + if err := (<-ss.OAuth().RemoveAccessData(a1.Token)).Err; err != nil { + t.Fatal(err) + } + + if result := (<-ss.OAuth().GetPreviousAccessData(a1.UserId, a1.ClientId)); result.Err != nil { + } else { + if result.Data != nil { + t.Fatal("did not delete access token") + } + } +} + +func testOAuthStoreSaveAuthData(t *testing.T, ss store.Store) { + a1 := model.AuthData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Code = model.NewId() + a1.RedirectUri = "http://example.com" + if err := (<-ss.OAuth().SaveAuthData(&a1)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthStoreGetAuthData(t *testing.T, ss store.Store) { + a1 := model.AuthData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Code = model.NewId() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAuthData(&a1)) + + if err := (<-ss.OAuth().GetAuthData(a1.Code)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthStoreRemoveAuthData(t *testing.T, ss store.Store) { + a1 := model.AuthData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Code = model.NewId() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAuthData(&a1)) + + if err := (<-ss.OAuth().RemoveAuthData(a1.Code)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.OAuth().GetAuthData(a1.Code)).Err; err == nil { + t.Fatal("should have errored - auth code removed") + } +} + +func testOAuthStoreRemoveAuthDataByUser(t *testing.T, ss store.Store) { + a1 := model.AuthData{} + a1.ClientId = model.NewId() + a1.UserId = model.NewId() + a1.Code = model.NewId() + a1.RedirectUri = "http://example.com" + store.Must(ss.OAuth().SaveAuthData(&a1)) + + if err := (<-ss.OAuth().PermanentDeleteAuthDataByUser(a1.UserId)).Err; err != nil { + t.Fatal(err) + } +} + +func testOAuthGetAuthorizedApps(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.Name = "TestApp" + model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + store.Must(ss.OAuth().SaveApp(&a1)) + + // Lets try and get an Authorized app for a user who hasn't authorized it + if result := <-ss.OAuth().GetAuthorizedApps("fake0123456789abcderfgret1", 0, 1000); result.Err == nil { + if len(result.Data.([]*model.OAuthApp)) > 0 { + t.Fatal("Should have failed. Fake user hasn't authorized the app") + } + } else { + t.Fatal(result.Err) + } + + // allow the app + p := model.Preference{} + p.UserId = a1.CreatorId + p.Category = model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP + p.Name = a1.Id + p.Value = "true" + store.Must(ss.Preference().Save(&model.Preferences{p})) + + if result := <-ss.OAuth().GetAuthorizedApps(a1.CreatorId, 0, 1000); result.Err != nil { + t.Fatal(result.Err) + } else { + apps := result.Data.([]*model.OAuthApp) + if len(apps) == 0 { + t.Fatal("It should have return apps") + } + } +} + +func testOAuthGetAccessDataByUserForApp(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.Name = "TestApp" + model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + store.Must(ss.OAuth().SaveApp(&a1)) + + // allow the app + p := model.Preference{} + p.UserId = a1.CreatorId + p.Category = model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP + p.Name = a1.Id + p.Value = "true" + store.Must(ss.Preference().Save(&model.Preferences{p})) + + if result := <-ss.OAuth().GetAuthorizedApps(a1.CreatorId, 0, 1000); result.Err != nil { + t.Fatal(result.Err) + } else { + apps := result.Data.([]*model.OAuthApp) + if len(apps) == 0 { + t.Fatal("It should have return apps") + } + } + + // save the token + ad1 := model.AccessData{} + ad1.ClientId = a1.Id + ad1.UserId = a1.CreatorId + ad1.Token = model.NewId() + ad1.RefreshToken = model.NewId() + ad1.RedirectUri = "http://example.com" + + if err := (<-ss.OAuth().SaveAccessData(&ad1)).Err; err != nil { + t.Fatal(err) + } + + if result := <-ss.OAuth().GetAccessDataByUserForApp(a1.CreatorId, a1.Id); result.Err != nil { + t.Fatal(result.Err) + } else { + accessData := result.Data.([]*model.AccessData) + if len(accessData) == 0 { + t.Fatal("It should have return access data") + } + } +} + +func testOAuthStoreDeleteApp(t *testing.T, ss store.Store) { + a1 := model.OAuthApp{} + a1.CreatorId = model.NewId() + a1.Name = "TestApp" + model.NewId() + a1.CallbackUrls = []string{"https://nowhere.com"} + a1.Homepage = "https://nowhere.com" + store.Must(ss.OAuth().SaveApp(&a1)) + + // delete a non-existent app + if err := (<-ss.OAuth().DeleteApp("fakeclientId")).Err; err != nil { + t.Fatal(err) + } + + s1 := model.Session{} + s1.UserId = model.NewId() + s1.Token = model.NewId() + s1.IsOAuth = true + + store.Must(ss.Session().Save(&s1)) + + ad1 := model.AccessData{} + ad1.ClientId = a1.Id + ad1.UserId = a1.CreatorId + ad1.Token = s1.Token + ad1.RefreshToken = model.NewId() + ad1.RedirectUri = "http://example.com" + + store.Must(ss.OAuth().SaveAccessData(&ad1)) + + if err := (<-ss.OAuth().DeleteApp(a1.Id)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.Session().Get(s1.Token)).Err; err == nil { + t.Fatal("should error - session should be deleted") + } + + if err := (<-ss.OAuth().GetAccessData(s1.Token)).Err; err == nil { + t.Fatal("should error - access data should be deleted") + } +} diff --git a/store/storetest/post_store.go b/store/storetest/post_store.go new file mode 100644 index 000000000..f721bed64 --- /dev/null +++ b/store/storetest/post_store.go @@ -0,0 +1,1684 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/utils" +) + +func TestPostStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testPostStoreSave(t, ss) }) + t.Run("Get", func(t *testing.T) { testPostStoreGet(t, ss) }) + t.Run("GetSingle", func(t *testing.T) { testPostStoreGetSingle(t, ss) }) + t.Run("GetEtagCache", func(t *testing.T) { testGetEtagCache(t, ss) }) + t.Run("Update", func(t *testing.T) { testPostStoreUpdate(t, ss) }) + t.Run("Delete", func(t *testing.T) { testPostStoreDelete(t, ss) }) + t.Run("Delete1Level", func(t *testing.T) { testPostStoreDelete1Level(t, ss) }) + t.Run("Delete2Level", func(t *testing.T) { testPostStoreDelete2Level(t, ss) }) + t.Run("PermDelete1Level", func(t *testing.T) { testPostStorePermDelete1Level(t, ss) }) + t.Run("PermDelete1Level2", func(t *testing.T) { testPostStorePermDelete1Level2(t, ss) }) + t.Run("GetWithChildren", func(t *testing.T) { testPostStoreGetWithChildren(t, ss) }) + t.Run("GetPostsWtihDetails", func(t *testing.T) { testPostStoreGetPostsWtihDetails(t, ss) }) + t.Run("GetPostsBeforeAfter", func(t *testing.T) { testPostStoreGetPostsBeforeAfter(t, ss) }) + t.Run("GetPostsSince", func(t *testing.T) { testPostStoreGetPostsSince(t, ss) }) + t.Run("Search", func(t *testing.T) { testPostStoreSearch(t, ss) }) + t.Run("UserCountsWithPostsByDay", func(t *testing.T) { testUserCountsWithPostsByDay(t, ss) }) + t.Run("PostCountsByDay", func(t *testing.T) { testPostCountsByDay(t, ss) }) + t.Run("GetFlaggedPostsForTeam", func(t *testing.T) { testPostStoreGetFlaggedPostsForTeam(t, ss) }) + t.Run("GetFlaggedPosts", func(t *testing.T) { testPostStoreGetFlaggedPosts(t, ss) }) + t.Run("GetFlaggedPostsForChannel", func(t *testing.T) { testPostStoreGetFlaggedPostsForChannel(t, ss) }) + t.Run("GetPostsCreatedAt", func(t *testing.T) { testPostStoreGetPostsCreatedAt(t, ss) }) + t.Run("Overwrite", func(t *testing.T) { testPostStoreOverwrite(t, ss) }) + t.Run("GetPostsByIds", func(t *testing.T) { testPostStoreGetPostsByIds(t, ss) }) + t.Run("GetPostsBatchForIndexing", func(t *testing.T) { testPostStoreGetPostsBatchForIndexing(t, ss) }) + t.Run("PermanentDeleteBatch", func(t *testing.T) { testPostStorePermanentDeleteBatch(t, ss) }) +} + +func testPostStoreSave(t *testing.T, ss store.Store) { + o1 := model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + + if err := (<-ss.Post().Save(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Post().Save(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func testPostStoreGet(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + + etag1 := (<-ss.Post().GetEtag(o1.ChannelId, false)).Data.(string) + if strings.Index(etag1, model.CurrentVersion+".") != 0 { + t.Fatal("Invalid Etag") + } + + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + etag2 := (<-ss.Post().GetEtag(o1.ChannelId, false)).Data.(string) + if strings.Index(etag2, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)) != 0 { + t.Fatal("Invalid Etag") + } + + if r1 := <-ss.Post().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.PostList).Posts[o1.Id].CreateAt != o1.CreateAt { + t.Fatal("invalid returned post") + } + } + + if err := (<-ss.Post().Get("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + if err := (<-ss.Post().Get("")).Err; err == nil { + t.Fatal("should fail for blank post ids") + } +} + +func testPostStoreGetSingle(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + if r1 := <-ss.Post().GetSingle(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Post).CreateAt != o1.CreateAt { + t.Fatal("invalid returned post") + } + } + + if err := (<-ss.Post().GetSingle("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testGetEtagCache(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + + etag1 := (<-ss.Post().GetEtag(o1.ChannelId, true)).Data.(string) + if strings.Index(etag1, model.CurrentVersion+".") != 0 { + t.Fatal("Invalid Etag") + } + + // This one should come from the cache + etag2 := (<-ss.Post().GetEtag(o1.ChannelId, true)).Data.(string) + if strings.Index(etag2, model.CurrentVersion+".") != 0 { + t.Fatal("Invalid Etag") + } + + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + // We have not invalidated the cache so this should be the same as above + etag3 := (<-ss.Post().GetEtag(o1.ChannelId, true)).Data.(string) + if strings.Index(etag3, etag2) != 0 { + t.Fatal("Invalid Etag") + } + + ss.Post().InvalidateLastPostTimeCache(o1.ChannelId) + + // Invalidated cache so we should get a good result + etag4 := (<-ss.Post().GetEtag(o1.ChannelId, true)).Data.(string) + if strings.Index(etag4, fmt.Sprintf("%v.%v", model.CurrentVersion, o1.UpdateAt)) != 0 { + t.Fatal("Invalid Etag") + } +} + +func testPostStoreUpdate(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "CCCCCCCCC" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + ro1 := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id] + ro2 := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id] + ro3 := (<-ss.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id] + + if ro1.Message != o1.Message { + t.Fatal("Failed to save/get") + } + + o1a := &model.Post{} + *o1a = *ro1 + o1a.Message = ro1.Message + "BBBBBBBBBB" + if result := <-ss.Post().Update(o1a, ro1); result.Err != nil { + t.Fatal(result.Err) + } + + ro1a := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id] + + if ro1a.Message != o1a.Message { + t.Fatal("Failed to update/get") + } + + o2a := &model.Post{} + *o2a = *ro2 + o2a.Message = ro2.Message + "DDDDDDD" + if result := <-ss.Post().Update(o2a, ro2); result.Err != nil { + t.Fatal(result.Err) + } + + ro2a := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id] + + if ro2a.Message != o2a.Message { + t.Fatal("Failed to update/get") + } + + o3a := &model.Post{} + *o3a = *ro3 + o3a.Message = ro3.Message + "WWWWWWW" + if result := <-ss.Post().Update(o3a, ro3); result.Err != nil { + t.Fatal(result.Err) + } + + ro3a := (<-ss.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id] + + if ro3a.Message != o3a.Message && ro3a.Hashtags != o3a.Hashtags { + t.Fatal("Failed to update/get") + } + + o4 := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + Message: model.NewId(), + Filenames: []string{"test"}, + })).(*model.Post) + + ro4 := (<-ss.Post().Get(o4.Id)).Data.(*model.PostList).Posts[o4.Id] + + o4a := &model.Post{} + *o4a = *ro4 + o4a.Filenames = []string{} + o4a.FileIds = []string{model.NewId()} + if result := <-ss.Post().Update(o4a, ro4); result.Err != nil { + t.Fatal(result.Err) + } + + if ro4a := store.Must(ss.Post().Get(o4.Id)).(*model.PostList).Posts[o4.Id]; len(ro4a.Filenames) != 0 { + t.Fatal("Failed to clear Filenames") + } else if len(ro4a.FileIds) != 1 { + t.Fatal("Failed to set FileIds") + } +} + +func testPostStoreDelete(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + + etag1 := (<-ss.Post().GetEtag(o1.ChannelId, false)).Data.(string) + if strings.Index(etag1, model.CurrentVersion+".") != 0 { + t.Fatal("Invalid Etag") + } + + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + if r1 := <-ss.Post().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.PostList).Posts[o1.Id].CreateAt != o1.CreateAt { + t.Fatal("invalid returned post") + } + } + + if r2 := <-ss.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } + + etag2 := (<-ss.Post().GetEtag(o1.ChannelId, false)).Data.(string) + if strings.Index(etag2, model.CurrentVersion+".") != 0 { + t.Fatal("Invalid Etag") + } +} + +func testPostStoreDelete1Level(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + if r2 := <-ss.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o1.Id)); r3.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r4 := (<-ss.Post().Get(o2.Id)); r4.Err == nil { + t.Fatal("Deleted id should have failed") + } +} + +func testPostStoreDelete2Level(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.ParentId = o2.Id + o3.RootId = o1.Id + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + o4 := &model.Post{} + o4.ChannelId = model.NewId() + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + + if r2 := <-ss.Post().Delete(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o1.Id)); r3.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r4 := (<-ss.Post().Get(o2.Id)); r4.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r5 := (<-ss.Post().Get(o3.Id)); r5.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r6 := <-ss.Post().Get(o4.Id); r6.Err != nil { + t.Fatal(r6.Err) + } +} + +func testPostStorePermDelete1Level(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = model.NewId() + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + if r2 := <-ss.Post().PermanentDeleteByUser(o2.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o1.Id)); r3.Err != nil { + t.Fatal("Deleted id shouldn't have failed") + } + + if r4 := (<-ss.Post().Get(o2.Id)); r4.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r2 := <-ss.Post().PermanentDeleteByChannel(o3.ChannelId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o3.Id)); r3.Err == nil { + t.Fatal("Deleted id should have failed") + } +} + +func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = model.NewId() + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + if r2 := <-ss.Post().PermanentDeleteByUser(o1.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Post().Get(o1.Id)); r3.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r4 := (<-ss.Post().Get(o2.Id)); r4.Err == nil { + t.Fatal("Deleted id should have failed") + } + + if r5 := (<-ss.Post().Get(o3.Id)); r5.Err != nil { + t.Fatal("Deleted id shouldn't have failed") + } +} + +func testPostStoreGetWithChildren(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.ParentId = o2.Id + o3.RootId = o1.Id + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + if r1 := <-ss.Post().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + pl := r1.Data.(*model.PostList) + if len(pl.Posts) != 3 { + t.Fatal("invalid returned post") + } + } + + store.Must(ss.Post().Delete(o3.Id, model.GetMillis())) + + if r2 := <-ss.Post().Get(o1.Id); r2.Err != nil { + t.Fatal(r2.Err) + } else { + pl := r2.Data.(*model.PostList) + if len(pl.Posts) != 2 { + t.Fatal("invalid returned post") + } + } + + store.Must(ss.Post().Delete(o2.Id, model.GetMillis())) + + if r3 := <-ss.Post().Get(o1.Id); r3.Err != nil { + t.Fatal(r3.Err) + } else { + pl := r3.Data.(*model.PostList) + if len(pl.Posts) != 1 { + t.Fatal("invalid returned post") + } + } +} + +func testPostStoreGetPostsWtihDetails(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2a := &model.Post{} + o2a.ChannelId = o1.ChannelId + o2a.UserId = model.NewId() + o2a.Message = "zz" + model.NewId() + "b" + o2a.ParentId = o1.Id + o2a.RootId = o1.Id + o2a = (<-ss.Post().Save(o2a)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.ParentId = o1.Id + o3.RootId = o1.Id + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o4 := &model.Post{} + o4.ChannelId = o1.ChannelId + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o5 := &model.Post{} + o5.ChannelId = o1.ChannelId + o5.UserId = model.NewId() + o5.Message = "zz" + model.NewId() + "b" + o5.ParentId = o4.Id + o5.RootId = o4.Id + o5 = (<-ss.Post().Save(o5)).Data.(*model.Post) + + r1 := (<-ss.Post().GetPosts(o1.ChannelId, 0, 4, false)).Data.(*model.PostList) + + if r1.Order[0] != o5.Id { + t.Fatal("invalid order") + } + + if r1.Order[1] != o4.Id { + t.Fatal("invalid order") + } + + if r1.Order[2] != o3.Id { + t.Fatal("invalid order") + } + + if r1.Order[3] != o2a.Id { + t.Fatal("invalid order") + } + + if len(r1.Posts) != 6 { //the last 4, + o1 (o2a and o3's parent) + o2 (in same thread as o2a and o3) + t.Fatal("wrong size") + } + + if r1.Posts[o1.Id].Message != o1.Message { + t.Fatal("Missing parent") + } + + r2 := (<-ss.Post().GetPosts(o1.ChannelId, 0, 4, true)).Data.(*model.PostList) + + if r2.Order[0] != o5.Id { + t.Fatal("invalid order") + } + + if r2.Order[1] != o4.Id { + t.Fatal("invalid order") + } + + if r2.Order[2] != o3.Id { + t.Fatal("invalid order") + } + + if r2.Order[3] != o2a.Id { + t.Fatal("invalid order") + } + + if len(r2.Posts) != 6 { //the last 4, + o1 (o2a and o3's parent) + o2 (in same thread as o2a and o3) + t.Fatal("wrong size") + } + + if r2.Posts[o1.Id].Message != o1.Message { + t.Fatal("Missing parent") + } +} + +func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { + o0 := &model.Post{} + o0.ChannelId = model.NewId() + o0.UserId = model.NewId() + o0.Message = "zz" + model.NewId() + "b" + o0 = (<-ss.Post().Save(o0)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2a := &model.Post{} + o2a.ChannelId = o1.ChannelId + o2a.UserId = model.NewId() + o2a.Message = "zz" + model.NewId() + "b" + o2a.ParentId = o1.Id + o2a.RootId = o1.Id + o2a = (<-ss.Post().Save(o2a)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.ParentId = o1.Id + o3.RootId = o1.Id + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o4 := &model.Post{} + o4.ChannelId = o1.ChannelId + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o5 := &model.Post{} + o5.ChannelId = o1.ChannelId + o5.UserId = model.NewId() + o5.Message = "zz" + model.NewId() + "b" + o5.ParentId = o4.Id + o5.RootId = o4.Id + o5 = (<-ss.Post().Save(o5)).Data.(*model.Post) + + r1 := (<-ss.Post().GetPostsBefore(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList) + + if len(r1.Posts) != 0 { + t.Fatal("Wrong size") + } + + r2 := (<-ss.Post().GetPostsAfter(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList) + + if r2.Order[0] != o4.Id { + t.Fatal("invalid order") + } + + if r2.Order[1] != o3.Id { + t.Fatal("invalid order") + } + + if r2.Order[2] != o2a.Id { + t.Fatal("invalid order") + } + + if r2.Order[3] != o2.Id { + t.Fatal("invalid order") + } + + if len(r2.Posts) != 5 { + t.Fatal("wrong size") + } + + r3 := (<-ss.Post().GetPostsBefore(o3.ChannelId, o3.Id, 2, 0)).Data.(*model.PostList) + + if r3.Order[0] != o2a.Id { + t.Fatal("invalid order") + } + + if r3.Order[1] != o2.Id { + t.Fatal("invalid order") + } + + if len(r3.Posts) != 3 { + t.Fatal("wrong size") + } + + if r3.Posts[o1.Id].Message != o1.Message { + t.Fatal("Missing parent") + } +} + +func testPostStoreGetPostsSince(t *testing.T, ss store.Store) { + o0 := &model.Post{} + o0.ChannelId = model.NewId() + o0.UserId = model.NewId() + o0.Message = "zz" + model.NewId() + "b" + o0 = (<-ss.Post().Save(o0)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2a := &model.Post{} + o2a.ChannelId = o1.ChannelId + o2a.UserId = model.NewId() + o2a.Message = "zz" + model.NewId() + "b" + o2a.ParentId = o1.Id + o2a.RootId = o1.Id + o2a = (<-ss.Post().Save(o2a)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.ParentId = o1.Id + o3.RootId = o1.Id + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o4 := &model.Post{} + o4.ChannelId = o1.ChannelId + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o5 := &model.Post{} + o5.ChannelId = o1.ChannelId + o5.UserId = model.NewId() + o5.Message = "zz" + model.NewId() + "b" + o5.ParentId = o4.Id + o5.RootId = o4.Id + o5 = (<-ss.Post().Save(o5)).Data.(*model.Post) + + r1 := (<-ss.Post().GetPostsSince(o1.ChannelId, o1.CreateAt, false)).Data.(*model.PostList) + + if r1.Order[0] != o5.Id { + t.Fatal("invalid order") + } + + if r1.Order[1] != o4.Id { + t.Fatal("invalid order") + } + + if r1.Order[2] != o3.Id { + t.Fatal("invalid order") + } + + if r1.Order[3] != o2a.Id { + t.Fatal("invalid order") + } + + if len(r1.Posts) != 6 { + t.Fatal("wrong size") + } + + if r1.Posts[o1.Id].Message != o1.Message { + t.Fatal("Missing parent") + } + + r2 := (<-ss.Post().GetPostsSince(o1.ChannelId, o5.UpdateAt, true)).Data.(*model.PostList) + + if len(r2.Order) != 0 { + t.Fatal("wrong size ", len(r2.Posts)) + } +} + +func testPostStoreSearch(t *testing.T, ss store.Store) { + teamId := model.NewId() + userId := model.NewId() + + c1 := &model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Channel1" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = (<-ss.Channel().Save(c1)).Data.(*model.Channel) + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = userId + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + store.Must(ss.Channel().SaveMember(&m1)) + + c2 := &model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Channel1" + c2.Name = "zz" + model.NewId() + "b" + c2.Type = model.CHANNEL_OPEN + c2 = (<-ss.Channel().Save(c2)).Data.(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = model.NewId() + o1.Message = "corey mattermost new york" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = model.NewId() + o1a.Message = "corey mattermost new york" + o1a.Type = model.POST_JOIN_CHANNEL + o1a = (<-ss.Post().Save(o1a)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = model.NewId() + o2.Message = "New Jersey is where John is from" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = c2.Id + o3.UserId = model.NewId() + o3.Message = "New Jersey is where John is from corey new york" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + o4 := &model.Post{} + o4.ChannelId = c1.Id + o4.UserId = model.NewId() + o4.Hashtags = "#hashtag" + o4.Message = "(message)blargh" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + + o5 := &model.Post{} + o5.ChannelId = c1.Id + o5.UserId = model.NewId() + o5.Hashtags = "#secret #howdy" + o5 = (<-ss.Post().Save(o5)).Data.(*model.Post) + + r1 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "corey", IsHashtag: false})).Data.(*model.PostList) + if len(r1.Order) != 1 || r1.Order[0] != o1.Id { + t.Fatal("returned wrong search result") + } + + r3 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "new", IsHashtag: false})).Data.(*model.PostList) + if len(r3.Order) != 2 || (r3.Order[0] != o1.Id && r3.Order[1] != o1.Id) { + t.Fatal("returned wrong search result") + } + + r4 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "john", IsHashtag: false})).Data.(*model.PostList) + if len(r4.Order) != 1 || r4.Order[0] != o2.Id { + t.Fatal("returned wrong search result") + } + + r5 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "matter*", IsHashtag: false})).Data.(*model.PostList) + if len(r5.Order) != 1 || r5.Order[0] != o1.Id { + t.Fatal("returned wrong search result") + } + + r6 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "#hashtag", IsHashtag: true})).Data.(*model.PostList) + if len(r6.Order) != 1 || r6.Order[0] != o4.Id { + t.Fatal("returned wrong search result") + } + + r7 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "#secret", IsHashtag: true})).Data.(*model.PostList) + if len(r7.Order) != 1 || r7.Order[0] != o5.Id { + t.Fatal("returned wrong search result") + } + + r8 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "@thisshouldmatchnothing", IsHashtag: true})).Data.(*model.PostList) + if len(r8.Order) != 0 { + t.Fatal("returned wrong search result") + } + + r9 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "mattermost jersey", IsHashtag: false})).Data.(*model.PostList) + if len(r9.Order) != 0 { + t.Fatal("returned wrong search result") + } + + r9a := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "corey new york", IsHashtag: false})).Data.(*model.PostList) + if len(r9a.Order) != 1 { + t.Fatal("returned wrong search result") + } + + r10 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "matter* jer*", IsHashtag: false})).Data.(*model.PostList) + if len(r10.Order) != 0 { + t.Fatal("returned wrong search result") + } + + r11 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "message blargh", IsHashtag: false})).Data.(*model.PostList) + if len(r11.Order) != 1 { + t.Fatal("returned wrong search result") + } + + r12 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "blargh>", IsHashtag: false})).Data.(*model.PostList) + if len(r12.Order) != 1 { + t.Fatal("returned wrong search result") + } + + r13 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "Jersey corey", IsHashtag: false, OrTerms: true})).Data.(*model.PostList) + if len(r13.Order) != 2 { + t.Fatal("returned wrong search result") + } +} + +func testUserCountsWithPostsByDay(t *testing.T, ss store.Store) { + t1 := &model.Team{} + t1.DisplayName = "DisplayName" + t1.Name = "zz" + model.NewId() + "b" + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + t1 = store.Must(ss.Team().Save(t1)).(*model.Team) + + c1 := &model.Channel{} + c1.TeamId = t1.Id + c1.DisplayName = "Channel2" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = store.Must(ss.Channel().Save(c1)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = model.NewId() + o1.CreateAt = utils.MillisFromTime(utils.Yesterday()) + o1.Message = "zz" + model.NewId() + "b" + o1 = store.Must(ss.Post().Save(o1)).(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = model.NewId() + o1a.CreateAt = o1.CreateAt + o1a.Message = "zz" + model.NewId() + "b" + o1a = store.Must(ss.Post().Save(o1a)).(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = model.NewId() + o2.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24) + o2.Message = "zz" + model.NewId() + "b" + o2 = store.Must(ss.Post().Save(o2)).(*model.Post) + + o2a := &model.Post{} + o2a.ChannelId = c1.Id + o2a.UserId = o2.UserId + o2a.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24) + o2a.Message = "zz" + model.NewId() + "b" + o2a = store.Must(ss.Post().Save(o2a)).(*model.Post) + + if r1 := <-ss.Post().AnalyticsUserCountsWithPostsByDay(t1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + row1 := r1.Data.(model.AnalyticsRows)[0] + if row1.Value != 2 { + t.Fatal("wrong value") + } + + row2 := r1.Data.(model.AnalyticsRows)[1] + if row2.Value != 1 { + t.Fatal("wrong value") + } + } +} + +func testPostCountsByDay(t *testing.T, ss store.Store) { + t1 := &model.Team{} + t1.DisplayName = "DisplayName" + t1.Name = "zz" + model.NewId() + "b" + t1.Email = model.NewId() + "@nowhere.com" + t1.Type = model.TEAM_OPEN + t1 = store.Must(ss.Team().Save(t1)).(*model.Team) + + c1 := &model.Channel{} + c1.TeamId = t1.Id + c1.DisplayName = "Channel2" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = store.Must(ss.Channel().Save(c1)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = model.NewId() + o1.CreateAt = utils.MillisFromTime(utils.Yesterday()) + o1.Message = "zz" + model.NewId() + "b" + o1 = store.Must(ss.Post().Save(o1)).(*model.Post) + + o1a := &model.Post{} + o1a.ChannelId = c1.Id + o1a.UserId = model.NewId() + o1a.CreateAt = o1.CreateAt + o1a.Message = "zz" + model.NewId() + "b" + o1a = store.Must(ss.Post().Save(o1a)).(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c1.Id + o2.UserId = model.NewId() + o2.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24 * 2) + o2.Message = "zz" + model.NewId() + "b" + o2 = store.Must(ss.Post().Save(o2)).(*model.Post) + + o2a := &model.Post{} + o2a.ChannelId = c1.Id + o2a.UserId = o2.UserId + o2a.CreateAt = o1.CreateAt - (1000 * 60 * 60 * 24 * 2) + o2a.Message = "zz" + model.NewId() + "b" + o2a = store.Must(ss.Post().Save(o2a)).(*model.Post) + + time.Sleep(1 * time.Second) + + if r1 := <-ss.Post().AnalyticsPostCountsByDay(t1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + row1 := r1.Data.(model.AnalyticsRows)[0] + if row1.Value != 2 { + t.Fatal(row1) + } + + row2 := r1.Data.(model.AnalyticsRows)[1] + if row2.Value != 2 { + t.Fatal("wrong value") + } + } + + if r1 := <-ss.Post().AnalyticsPostCount(t1.Id, false, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) != 4 { + t.Fatal("wrong value") + } + } +} + +func testPostStoreGetFlaggedPostsForTeam(t *testing.T, ss store.Store) { + c1 := &model.Channel{} + c1.TeamId = model.NewId() + c1.DisplayName = "Channel1" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = store.Must(ss.Channel().Save(c1)).(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.DeleteAt = 1 + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o4 := &model.Post{} + o4.ChannelId = model.NewId() + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + c2 := &model.Channel{} + c2.DisplayName = "DMChannel1" + c2.Name = "zz" + model.NewId() + "b" + c2.Type = model.CHANNEL_DIRECT + + m1 := &model.ChannelMember{} + m1.ChannelId = c2.Id + m1.UserId = o1.UserId + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := &model.ChannelMember{} + m2.ChannelId = c2.Id + m2.UserId = model.NewId() + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + c2 = store.Must(ss.Channel().SaveDirectChannel(c2, m1, m2)).(*model.Channel) + + o5 := &model.Post{} + o5.ChannelId = c2.Id + o5.UserId = m2.UserId + o5.Message = "zz" + model.NewId() + "b" + o5 = (<-ss.Post().Save(o5)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + r1 := (<-ss.Post().GetFlaggedPosts(o1.ChannelId, 0, 2)).Data.(*model.PostList) + + if len(r1.Order) != 0 { + t.Fatal("should be empty") + } + + preferences := model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o1.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r2 := (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)).Data.(*model.PostList) + + if len(r2.Order) != 1 { + t.Fatal("should have 1 post") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o2.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r3 := (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 1)).Data.(*model.PostList) + + if len(r3.Order) != 1 { + t.Fatal("should have 1 post") + } + + r3 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 1, 1)).Data.(*model.PostList) + + if len(r3.Order) != 1 { + t.Fatal("should have 1 post") + } + + r3 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 1000, 10)).Data.(*model.PostList) + + if len(r3.Order) != 0 { + t.Fatal("should be empty") + } + + r4 := (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 2 { + t.Fatal("should have 2 posts") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o3.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r4 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 2 { + t.Fatal("should have 2 posts") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o4.Id, + Value: "true", + }, + } + store.Must(ss.Preference().Save(&preferences)) + + r4 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 2 { + t.Fatal("should have 2 posts") + } + + r4 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, model.NewId(), 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 0 { + t.Fatal("should have 0 posts") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o5.Id, + Value: "true", + }, + } + store.Must(ss.Preference().Save(&preferences)) + + r4 = (<-ss.Post().GetFlaggedPostsForTeam(o1.UserId, c1.TeamId, 0, 10)).Data.(*model.PostList) + + if len(r4.Order) != 3 { + t.Fatal("should have 3 posts") + } +} + +func testPostStoreGetFlaggedPosts(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.DeleteAt = 1 + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + r1 := (<-ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)).Data.(*model.PostList) + + if len(r1.Order) != 0 { + t.Fatal("should be empty") + } + + preferences := model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o1.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r2 := (<-ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)).Data.(*model.PostList) + + if len(r2.Order) != 1 { + t.Fatal("should have 1 post") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o2.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r3 := (<-ss.Post().GetFlaggedPosts(o1.UserId, 0, 1)).Data.(*model.PostList) + + if len(r3.Order) != 1 { + t.Fatal("should have 1 post") + } + + r3 = (<-ss.Post().GetFlaggedPosts(o1.UserId, 1, 1)).Data.(*model.PostList) + + if len(r3.Order) != 1 { + t.Fatal("should have 1 post") + } + + r3 = (<-ss.Post().GetFlaggedPosts(o1.UserId, 1000, 10)).Data.(*model.PostList) + + if len(r3.Order) != 0 { + t.Fatal("should be empty") + } + + r4 := (<-ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 2 { + t.Fatal("should have 2 posts") + } + + preferences = model.Preferences{ + { + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o3.Id, + Value: "true", + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + r4 = (<-ss.Post().GetFlaggedPosts(o1.UserId, 0, 2)).Data.(*model.PostList) + + if len(r4.Order) != 2 { + t.Fatal("should have 2 posts") + } +} + +func testPostStoreGetFlaggedPostsForChannel(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + // deleted post + o3 := &model.Post{} + o3.ChannelId = model.NewId() + o3.UserId = o1.ChannelId + o3.Message = "zz" + model.NewId() + "b" + o3.DeleteAt = 1 + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o4 := &model.Post{} + o4.ChannelId = model.NewId() + o4.UserId = model.NewId() + o4.Message = "zz" + model.NewId() + "b" + o4 = (<-ss.Post().Save(o4)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + r := (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)).Data.(*model.PostList) + + if len(r.Order) != 0 { + t.Fatal("should be empty") + } + + preference := model.Preference{ + UserId: o1.UserId, + Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, + Name: o1.Id, + Value: "true", + } + + store.Must(ss.Preference().Save(&model.Preferences{preference})) + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)).Data.(*model.PostList) + + if len(r.Order) != 1 { + t.Fatal("should have 1 post") + } + + preference.Name = o2.Id + store.Must(ss.Preference().Save(&model.Preferences{preference})) + + preference.Name = o3.Id + store.Must(ss.Preference().Save(&model.Preferences{preference})) + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 1)).Data.(*model.PostList) + + if len(r.Order) != 1 { + t.Fatal("should have 1 post") + } + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 1, 1)).Data.(*model.PostList) + + if len(r.Order) != 1 { + t.Fatal("should have 1 post") + } + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 1000, 10)).Data.(*model.PostList) + + if len(r.Order) != 0 { + t.Fatal("should be empty") + } + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o1.ChannelId, 0, 10)).Data.(*model.PostList) + + if len(r.Order) != 2 { + t.Fatal("should have 2 posts") + } + + preference.Name = o4.Id + store.Must(ss.Preference().Save(&model.Preferences{preference})) + + r = (<-ss.Post().GetFlaggedPostsForChannel(o1.UserId, o4.ChannelId, 0, 10)).Data.(*model.PostList) + + if len(r.Order) != 1 { + t.Fatal("should have 1 post") + } +} + +func testPostStoreGetPostsCreatedAt(t *testing.T, ss store.Store) { + createTime := model.GetMillis() + + o0 := &model.Post{} + o0.ChannelId = model.NewId() + o0.UserId = model.NewId() + o0.Message = "zz" + model.NewId() + "b" + o0.CreateAt = createTime + o0 = (<-ss.Post().Save(o0)).Data.(*model.Post) + + o1 := &model.Post{} + o1.ChannelId = o0.Id + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "b" + o0.CreateAt = createTime + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "b" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + o3 := &model.Post{} + o3.ChannelId = model.NewId() + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "b" + o3.CreateAt = createTime + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + time.Sleep(2 * time.Millisecond) + + r1 := (<-ss.Post().GetPostsCreatedAt(o1.ChannelId, createTime)).Data.([]*model.Post) + + if len(r1) != 2 { + t.Fatalf("Got the wrong number of posts.") + } +} + +func testPostStoreOverwrite(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "CCCCCCCCC" + o2.ParentId = o1.Id + o2.RootId = o1.Id + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + ro1 := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id] + ro2 := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id] + ro3 := (<-ss.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id] + + if ro1.Message != o1.Message { + t.Fatal("Failed to save/get") + } + + o1a := &model.Post{} + *o1a = *ro1 + o1a.Message = ro1.Message + "BBBBBBBBBB" + if result := <-ss.Post().Overwrite(o1a); result.Err != nil { + t.Fatal(result.Err) + } + + ro1a := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id] + + if ro1a.Message != o1a.Message { + t.Fatal("Failed to overwrite/get") + } + + o2a := &model.Post{} + *o2a = *ro2 + o2a.Message = ro2.Message + "DDDDDDD" + if result := <-ss.Post().Overwrite(o2a); result.Err != nil { + t.Fatal(result.Err) + } + + ro2a := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o2.Id] + + if ro2a.Message != o2a.Message { + t.Fatal("Failed to overwrite/get") + } + + o3a := &model.Post{} + *o3a = *ro3 + o3a.Message = ro3.Message + "WWWWWWW" + if result := <-ss.Post().Overwrite(o3a); result.Err != nil { + t.Fatal(result.Err) + } + + ro3a := (<-ss.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id] + + if ro3a.Message != o3a.Message && ro3a.Hashtags != o3a.Hashtags { + t.Fatal("Failed to overwrite/get") + } + + o4 := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + Message: model.NewId(), + Filenames: []string{"test"}, + })).(*model.Post) + + ro4 := (<-ss.Post().Get(o4.Id)).Data.(*model.PostList).Posts[o4.Id] + + o4a := &model.Post{} + *o4a = *ro4 + o4a.Filenames = []string{} + o4a.FileIds = []string{model.NewId()} + if result := <-ss.Post().Overwrite(o4a); result.Err != nil { + t.Fatal(result.Err) + } + + if ro4a := store.Must(ss.Post().Get(o4.Id)).(*model.PostList).Posts[o4.Id]; len(ro4a.Filenames) != 0 { + t.Fatal("Failed to clear Filenames") + } else if len(ro4a.FileIds) != 1 { + t.Fatal("Failed to set FileIds") + } +} + +func testPostStoreGetPostsByIds(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = o1.ChannelId + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "CCCCCCCCC" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = o1.ChannelId + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + ro1 := (<-ss.Post().Get(o1.Id)).Data.(*model.PostList).Posts[o1.Id] + ro2 := (<-ss.Post().Get(o2.Id)).Data.(*model.PostList).Posts[o2.Id] + ro3 := (<-ss.Post().Get(o3.Id)).Data.(*model.PostList).Posts[o3.Id] + + postIds := []string{ + ro1.Id, + ro2.Id, + ro3.Id, + } + + if ro4 := store.Must(ss.Post().GetPostsByIds(postIds)).([]*model.Post); len(ro4) != 3 { + t.Fatalf("Expected 3 posts in results. Got %v", len(ro4)) + } + + store.Must(ss.Post().Delete(ro1.Id, model.GetMillis())) + + if ro5 := store.Must(ss.Post().GetPostsByIds(postIds)).([]*model.Post); len(ro5) != 2 { + t.Fatalf("Expected 2 posts in results. Got %v", len(ro5)) + } +} + +func testPostStoreGetPostsBatchForIndexing(t *testing.T, ss store.Store) { + c1 := &model.Channel{} + c1.TeamId = model.NewId() + c1.DisplayName = "Channel1" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = (<-ss.Channel().Save(c1)).Data.(*model.Channel) + + c2 := &model.Channel{} + c2.TeamId = model.NewId() + c2.DisplayName = "Channel2" + c2.Name = "zz" + model.NewId() + "b" + c2.Type = model.CHANNEL_OPEN + c2 = (<-ss.Channel().Save(c2)).Data.(*model.Channel) + + o1 := &model.Post{} + o1.ChannelId = c1.Id + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = c2.Id + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "CCCCCCCCC" + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = c1.Id + o3.UserId = model.NewId() + o3.ParentId = o1.Id + o3.RootId = o1.Id + o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ" + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + if r := store.Must(ss.Post().GetPostsBatchForIndexing(o1.CreateAt, 100)).([]*model.PostForIndexing); len(r) != 3 { + t.Fatalf("Expected 3 posts in results. Got %v", len(r)) + } else { + for _, p := range r { + if p.Id == o1.Id { + if p.TeamId != c1.TeamId { + t.Fatalf("Unexpected team ID") + } + if p.ParentCreateAt != nil { + t.Fatalf("Unexpected parent create at") + } + } else if p.Id == o2.Id { + if p.TeamId != c2.TeamId { + t.Fatalf("Unexpected team ID") + } + if p.ParentCreateAt != nil { + t.Fatalf("Unexpected parent create at") + } + } else if p.Id == o3.Id { + if p.TeamId != c1.TeamId { + t.Fatalf("Unexpected team ID") + } + if *p.ParentCreateAt != o1.CreateAt { + t.Fatalf("Unexpected parent create at") + } + } else { + t.Fatalf("unexpected post returned") + } + } + } +} + +func testPostStorePermanentDeleteBatch(t *testing.T, ss store.Store) { + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1.CreateAt = 1000 + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + o2 := &model.Post{} + o2.ChannelId = model.NewId() + o2.UserId = model.NewId() + o2.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o2.CreateAt = 1000 + o2 = (<-ss.Post().Save(o2)).Data.(*model.Post) + + o3 := &model.Post{} + o3.ChannelId = model.NewId() + o3.UserId = model.NewId() + o3.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o3.CreateAt = 100000 + o3 = (<-ss.Post().Save(o3)).Data.(*model.Post) + + store.Must(ss.Post().PermanentDeleteBatch(2000, 1000)) + + if p := <-ss.Post().Get(o1.Id); p.Err == nil { + t.Fatalf("Should have not found post 1 after purge") + } + + if p := <-ss.Post().Get(o2.Id); p.Err == nil { + t.Fatalf("Should have not found post 2 after purge") + } + + if p := <-ss.Post().Get(o3.Id); p.Err != nil { + t.Fatalf("Should have found post 3 after purge") + } +} diff --git a/store/storetest/preference_store.go b/store/storetest/preference_store.go new file mode 100644 index 000000000..edd0e9549 --- /dev/null +++ b/store/storetest/preference_store.go @@ -0,0 +1,447 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestPreferenceStore(t *testing.T, ss store.Store) { + t.Run("PreferenceSave", func(t *testing.T) { testPreferenceSave(t, ss) }) + t.Run("PreferenceGet", func(t *testing.T) { testPreferenceGet(t, ss) }) + t.Run("PreferenceGetCategory", func(t *testing.T) { testPreferenceGetCategory(t, ss) }) + t.Run("PreferenceGetAll", func(t *testing.T) { testPreferenceGetAll(t, ss) }) + t.Run("PreferenceDeleteByUser", func(t *testing.T) { testPreferenceDeleteByUser(t, ss) }) + t.Run("IsFeatureEnabled", func(t *testing.T) { testIsFeatureEnabled(t, ss) }) + t.Run("PreferenceDelete", func(t *testing.T) { testPreferenceDelete(t, ss) }) + t.Run("PreferenceDeleteCategory", func(t *testing.T) { testPreferenceDeleteCategory(t, ss) }) + t.Run("PreferenceDeleteCategoryAndName", func(t *testing.T) { testPreferenceDeleteCategoryAndName(t, ss) }) + t.Run("PreferenceCleanupFlagsBatch", func(t *testing.T) { testPreferenceCleanupFlagsBatch(t, ss) }) +} + +func testPreferenceSave(t *testing.T, ss store.Store) { + id := model.NewId() + + preferences := model.Preferences{ + { + UserId: id, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, + Name: model.NewId(), + Value: "value1a", + }, + { + UserId: id, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, + Name: model.NewId(), + Value: "value1b", + }, + } + if count := store.Must(ss.Preference().Save(&preferences)); count != 2 { + t.Fatal("got incorrect number of rows saved") + } + + for _, preference := range preferences { + if data := store.Must(ss.Preference().Get(preference.UserId, preference.Category, preference.Name)).(model.Preference); preference != data { + t.Fatal("got incorrect preference after first Save") + } + } + + preferences[0].Value = "value2a" + preferences[1].Value = "value2b" + if count := store.Must(ss.Preference().Save(&preferences)); count != 2 { + t.Fatal("got incorrect number of rows saved") + } + + for _, preference := range preferences { + if data := store.Must(ss.Preference().Get(preference.UserId, preference.Category, preference.Name)).(model.Preference); preference != data { + t.Fatal("got incorrect preference after second Save") + } + } +} + +func testPreferenceGet(t *testing.T, ss store.Store) { + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + if result := <-ss.Preference().Get(userId, category, name); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(model.Preference); data != preferences[0] { + t.Fatal("got incorrect preference") + } + + // make sure getting a missing preference fails + if result := <-ss.Preference().Get(model.NewId(), model.NewId(), model.NewId()); result.Err == nil { + t.Fatal("no error on getting a missing preference") + } +} + +func testPreferenceGetCategory(t *testing.T, ss store.Store) { + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + // same name/category, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + if result := <-ss.Preference().GetCategory(userId, category); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(model.Preferences); len(data) != 2 { + t.Fatal("got the wrong number of preferences") + } else if !((data[0] == preferences[0] && data[1] == preferences[1]) || (data[0] == preferences[1] && data[1] == preferences[0])) { + t.Fatal("got incorrect preferences") + } + + // make sure getting a missing preference category doesn't fail + if result := <-ss.Preference().GetCategory(model.NewId(), model.NewId()); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(model.Preferences); len(data) != 0 { + t.Fatal("shouldn't have got any preferences") + } +} + +func testPreferenceGetAll(t *testing.T, ss store.Store) { + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + // same name/category, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + if result := <-ss.Preference().GetAll(userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(model.Preferences); len(data) != 3 { + t.Fatal("got the wrong number of preferences") + } else { + for i := 0; i < 3; i++ { + if data[0] != preferences[i] && data[1] != preferences[i] && data[2] != preferences[i] { + t.Fatal("got incorrect preferences") + } + } + } +} + +func testPreferenceDeleteByUser(t *testing.T, ss store.Store) { + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + // same name/category, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + store.Must(ss.Preference().Save(&preferences)) + + if result := <-ss.Preference().PermanentDeleteByUser(userId); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testIsFeatureEnabled(t *testing.T, ss store.Store) { + feature1 := "testFeat1" + feature2 := "testFeat2" + feature3 := "testFeat3" + + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS + + features := model.Preferences{ + { + UserId: userId, + Category: category, + Name: store.FEATURE_TOGGLE_PREFIX + feature1, + Value: "true", + }, + { + UserId: userId, + Category: category, + Name: model.NewId(), + Value: "false", + }, + { + UserId: userId, + Category: model.NewId(), + Name: store.FEATURE_TOGGLE_PREFIX + feature1, + Value: "false", + }, + { + UserId: model.NewId(), + Category: category, + Name: store.FEATURE_TOGGLE_PREFIX + feature2, + Value: "false", + }, + { + UserId: model.NewId(), + Category: category, + Name: store.FEATURE_TOGGLE_PREFIX + feature3, + Value: "foobar", + }, + } + + store.Must(ss.Preference().Save(&features)) + + if result := <-ss.Preference().IsFeatureEnabled(feature1, userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(bool); data != true { + t.Fatalf("got incorrect setting for feature1, %v=%v", true, data) + } + + if result := <-ss.Preference().IsFeatureEnabled(feature2, userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(bool); data != false { + t.Fatalf("got incorrect setting for feature2, %v=%v", false, data) + } + + // make sure we get false if something different than "true" or "false" has been saved to database + if result := <-ss.Preference().IsFeatureEnabled(feature3, userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(bool); data != false { + t.Fatalf("got incorrect setting for feature3, %v=%v", false, data) + } + + // make sure false is returned if a non-existent feature is queried + if result := <-ss.Preference().IsFeatureEnabled("someOtherFeature", userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(bool); data != false { + t.Fatalf("got incorrect setting for non-existent feature 'someOtherFeature', %v=%v", false, data) + } +} + +func testPreferenceDelete(t *testing.T, ss store.Store) { + preference := model.Preference{ + UserId: model.NewId(), + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, + Name: model.NewId(), + Value: "value1a", + } + + store.Must(ss.Preference().Save(&model.Preferences{preference})) + + if prefs := store.Must(ss.Preference().GetAll(preference.UserId)).(model.Preferences); len([]model.Preference(prefs)) != 1 { + t.Fatal("should've returned 1 preference") + } + + if result := <-ss.Preference().Delete(preference.UserId, preference.Category, preference.Name); result.Err != nil { + t.Fatal(result.Err) + } + + if prefs := store.Must(ss.Preference().GetAll(preference.UserId)).(model.Preferences); len([]model.Preference(prefs)) != 0 { + t.Fatal("should've returned no preferences") + } +} + +func testPreferenceDeleteCategory(t *testing.T, ss store.Store) { + category := model.NewId() + userId := model.NewId() + + preference1 := model.Preference{ + UserId: userId, + Category: category, + Name: model.NewId(), + Value: "value1a", + } + + preference2 := model.Preference{ + UserId: userId, + Category: category, + Name: model.NewId(), + Value: "value1a", + } + + store.Must(ss.Preference().Save(&model.Preferences{preference1, preference2})) + + if prefs := store.Must(ss.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 2 { + t.Fatal("should've returned 2 preferences") + } + + if result := <-ss.Preference().DeleteCategory(userId, category); result.Err != nil { + t.Fatal(result.Err) + } + + if prefs := store.Must(ss.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 0 { + t.Fatal("should've returned no preferences") + } +} + +func testPreferenceDeleteCategoryAndName(t *testing.T, ss store.Store) { + category := model.NewId() + name := model.NewId() + userId := model.NewId() + userId2 := model.NewId() + + preference1 := model.Preference{ + UserId: userId, + Category: category, + Name: name, + Value: "value1a", + } + + preference2 := model.Preference{ + UserId: userId2, + Category: category, + Name: name, + Value: "value1a", + } + + store.Must(ss.Preference().Save(&model.Preferences{preference1, preference2})) + + if prefs := store.Must(ss.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 1 { + t.Fatal("should've returned 1 preference") + } + + if prefs := store.Must(ss.Preference().GetAll(userId2)).(model.Preferences); len([]model.Preference(prefs)) != 1 { + t.Fatal("should've returned 1 preference") + } + + if result := <-ss.Preference().DeleteCategoryAndName(category, name); result.Err != nil { + t.Fatal(result.Err) + } + + if prefs := store.Must(ss.Preference().GetAll(userId)).(model.Preferences); len([]model.Preference(prefs)) != 0 { + t.Fatal("should've returned no preferences") + } + + if prefs := store.Must(ss.Preference().GetAll(userId2)).(model.Preferences); len([]model.Preference(prefs)) != 0 { + t.Fatal("should've returned no preferences") + } +} + +func testPreferenceCleanupFlagsBatch(t *testing.T, ss store.Store) { + category := model.PREFERENCE_CATEGORY_FLAGGED_POST + userId := model.NewId() + + o1 := &model.Post{} + o1.ChannelId = model.NewId() + o1.UserId = userId + o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA" + o1.CreateAt = 1000 + o1 = (<-ss.Post().Save(o1)).Data.(*model.Post) + + preference1 := model.Preference{ + UserId: userId, + Category: category, + Name: o1.Id, + Value: "true", + } + + preference2 := model.Preference{ + UserId: userId, + Category: category, + Name: model.NewId(), + Value: "true", + } + + store.Must(ss.Preference().Save(&model.Preferences{preference1, preference2})) + + result := <-ss.Preference().CleanupFlagsBatch(10000) + assert.Nil(t, result.Err) + + result = <-ss.Preference().Get(userId, category, preference1.Name) + assert.Nil(t, result.Err) + + result = <-ss.Preference().Get(userId, category, preference2.Name) + assert.NotNil(t, result.Err) +} diff --git a/store/storetest/reaction_store.go b/store/storetest/reaction_store.go new file mode 100644 index 000000000..1b5128426 --- /dev/null +++ b/store/storetest/reaction_store.go @@ -0,0 +1,350 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestReactionStore(t *testing.T, ss store.Store) { + t.Run("ReactionSave", func(t *testing.T) { testReactionSave(t, ss) }) + t.Run("ReactionDelete", func(t *testing.T) { testReactionDelete(t, ss) }) + t.Run("ReactionGetForPost", func(t *testing.T) { testReactionGetForPost(t, ss) }) + t.Run("ReactionDeleteAllWithEmojiName", func(t *testing.T) { testReactionDeleteAllWithEmojiName(t, ss) }) + t.Run("PermanentDeleteBatch", func(t *testing.T) { testReactionStorePermanentDeleteBatch(t, ss) }) +} + +func testReactionSave(t *testing.T, ss store.Store) { + post := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + firstUpdateAt := post.UpdateAt + + reaction1 := &model.Reaction{ + UserId: model.NewId(), + PostId: post.Id, + EmojiName: model.NewId(), + } + if result := <-ss.Reaction().Save(reaction1); result.Err != nil { + t.Fatal(result.Err) + } else if saved := result.Data.(*model.Reaction); saved.UserId != reaction1.UserId || + saved.PostId != reaction1.PostId || saved.EmojiName != reaction1.EmojiName { + t.Fatal("should've saved reaction and returned it") + } + + var secondUpdateAt int64 + if postList := store.Must(ss.Post().Get(reaction1.PostId)).(*model.PostList); !postList.Posts[post.Id].HasReactions { + t.Fatal("should've set HasReactions = true on post") + } else if postList.Posts[post.Id].UpdateAt == firstUpdateAt { + t.Fatal("should've marked post as updated when HasReactions changed") + } else { + secondUpdateAt = postList.Posts[post.Id].UpdateAt + } + + if result := <-ss.Reaction().Save(reaction1); result.Err != nil { + t.Log(result.Err) + t.Fatal("should've allowed saving a duplicate reaction") + } + + // different user + reaction2 := &model.Reaction{ + UserId: model.NewId(), + PostId: reaction1.PostId, + EmojiName: reaction1.EmojiName, + } + if result := <-ss.Reaction().Save(reaction2); result.Err != nil { + t.Fatal(result.Err) + } + + if postList := store.Must(ss.Post().Get(reaction2.PostId)).(*model.PostList); postList.Posts[post.Id].UpdateAt != secondUpdateAt { + t.Fatal("shouldn't mark as updated when HasReactions hasn't changed") + } + + // different post + reaction3 := &model.Reaction{ + UserId: reaction1.UserId, + PostId: model.NewId(), + EmojiName: reaction1.EmojiName, + } + if result := <-ss.Reaction().Save(reaction3); result.Err != nil { + t.Fatal(result.Err) + } + + // different emoji + reaction4 := &model.Reaction{ + UserId: reaction1.UserId, + PostId: reaction1.PostId, + EmojiName: model.NewId(), + } + if result := <-ss.Reaction().Save(reaction4); result.Err != nil { + t.Fatal(result.Err) + } + + // invalid reaction + reaction5 := &model.Reaction{ + UserId: reaction1.UserId, + PostId: reaction1.PostId, + } + if result := <-ss.Reaction().Save(reaction5); result.Err == nil { + t.Fatal("should've failed for invalid reaction") + } +} + +func testReactionDelete(t *testing.T, ss store.Store) { + post := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + + reaction := &model.Reaction{ + UserId: model.NewId(), + PostId: post.Id, + EmojiName: model.NewId(), + } + + store.Must(ss.Reaction().Save(reaction)) + firstUpdateAt := store.Must(ss.Post().Get(reaction.PostId)).(*model.PostList).Posts[post.Id].UpdateAt + + if result := <-ss.Reaction().Delete(reaction); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.Reaction().GetForPost(post.Id, false); result.Err != nil { + t.Fatal(result.Err) + } else if len(result.Data.([]*model.Reaction)) != 0 { + t.Fatal("should've deleted reaction") + } + + if postList := store.Must(ss.Post().Get(post.Id)).(*model.PostList); postList.Posts[post.Id].HasReactions { + t.Fatal("should've set HasReactions = false on post") + } else if postList.Posts[post.Id].UpdateAt == firstUpdateAt { + t.Fatal("shouldn't mark as updated when HasReactions has changed after deleting reactions") + } +} + +func testReactionGetForPost(t *testing.T, ss store.Store) { + postId := model.NewId() + + userId := model.NewId() + + reactions := []*model.Reaction{ + { + UserId: userId, + PostId: postId, + EmojiName: "smile", + }, + { + UserId: model.NewId(), + PostId: postId, + EmojiName: "smile", + }, + { + UserId: userId, + PostId: postId, + EmojiName: "sad", + }, + { + UserId: userId, + PostId: model.NewId(), + EmojiName: "angry", + }, + } + + for _, reaction := range reactions { + store.Must(ss.Reaction().Save(reaction)) + } + + if result := <-ss.Reaction().GetForPost(postId, false); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.([]*model.Reaction); len(returned) != 3 { + t.Fatal("should've returned 3 reactions") + } else { + for _, reaction := range reactions { + found := false + + for _, returnedReaction := range returned { + if returnedReaction.UserId == reaction.UserId && returnedReaction.PostId == reaction.PostId && + returnedReaction.EmojiName == reaction.EmojiName { + found = true + break + } + } + + if !found && reaction.PostId == postId { + t.Fatalf("should've returned reaction for post %v", reaction) + } else if found && reaction.PostId != postId { + t.Fatal("shouldn't have returned reaction for another post") + } + } + } + + // Should return cached item + if result := <-ss.Reaction().GetForPost(postId, true); result.Err != nil { + t.Fatal(result.Err) + } else if returned := result.Data.([]*model.Reaction); len(returned) != 3 { + t.Fatal("should've returned 3 reactions") + } else { + for _, reaction := range reactions { + found := false + + for _, returnedReaction := range returned { + if returnedReaction.UserId == reaction.UserId && returnedReaction.PostId == reaction.PostId && + returnedReaction.EmojiName == reaction.EmojiName { + found = true + break + } + } + + if !found && reaction.PostId == postId { + t.Fatalf("should've returned reaction for post %v", reaction) + } else if found && reaction.PostId != postId { + t.Fatal("shouldn't have returned reaction for another post") + } + } + } +} + +func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) { + emojiToDelete := model.NewId() + + post := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + post2 := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + post3 := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + + userId := model.NewId() + + reactions := []*model.Reaction{ + { + UserId: userId, + PostId: post.Id, + EmojiName: emojiToDelete, + }, + { + UserId: model.NewId(), + PostId: post.Id, + EmojiName: emojiToDelete, + }, + { + UserId: userId, + PostId: post.Id, + EmojiName: "sad", + }, + { + UserId: userId, + PostId: post2.Id, + EmojiName: "angry", + }, + { + UserId: userId, + PostId: post3.Id, + EmojiName: emojiToDelete, + }, + } + + for _, reaction := range reactions { + store.Must(ss.Reaction().Save(reaction)) + } + + if result := <-ss.Reaction().DeleteAllWithEmojiName(emojiToDelete); result.Err != nil { + t.Fatal(result.Err) + } + + // check that the reactions were deleted + if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 1 { + t.Fatal("should've only removed reactions with emoji name") + } else { + for _, reaction := range returned { + if reaction.EmojiName == "smile" { + t.Fatal("should've removed reaction with emoji name") + } + } + } + + if returned := store.Must(ss.Reaction().GetForPost(post2.Id, false)).([]*model.Reaction); len(returned) != 1 { + t.Fatal("should've only removed reactions with emoji name") + } + + if returned := store.Must(ss.Reaction().GetForPost(post3.Id, false)).([]*model.Reaction); len(returned) != 0 { + t.Fatal("should've only removed reactions with emoji name") + } + + // check that the posts are updated + if postList := store.Must(ss.Post().Get(post.Id)).(*model.PostList); !postList.Posts[post.Id].HasReactions { + t.Fatal("post should still have reactions") + } + + if postList := store.Must(ss.Post().Get(post2.Id)).(*model.PostList); !postList.Posts[post2.Id].HasReactions { + t.Fatal("post should still have reactions") + } + + if postList := store.Must(ss.Post().Get(post3.Id)).(*model.PostList); postList.Posts[post3.Id].HasReactions { + t.Fatal("post shouldn't have reactions any more") + } +} + +func testReactionStorePermanentDeleteBatch(t *testing.T, ss store.Store) { + post := store.Must(ss.Post().Save(&model.Post{ + ChannelId: model.NewId(), + UserId: model.NewId(), + })).(*model.Post) + + reactions := []*model.Reaction{ + { + UserId: model.NewId(), + PostId: post.Id, + EmojiName: "sad", + CreateAt: 1000, + }, + { + UserId: model.NewId(), + PostId: post.Id, + EmojiName: "sad", + CreateAt: 1500, + }, + { + UserId: model.NewId(), + PostId: post.Id, + EmojiName: "sad", + CreateAt: 2000, + }, + { + UserId: model.NewId(), + PostId: post.Id, + EmojiName: "sad", + CreateAt: 2000, + }, + } + + // Need to hang on to a reaction to delete later in order to clear the cache, as "allowFromCache" isn't honoured any more. + var lastReaction *model.Reaction + for _, reaction := range reactions { + lastReaction = store.Must(ss.Reaction().Save(reaction)).(*model.Reaction) + } + + if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 4 { + t.Fatal("expected 4 reactions") + } + + store.Must(ss.Reaction().PermanentDeleteBatch(1800, 1000)) + + // This is to force a clear of the cache. + store.Must(ss.Reaction().Delete(lastReaction)) + + if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 1 { + t.Fatalf("expected 1 reaction. Got: %v", len(returned)) + } +} diff --git a/store/storetest/session_store.go b/store/storetest/session_store.go new file mode 100644 index 000000000..3d35b91ad --- /dev/null +++ b/store/storetest/session_store.go @@ -0,0 +1,250 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestSessionStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testSessionStoreSave(t, ss) }) + t.Run("SessionGet", func(t *testing.T) { testSessionGet(t, ss) }) + t.Run("SessionGetWithDeviceId", func(t *testing.T) { testSessionGetWithDeviceId(t, ss) }) + t.Run("SessionRemove", func(t *testing.T) { testSessionRemove(t, ss) }) + t.Run("SessionRemoveAll", func(t *testing.T) { testSessionRemoveAll(t, ss) }) + t.Run("SessionRemoveByUser", func(t *testing.T) { testSessionRemoveByUser(t, ss) }) + t.Run("SessionRemoveToken", func(t *testing.T) { testSessionRemoveToken(t, ss) }) + t.Run("SessionUpdateDeviceId", func(t *testing.T) { testSessionUpdateDeviceId(t, ss) }) + t.Run("SessionUpdateDeviceId2", func(t *testing.T) { testSessionUpdateDeviceId2(t, ss) }) + t.Run("UpdateLastActivityAt", func(t *testing.T) { testSessionStoreUpdateLastActivityAt(t, ss) }) + t.Run("SessionCount", func(t *testing.T) { testSessionCount(t, ss) }) +} + +func testSessionStoreSave(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + + if err := (<-ss.Session().Save(&s1)).Err; err != nil { + t.Fatal(err) + } +} + +func testSessionGet(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + s2 := model.Session{} + s2.UserId = s1.UserId + store.Must(ss.Session().Save(&s2)) + + s3 := model.Session{} + s3.UserId = s1.UserId + s3.ExpiresAt = 1 + store.Must(ss.Session().Save(&s3)) + + if rs1 := (<-ss.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + if rs2 := (<-ss.Session().GetSessions(s1.UserId)); rs2.Err != nil { + t.Fatal(rs2.Err) + } else { + if len(rs2.Data.([]*model.Session)) != 2 { + t.Fatal("should match len") + } + } +} + +func testSessionGetWithDeviceId(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + s1.ExpiresAt = model.GetMillis() + 10000 + store.Must(ss.Session().Save(&s1)) + + s2 := model.Session{} + s2.UserId = s1.UserId + s2.DeviceId = model.NewId() + s2.ExpiresAt = model.GetMillis() + 10000 + store.Must(ss.Session().Save(&s2)) + + s3 := model.Session{} + s3.UserId = s1.UserId + s3.ExpiresAt = 1 + s3.DeviceId = model.NewId() + store.Must(ss.Session().Save(&s3)) + + if rs1 := (<-ss.Session().GetSessionsWithActiveDeviceIds(s1.UserId)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if len(rs1.Data.([]*model.Session)) != 1 { + t.Fatal("should match len") + } + } +} + +func testSessionRemove(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + store.Must(ss.Session().Remove(s1.Id)) + + if rs2 := (<-ss.Session().Get(s1.Id)); rs2.Err == nil { + t.Fatal("should have been removed") + } +} + +func testSessionRemoveAll(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + store.Must(ss.Session().RemoveAllSessions()) + + if rs2 := (<-ss.Session().Get(s1.Id)); rs2.Err == nil { + t.Fatal("should have been removed") + } +} + +func testSessionRemoveByUser(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + store.Must(ss.Session().PermanentDeleteSessionsByUser(s1.UserId)) + + if rs2 := (<-ss.Session().Get(s1.Id)); rs2.Err == nil { + t.Fatal("should have been removed") + } +} + +func testSessionRemoveToken(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().Get(s1.Id)); rs1.Err != nil { + t.Fatal(rs1.Err) + } else { + if rs1.Data.(*model.Session).Id != s1.Id { + t.Fatal("should match") + } + } + + store.Must(ss.Session().Remove(s1.Token)) + + if rs2 := (<-ss.Session().Get(s1.Id)); rs2.Err == nil { + t.Fatal("should have been removed") + } + + if rs3 := (<-ss.Session().GetSessions(s1.UserId)); rs3.Err != nil { + t.Fatal(rs3.Err) + } else { + if len(rs3.Data.([]*model.Session)) != 0 { + t.Fatal("should match len") + } + } +} + +func testSessionUpdateDeviceId(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().UpdateDeviceId(s1.Id, model.PUSH_NOTIFY_APPLE+":1234567890", s1.ExpiresAt)); rs1.Err != nil { + t.Fatal(rs1.Err) + } + + s2 := model.Session{} + s2.UserId = model.NewId() + store.Must(ss.Session().Save(&s2)) + + if rs2 := (<-ss.Session().UpdateDeviceId(s2.Id, model.PUSH_NOTIFY_APPLE+":1234567890", s1.ExpiresAt)); rs2.Err != nil { + t.Fatal(rs2.Err) + } +} + +func testSessionUpdateDeviceId2(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if rs1 := (<-ss.Session().UpdateDeviceId(s1.Id, model.PUSH_NOTIFY_APPLE_REACT_NATIVE+":1234567890", s1.ExpiresAt)); rs1.Err != nil { + t.Fatal(rs1.Err) + } + + s2 := model.Session{} + s2.UserId = model.NewId() + store.Must(ss.Session().Save(&s2)) + + if rs2 := (<-ss.Session().UpdateDeviceId(s2.Id, model.PUSH_NOTIFY_APPLE_REACT_NATIVE+":1234567890", s1.ExpiresAt)); rs2.Err != nil { + t.Fatal(rs2.Err) + } +} + +func testSessionStoreUpdateLastActivityAt(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + store.Must(ss.Session().Save(&s1)) + + if err := (<-ss.Session().UpdateLastActivityAt(s1.Id, 1234567890)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.Session().Get(s1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Session).LastActivityAt != 1234567890 { + t.Fatal("LastActivityAt not updated correctly") + } + } + +} + +func testSessionCount(t *testing.T, ss store.Store) { + s1 := model.Session{} + s1.UserId = model.NewId() + s1.ExpiresAt = model.GetMillis() + 100000 + store.Must(ss.Session().Save(&s1)) + + if r1 := <-ss.Session().AnalyticsSessionCount(); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) == 0 { + t.Fatal("should have at least 1 session") + } + } +} diff --git a/store/storetest/status_store.go b/store/storetest/status_store.go new file mode 100644 index 000000000..f2ea0f898 --- /dev/null +++ b/store/storetest/status_store.go @@ -0,0 +1,106 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestStatusStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testStatusStore(t, ss) }) + t.Run("ActiveUserCount", func(t *testing.T) { testActiveUserCount(t, ss) }) +} + +func testStatusStore(t *testing.T, ss store.Store) { + status := &model.Status{UserId: model.NewId(), Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""} + + if err := (<-ss.Status().SaveOrUpdate(status)).Err; err != nil { + t.Fatal(err) + } + + status.LastActivityAt = 10 + + if err := (<-ss.Status().SaveOrUpdate(status)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.Status().Get(status.UserId)).Err; err != nil { + t.Fatal(err) + } + + status2 := &model.Status{UserId: model.NewId(), Status: model.STATUS_AWAY, Manual: false, LastActivityAt: 0, ActiveChannel: ""} + if err := (<-ss.Status().SaveOrUpdate(status2)).Err; err != nil { + t.Fatal(err) + } + + status3 := &model.Status{UserId: model.NewId(), Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""} + if err := (<-ss.Status().SaveOrUpdate(status3)).Err; err != nil { + t.Fatal(err) + } + + if result := <-ss.Status().GetOnlineAway(); result.Err != nil { + t.Fatal(result.Err) + } else { + statuses := result.Data.([]*model.Status) + for _, status := range statuses { + if status.Status == model.STATUS_OFFLINE { + t.Fatal("should not have returned offline statuses") + } + } + } + + if result := <-ss.Status().GetOnline(); result.Err != nil { + t.Fatal(result.Err) + } else { + statuses := result.Data.([]*model.Status) + for _, status := range statuses { + if status.Status != model.STATUS_ONLINE { + t.Fatal("should not have returned offline statuses") + } + } + } + + if result := <-ss.Status().GetByIds([]string{status.UserId, "junk"}); result.Err != nil { + t.Fatal(result.Err) + } else { + statuses := result.Data.([]*model.Status) + if len(statuses) != 1 { + t.Fatal("should only have 1 status") + } + } + + if err := (<-ss.Status().ResetAll()).Err; err != nil { + t.Fatal(err) + } + + if result := <-ss.Status().Get(status.UserId); result.Err != nil { + t.Fatal(result.Err) + } else { + status := result.Data.(*model.Status) + if status.Status != model.STATUS_OFFLINE { + t.Fatal("should be offline") + } + } + + if result := <-ss.Status().UpdateLastActivityAt(status.UserId, 10); result.Err != nil { + t.Fatal(result.Err) + } +} + +func testActiveUserCount(t *testing.T, ss store.Store) { + status := &model.Status{UserId: model.NewId(), Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} + store.Must(ss.Status().SaveOrUpdate(status)) + + if result := <-ss.Status().GetTotalActiveUsersCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + count := result.Data.(int64) + if count <= 0 { + t.Fatal() + } + } +} diff --git a/store/storetest/system_store.go b/store/storetest/system_store.go new file mode 100644 index 000000000..32c39ee41 --- /dev/null +++ b/store/storetest/system_store.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestSystemStore(t *testing.T, ss store.Store) { + t.Run("", func(t *testing.T) { testSystemStore(t, ss) }) + t.Run("SaveOrUpdate", func(t *testing.T) { testSystemStoreSaveOrUpdate(t, ss) }) +} + +func testSystemStore(t *testing.T, ss store.Store) { + system := &model.System{Name: model.NewId(), Value: "value"} + store.Must(ss.System().Save(system)) + + result := <-ss.System().Get() + systems := result.Data.(model.StringMap) + + if systems[system.Name] != system.Value { + t.Fatal() + } + + system.Value = "value2" + store.Must(ss.System().Update(system)) + + result2 := <-ss.System().Get() + systems2 := result2.Data.(model.StringMap) + + if systems2[system.Name] != system.Value { + t.Fatal() + } + + result3 := <-ss.System().GetByName(system.Name) + rsystem := result3.Data.(*model.System) + if rsystem.Value != system.Value { + t.Fatal() + } +} + +func testSystemStoreSaveOrUpdate(t *testing.T, ss store.Store) { + system := &model.System{Name: model.NewId(), Value: "value"} + + if err := (<-ss.System().SaveOrUpdate(system)).Err; err != nil { + t.Fatal(err) + } + + system.Value = "value2" + + if r := <-ss.System().SaveOrUpdate(system); r.Err != nil { + t.Fatal(r.Err) + } +} diff --git a/store/storetest/team_store.go b/store/storetest/team_store.go new file mode 100644 index 000000000..e48595780 --- /dev/null +++ b/store/storetest/team_store.go @@ -0,0 +1,1010 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/utils" +) + +func TestTeamStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testTeamStoreSave(t, ss) }) + t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) }) + t.Run("UpdateDisplayName", func(t *testing.T) { testTeamStoreUpdateDisplayName(t, ss) }) + t.Run("Get", func(t *testing.T) { testTeamStoreGet(t, ss) }) + t.Run("GetByName", func(t *testing.T) { testTeamStoreGetByName(t, ss) }) + t.Run("SearchByName", func(t *testing.T) { testTeamStoreSearchByName(t, ss) }) + t.Run("SearchAll", func(t *testing.T) { testTeamStoreSearchAll(t, ss) }) + t.Run("SearchOpen", func(t *testing.T) { testTeamStoreSearchOpen(t, ss) }) + t.Run("GetByIniviteId", func(t *testing.T) { testTeamStoreGetByIniviteId(t, ss) }) + t.Run("ByUserId", func(t *testing.T) { testTeamStoreByUserId(t, ss) }) + t.Run("GetAllTeamListing", func(t *testing.T) { testGetAllTeamListing(t, ss) }) + t.Run("GetAllTeamPageListing", func(t *testing.T) { testGetAllTeamPageListing(t, ss) }) + t.Run("Delete", func(t *testing.T) { testDelete(t, ss) }) + t.Run("TeamCount", func(t *testing.T) { testTeamCount(t, ss) }) + t.Run("TeamMembers", func(t *testing.T) { testTeamMembers(t, ss) }) + t.Run("SaveTeamMemberMaxMembers", func(t *testing.T) { testSaveTeamMemberMaxMembers(t, ss) }) + t.Run("GetTeamMember", func(t *testing.T) { testGetTeamMember(t, ss) }) + t.Run("GetTeamMembersByIds", func(t *testing.T) { testGetTeamMembersByIds(t, ss) }) + t.Run("MemberCount", func(t *testing.T) { testTeamStoreMemberCount(t, ss) }) + t.Run("GetChannelUnreadsForAllTeams", func(t *testing.T) { testGetChannelUnreadsForAllTeams(t, ss) }) + t.Run("GetChannelUnreadsForTeam", func(t *testing.T) { testGetChannelUnreadsForTeam(t, ss) }) +} + +func testTeamStoreSave(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Team().Save(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } + + o1.Id = "" + if err := (<-ss.Team().Save(&o1)).Err; err == nil { + t.Fatal("should be unique domain") + } +} + +func testTeamStoreUpdate(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + time.Sleep(100 * time.Millisecond) + + if err := (<-ss.Team().Update(&o1)).Err; err != nil { + t.Fatal(err) + } + + o1.Id = "missing" + if err := (<-ss.Team().Update(&o1)).Err; err == nil { + t.Fatal("Update should have failed because of missing key") + } + + o1.Id = model.NewId() + if err := (<-ss.Team().Update(&o1)).Err; err == nil { + t.Fatal("Update should have faile because id change") + } +} + +func testTeamStoreUpdateDisplayName(t *testing.T, ss store.Store) { + o1 := &model.Team{} + o1.DisplayName = "Display Name" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1 = (<-ss.Team().Save(o1)).Data.(*model.Team) + + newDisplayName := "NewDisplayName" + + if err := (<-ss.Team().UpdateDisplayName(newDisplayName, o1.Id)).Err; err != nil { + t.Fatal(err) + } + + ro1 := (<-ss.Team().Get(o1.Id)).Data.(*model.Team) + if ro1.DisplayName != newDisplayName { + t.Fatal("DisplayName not updated") + } +} + +func testTeamStoreGet(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + store.Must(ss.Team().Save(&o1)) + + if r1 := <-ss.Team().Get(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Team).ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + } + + if err := (<-ss.Team().Get("")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testTeamStoreGetByName(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.Team().GetByName(o1.Name); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Team).ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + } + + if err := (<-ss.Team().GetByName("")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testTeamStoreSearchByName(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + var name = "zzz" + model.NewId() + o1.Name = name + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.Team().SearchByName(name); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + } +} + +func testTeamStoreSearchAll(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "ADisplayName" + model.NewId() + o1.Name = "zz" + model.NewId() + "a" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + p2 := model.Team{} + p2.DisplayName = "BDisplayName" + model.NewId() + p2.Name = "b" + model.NewId() + "b" + p2.Email = model.NewId() + "@nowhere.com" + p2.Type = model.TEAM_INVITE + + if err := (<-ss.Team().Save(&p2)).Err; err != nil { + t.Fatal(err) + } + + r1 := <-ss.Team().SearchAll(o1.Name) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 1 { + t.Fatal("should have returned 1 team") + } + if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + + r1 = <-ss.Team().SearchAll(p2.DisplayName) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 1 { + t.Fatal("should have returned 1 team") + } + if r1.Data.([]*model.Team)[0].ToJson() != p2.ToJson() { + t.Fatal("invalid returned team") + } + + r1 = <-ss.Team().SearchAll("junk") + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 0 { + t.Fatal("should have not returned a team") + } +} + +func testTeamStoreSearchOpen(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "ADisplayName" + model.NewId() + o1.Name = "zz" + model.NewId() + "a" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + o2 := model.Team{} + o2.DisplayName = "ADisplayName" + model.NewId() + o2.Name = "zz" + model.NewId() + "a" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + o2.AllowOpenInvite = false + + if err := (<-ss.Team().Save(&o2)).Err; err != nil { + t.Fatal(err) + } + + p2 := model.Team{} + p2.DisplayName = "BDisplayName" + model.NewId() + p2.Name = "b" + model.NewId() + "b" + p2.Email = model.NewId() + "@nowhere.com" + p2.Type = model.TEAM_INVITE + p2.AllowOpenInvite = true + + if err := (<-ss.Team().Save(&p2)).Err; err != nil { + t.Fatal(err) + } + + r1 := <-ss.Team().SearchOpen(o1.Name) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 1 { + t.Fatal("should have returned 1 team") + } + if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + + r1 = <-ss.Team().SearchOpen(o1.DisplayName) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 1 { + t.Fatal("should have returned 1 team") + } + if r1.Data.([]*model.Team)[0].ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + + r1 = <-ss.Team().SearchOpen(p2.Name) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 0 { + t.Fatal("should have not returned a team") + } + + r1 = <-ss.Team().SearchOpen(p2.DisplayName) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 0 { + t.Fatal("should have not returned a team") + } + + r1 = <-ss.Team().SearchOpen("junk") + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 0 { + t.Fatal("should have not returned a team") + } + + r1 = <-ss.Team().SearchOpen(o2.DisplayName) + if r1.Err != nil { + t.Fatal(r1.Err) + } + if len(r1.Data.([]*model.Team)) != 0 { + t.Fatal("should have not returned a team") + } +} + +func testTeamStoreGetByIniviteId(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.InviteId = model.NewId() + + if err := (<-ss.Team().Save(&o1)).Err; err != nil { + t.Fatal(err) + } + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + + if err := (<-ss.Team().Save(&o2)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.Team().GetByInviteId(o1.InviteId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Team).ToJson() != o1.ToJson() { + t.Fatal("invalid returned team") + } + } + + o2.InviteId = "" + <-ss.Team().Update(&o2) + + if r1 := <-ss.Team().GetByInviteId(o2.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.Team).Id != o2.Id { + t.Fatal("invalid returned team") + } + } + + if err := (<-ss.Team().GetByInviteId("")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testTeamStoreByUserId(t *testing.T, ss store.Store) { + o1 := &model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.InviteId = model.NewId() + o1 = store.Must(ss.Team().Save(o1)).(*model.Team) + + m1 := &model.TeamMember{TeamId: o1.Id, UserId: model.NewId()} + store.Must(ss.Team().SaveMember(m1)) + + if r1 := <-ss.Team().GetTeamsByUserId(m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + if len(teams) == 0 { + t.Fatal("Should return a team") + } + + if teams[0].Id != o1.Id { + t.Fatal("should be a member") + } + + } +} + +func testGetAllTeamListing(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + store.Must(ss.Team().Save(&o2)) + + o3 := model.Team{} + o3.DisplayName = "DisplayName" + o3.Name = "z-z-z" + model.NewId() + "b" + o3.Email = model.NewId() + "@nowhere.com" + o3.Type = model.TEAM_INVITE + o3.AllowOpenInvite = true + store.Must(ss.Team().Save(&o3)) + + o4 := model.Team{} + o4.DisplayName = "DisplayName" + o4.Name = "zz" + model.NewId() + "b" + o4.Email = model.NewId() + "@nowhere.com" + o4.Type = model.TEAM_INVITE + store.Must(ss.Team().Save(&o4)) + + if r1 := <-ss.Team().GetAllTeamListing(); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + 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") + } + } +} + +func testGetAllTeamPageListing(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + o2.AllowOpenInvite = false + store.Must(ss.Team().Save(&o2)) + + o3 := model.Team{} + o3.DisplayName = "DisplayName" + o3.Name = "z-z-z" + model.NewId() + "b" + o3.Email = model.NewId() + "@nowhere.com" + o3.Type = model.TEAM_INVITE + o3.AllowOpenInvite = true + store.Must(ss.Team().Save(&o3)) + + o4 := model.Team{} + o4.DisplayName = "DisplayName" + o4.Name = "zz" + model.NewId() + "b" + o4.Email = model.NewId() + "@nowhere.com" + o4.Type = model.TEAM_INVITE + o4.AllowOpenInvite = false + store.Must(ss.Team().Save(&o4)) + + if r1 := <-ss.Team().GetAllTeamPageListing(0, 10); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if !team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as true") + } + } + + if len(teams) > 10 { + t.Fatal("should have returned max of 10 teams") + } + } + + o5 := model.Team{} + o5.DisplayName = "DisplayName" + o5.Name = "z-z-z" + model.NewId() + "b" + o5.Email = model.NewId() + "@nowhere.com" + o5.Type = model.TEAM_OPEN + o5.AllowOpenInvite = true + store.Must(ss.Team().Save(&o5)) + + if r1 := <-ss.Team().GetAllTeamPageListing(0, 4); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if !team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as true") + } + } + + if len(teams) > 4 { + t.Fatal("should have returned max of 4 teams") + } + } + + if r1 := <-ss.Team().GetAllTeamPageListing(1, 1); r1.Err != nil { + t.Fatal(r1.Err) + } else { + teams := r1.Data.([]*model.Team) + + for _, team := range teams { + if !team.AllowOpenInvite { + t.Fatal("should have returned team with AllowOpenInvite as true") + } + } + + if len(teams) > 1 { + t.Fatal("should have returned max of 1 team") + } + } +} + +func testDelete(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + o2 := model.Team{} + o2.DisplayName = "DisplayName" + o2.Name = "zz" + model.NewId() + "b" + o2.Email = model.NewId() + "@nowhere.com" + o2.Type = model.TEAM_OPEN + store.Must(ss.Team().Save(&o2)) + + if r1 := <-ss.Team().PermanentDelete(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } +} + +func testTeamCount(t *testing.T, ss store.Store) { + o1 := model.Team{} + o1.DisplayName = "DisplayName" + o1.Name = "z-z-z" + model.NewId() + "b" + o1.Email = model.NewId() + "@nowhere.com" + o1.Type = model.TEAM_OPEN + o1.AllowOpenInvite = true + store.Must(ss.Team().Save(&o1)) + + if r1 := <-ss.Team().AnalyticsTeamCount(); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(int64) == 0 { + t.Fatal("should be at least 1 team") + } + } +} + +func testTeamMembers(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + teamId2 := model.NewId() + + m1 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + m2 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + m3 := &model.TeamMember{TeamId: teamId2, UserId: model.NewId()} + + if r1 := <-ss.Team().SaveMember(m1); r1.Err != nil { + t.Fatal(r1.Err) + } + + store.Must(ss.Team().SaveMember(m2)) + store.Must(ss.Team().SaveMember(m3)) + + if r1 := <-ss.Team().GetMembers(teamId1, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 2 { + t.Fatal() + } + } + + if r1 := <-ss.Team().GetMembers(teamId2, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 1 { + t.Fatal() + } + + if ms[0].UserId != m3.UserId { + t.Fatal() + + } + } + + if r1 := <-ss.Team().GetTeamsForUser(m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 1 { + t.Fatal() + } + + if ms[0].TeamId != m1.TeamId { + t.Fatal() + + } + } + + if r1 := <-ss.Team().RemoveMember(teamId1, m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } + + if r1 := <-ss.Team().GetMembers(teamId1, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 1 { + t.Fatal() + } + + if ms[0].UserId != m2.UserId { + t.Fatal() + + } + } + + store.Must(ss.Team().SaveMember(m1)) + + if r1 := <-ss.Team().RemoveAllMembersByTeam(teamId1); r1.Err != nil { + t.Fatal(r1.Err) + } + + if r1 := <-ss.Team().GetMembers(teamId1, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 0 { + t.Fatal() + } + } + + uid := model.NewId() + m4 := &model.TeamMember{TeamId: teamId1, UserId: uid} + m5 := &model.TeamMember{TeamId: teamId2, UserId: uid} + store.Must(ss.Team().SaveMember(m4)) + store.Must(ss.Team().SaveMember(m5)) + + if r1 := <-ss.Team().GetTeamsForUser(uid); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 2 { + t.Fatal() + } + } + + if r1 := <-ss.Team().RemoveAllMembersByUser(uid); r1.Err != nil { + t.Fatal(r1.Err) + } + + if r1 := <-ss.Team().GetTeamsForUser(m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.TeamMember) + + if len(ms) != 0 { + t.Fatal() + } + } +} + +func testSaveTeamMemberMaxMembers(t *testing.T, ss store.Store) { + MaxUsersPerTeam := *utils.Cfg.TeamSettings.MaxUsersPerTeam + defer func() { + *utils.Cfg.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam + }() + *utils.Cfg.TeamSettings.MaxUsersPerTeam = 5 + + team := store.Must(ss.Team().Save(&model.Team{ + DisplayName: "DisplayName", + Name: "z-z-z" + model.NewId() + "b", + Type: model.TEAM_OPEN, + })).(*model.Team) + defer func() { + <-ss.Team().PermanentDelete(team.Id) + }() + + userIds := make([]string, *utils.Cfg.TeamSettings.MaxUsersPerTeam) + + for i := 0; i < *utils.Cfg.TeamSettings.MaxUsersPerTeam; i++ { + userIds[i] = store.Must(ss.User().Save(&model.User{ + Username: model.NewId(), + Email: model.NewId(), + })).(*model.User).Id + + defer func(userId string) { + <-ss.User().PermanentDelete(userId) + }(userIds[i]) + + store.Must(ss.Team().SaveMember(&model.TeamMember{ + TeamId: team.Id, + UserId: userIds[i], + })) + + defer func(userId string) { + <-ss.Team().RemoveMember(team.Id, userId) + }(userIds[i]) + } + + if result := <-ss.Team().GetTotalMemberCount(team.Id); result.Err != nil { + t.Fatal(result.Err) + } else if count := result.Data.(int64); int(count) != *utils.Cfg.TeamSettings.MaxUsersPerTeam { + t.Fatalf("should start with 5 team members, had %v instead", count) + } + + newUserId := store.Must(ss.User().Save(&model.User{ + Username: model.NewId(), + Email: model.NewId(), + })).(*model.User).Id + defer func() { + <-ss.User().PermanentDelete(newUserId) + }() + + if result := <-ss.Team().SaveMember(&model.TeamMember{ + TeamId: team.Id, + UserId: newUserId, + }); result.Err == nil { + t.Fatal("shouldn't be able to save member when at maximum members per team") + } + + if result := <-ss.Team().GetTotalMemberCount(team.Id); result.Err != nil { + t.Fatal(result.Err) + } else if count := result.Data.(int64); int(count) != *utils.Cfg.TeamSettings.MaxUsersPerTeam { + t.Fatalf("should still have 5 team members, had %v instead", count) + } + + // Leaving the team from the UI sets DeleteAt instead of using TeamStore.RemoveMember + store.Must(ss.Team().UpdateMember(&model.TeamMember{ + TeamId: team.Id, + UserId: userIds[0], + DeleteAt: 1234, + })) + + if result := <-ss.Team().GetTotalMemberCount(team.Id); result.Err != nil { + t.Fatal(result.Err) + } else if count := result.Data.(int64); int(count) != *utils.Cfg.TeamSettings.MaxUsersPerTeam-1 { + t.Fatalf("should now only have 4 team members, had %v instead", count) + } + + if result := <-ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId}); result.Err != nil { + t.Fatal("should've been able to save new member after deleting one", result.Err) + } else { + defer func(userId string) { + <-ss.Team().RemoveMember(team.Id, userId) + }(newUserId) + } + + if result := <-ss.Team().GetTotalMemberCount(team.Id); result.Err != nil { + t.Fatal(result.Err) + } else if count := result.Data.(int64); int(count) != *utils.Cfg.TeamSettings.MaxUsersPerTeam { + t.Fatalf("should have 5 team members again, had %v instead", count) + } + + // Deactivating a user should make them stop counting against max members + user2 := store.Must(ss.User().Get(userIds[1])).(*model.User) + user2.DeleteAt = 1234 + store.Must(ss.User().Update(user2, true)) + + newUserId2 := store.Must(ss.User().Save(&model.User{ + Username: model.NewId(), + Email: model.NewId(), + })).(*model.User).Id + if result := <-ss.Team().SaveMember(&model.TeamMember{TeamId: team.Id, UserId: newUserId2}); result.Err != nil { + t.Fatal("should've been able to save new member after deleting one", result.Err) + } else { + defer func(userId string) { + <-ss.Team().RemoveMember(team.Id, userId) + }(newUserId2) + } +} + +func testGetTeamMember(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + + m1 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + store.Must(ss.Team().SaveMember(m1)) + + if r := <-ss.Team().GetMember(m1.TeamId, m1.UserId); r.Err != nil { + t.Fatal(r.Err) + } else { + rm1 := r.Data.(*model.TeamMember) + + if rm1.TeamId != m1.TeamId { + t.Fatal("bad team id") + } + + if rm1.UserId != m1.UserId { + t.Fatal("bad user id") + } + } + + if r := <-ss.Team().GetMember(m1.TeamId, ""); r.Err == nil { + t.Fatal("empty user id - should have failed") + } + + if r := <-ss.Team().GetMember("", m1.UserId); r.Err == nil { + t.Fatal("empty team id - should have failed") + } +} + +func testGetTeamMembersByIds(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + + m1 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + store.Must(ss.Team().SaveMember(m1)) + + if r := <-ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId}); r.Err != nil { + t.Fatal(r.Err) + } else { + rm1 := r.Data.([]*model.TeamMember)[0] + + if rm1.TeamId != m1.TeamId { + t.Fatal("bad team id") + } + + if rm1.UserId != m1.UserId { + t.Fatal("bad user id") + } + } + + m2 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + store.Must(ss.Team().SaveMember(m2)) + + if r := <-ss.Team().GetMembersByIds(m1.TeamId, []string{m1.UserId, m2.UserId, model.NewId()}); r.Err != nil { + t.Fatal(r.Err) + } else { + rm := r.Data.([]*model.TeamMember) + + if len(rm) != 2 { + t.Fatal("return wrong number of results") + } + } + + if r := <-ss.Team().GetMembersByIds(m1.TeamId, []string{}); r.Err == nil { + t.Fatal("empty user ids - should have failed") + } +} + +func testTeamStoreMemberCount(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.DeleteAt = 1 + store.Must(ss.User().Save(u2)) + + teamId1 := model.NewId() + m1 := &model.TeamMember{TeamId: teamId1, UserId: u1.Id} + store.Must(ss.Team().SaveMember(m1)) + + m2 := &model.TeamMember{TeamId: teamId1, UserId: u2.Id} + store.Must(ss.Team().SaveMember(m2)) + + if result := <-ss.Team().GetTotalMemberCount(teamId1); result.Err != nil { + t.Fatal(result.Err) + } else { + if result.Data.(int64) != 2 { + t.Fatal("wrong count") + } + } + + if result := <-ss.Team().GetActiveMemberCount(teamId1); result.Err != nil { + t.Fatal(result.Err) + } else { + if result.Data.(int64) != 1 { + t.Fatal("wrong count") + } + } + + m3 := &model.TeamMember{TeamId: teamId1, UserId: model.NewId()} + store.Must(ss.Team().SaveMember(m3)) + + if result := <-ss.Team().GetTotalMemberCount(teamId1); result.Err != nil { + t.Fatal(result.Err) + } else { + if result.Data.(int64) != 2 { + t.Fatal("wrong count") + } + } + + if result := <-ss.Team().GetActiveMemberCount(teamId1); result.Err != nil { + t.Fatal(result.Err) + } else { + if result.Data.(int64) != 1 { + t.Fatal("wrong count") + } + } +} + +func testGetChannelUnreadsForAllTeams(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + teamId2 := model.NewId() + + uid := model.NewId() + m1 := &model.TeamMember{TeamId: teamId1, UserId: uid} + m2 := &model.TeamMember{TeamId: teamId2, UserId: uid} + store.Must(ss.Team().SaveMember(m1)) + store.Must(ss.Team().SaveMember(m2)) + + c1 := &model.Channel{TeamId: m1.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c1)) + c2 := &model.Channel{TeamId: m2.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c2)) + + cm1 := &model.ChannelMember{ChannelId: c1.Id, UserId: m1.UserId, NotifyProps: model.GetDefaultChannelNotifyProps(), MsgCount: 90} + store.Must(ss.Channel().SaveMember(cm1)) + cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m2.UserId, NotifyProps: model.GetDefaultChannelNotifyProps(), MsgCount: 90} + store.Must(ss.Channel().SaveMember(cm2)) + + if r1 := <-ss.Team().GetChannelUnreadsForAllTeams("", uid); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.ChannelUnread) + membersMap := make(map[string]bool) + for i := range ms { + id := ms[i].TeamId + if _, ok := membersMap[id]; !ok { + membersMap[id] = true + } + } + if len(membersMap) != 2 { + t.Fatal("Should be the unreads for all the teams") + } + + if ms[0].MsgCount != 10 { + t.Fatal("subtraction failed") + } + } + + if r2 := <-ss.Team().GetChannelUnreadsForAllTeams(teamId1, uid); r2.Err != nil { + t.Fatal(r2.Err) + } else { + ms := r2.Data.([]*model.ChannelUnread) + membersMap := make(map[string]bool) + for i := range ms { + id := ms[i].TeamId + if _, ok := membersMap[id]; !ok { + membersMap[id] = true + } + } + + if len(membersMap) != 1 { + t.Fatal("Should be the unreads for just one team") + } + + if ms[0].MsgCount != 10 { + t.Fatal("subtraction failed") + } + } + + if r1 := <-ss.Team().RemoveAllMembersByUser(uid); r1.Err != nil { + t.Fatal(r1.Err) + } +} + +func testGetChannelUnreadsForTeam(t *testing.T, ss store.Store) { + teamId1 := model.NewId() + + uid := model.NewId() + m1 := &model.TeamMember{TeamId: teamId1, UserId: uid} + store.Must(ss.Team().SaveMember(m1)) + + c1 := &model.Channel{TeamId: m1.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c1)) + c2 := &model.Channel{TeamId: m1.TeamId, Name: model.NewId(), DisplayName: "Town Square", Type: model.CHANNEL_OPEN, TotalMsgCount: 100} + store.Must(ss.Channel().Save(c2)) + + cm1 := &model.ChannelMember{ChannelId: c1.Id, UserId: m1.UserId, NotifyProps: model.GetDefaultChannelNotifyProps(), MsgCount: 90} + store.Must(ss.Channel().SaveMember(cm1)) + cm2 := &model.ChannelMember{ChannelId: c2.Id, UserId: m1.UserId, NotifyProps: model.GetDefaultChannelNotifyProps(), MsgCount: 90} + store.Must(ss.Channel().SaveMember(cm2)) + + if r1 := <-ss.Team().GetChannelUnreadsForTeam(m1.TeamId, m1.UserId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + ms := r1.Data.([]*model.ChannelUnread) + if len(ms) != 2 { + t.Fatal("wrong length") + } + + if ms[0].MsgCount != 10 { + t.Fatal("subtraction failed") + } + } +} diff --git a/store/storetest/user_access_token_store.go b/store/storetest/user_access_token_store.go new file mode 100644 index 000000000..292929419 --- /dev/null +++ b/store/storetest/user_access_token_store.go @@ -0,0 +1,89 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestUserAccessTokenStore(t *testing.T, ss store.Store) { + t.Run("UserAccessTokenSaveGetDelete", func(t *testing.T) { testUserAccessTokenSaveGetDelete(t, ss) }) +} + +func testUserAccessTokenSaveGetDelete(t *testing.T, ss store.Store) { + uat := &model.UserAccessToken{ + Token: model.NewId(), + UserId: model.NewId(), + Description: "testtoken", + } + + s1 := model.Session{} + s1.UserId = uat.UserId + s1.Token = uat.Token + + store.Must(ss.Session().Save(&s1)) + + if result := <-ss.UserAccessToken().Save(uat); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.UserAccessToken().Get(uat.Id); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.(*model.UserAccessToken); received.Token != uat.Token { + t.Fatal("received incorrect token after save") + } + + if result := <-ss.UserAccessToken().GetByToken(uat.Token); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.(*model.UserAccessToken); received.Token != uat.Token { + t.Fatal("received incorrect token after save") + } + + if result := <-ss.UserAccessToken().GetByToken("notarealtoken"); result.Err == nil { + t.Fatal("should have failed on bad token") + } + + if result := <-ss.UserAccessToken().GetByUser(uat.UserId, 0, 100); result.Err != nil { + t.Fatal(result.Err) + } else if received := result.Data.([]*model.UserAccessToken); len(received) != 1 { + t.Fatal("received incorrect number of tokens after save") + } + + if result := <-ss.UserAccessToken().Delete(uat.Id); result.Err != nil { + t.Fatal(result.Err) + } + + if err := (<-ss.Session().Get(s1.Token)).Err; err == nil { + t.Fatal("should error - session should be deleted") + } + + if err := (<-ss.UserAccessToken().GetByToken(s1.Token)).Err; err == nil { + t.Fatal("should error - access token should be deleted") + } + + s2 := model.Session{} + s2.UserId = uat.UserId + s2.Token = uat.Token + + store.Must(ss.Session().Save(&s2)) + + if result := <-ss.UserAccessToken().Save(uat); result.Err != nil { + t.Fatal(result.Err) + } + + if result := <-ss.UserAccessToken().DeleteAllForUser(uat.UserId); result.Err != nil { + t.Fatal(result.Err) + } + + if err := (<-ss.Session().Get(s2.Token)).Err; err == nil { + t.Fatal("should error - session should be deleted") + } + + if err := (<-ss.UserAccessToken().GetByToken(s2.Token)).Err; err == nil { + t.Fatal("should error - access token should be deleted") + } +} diff --git a/store/storetest/user_store.go b/store/storetest/user_store.go new file mode 100644 index 000000000..d00e88f56 --- /dev/null +++ b/store/storetest/user_store.go @@ -0,0 +1,2074 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "strings" + "testing" + "time" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestUserStore(t *testing.T, ss store.Store) { + t.Run("Save", func(t *testing.T) { testUserStoreSave(t, ss) }) + t.Run("Update", func(t *testing.T) { testUserStoreUpdate(t, ss) }) + t.Run("UpdateUpdateAt", func(t *testing.T) { testUserStoreUpdateUpdateAt(t, ss) }) + t.Run("UpdateFailedPasswordAttempts", func(t *testing.T) { testUserStoreUpdateFailedPasswordAttempts(t, ss) }) + t.Run("Get", func(t *testing.T) { testUserStoreGet(t, ss) }) + t.Run("UserCount", func(t *testing.T) { testUserCount(t, ss) }) + t.Run("GetAllUsingAuthService", func(t *testing.T) { testGetAllUsingAuthService(t, ss) }) + t.Run("GetAllProfiles", func(t *testing.T) { testUserStoreGetAllProfiles(t, ss) }) + t.Run("GetProfiles", func(t *testing.T) { testUserStoreGetProfiles(t, ss) }) + t.Run("GetProfilesInChannel", func(t *testing.T) { testUserStoreGetProfilesInChannel(t, ss) }) + t.Run("GetProfilesWithoutTeam", func(t *testing.T) { testUserStoreGetProfilesWithoutTeam(t, ss) }) + t.Run("GetAllProfilesInChannel", func(t *testing.T) { testUserStoreGetAllProfilesInChannel(t, ss) }) + t.Run("GetProfilesNotInChannel", func(t *testing.T) { testUserStoreGetProfilesNotInChannel(t, ss) }) + t.Run("GetProfilesByIds", func(t *testing.T) { testUserStoreGetProfilesByIds(t, ss) }) + t.Run("GetProfilesByUsernames", func(t *testing.T) { testUserStoreGetProfilesByUsernames(t, ss) }) + t.Run("GetSystemAdminProfiles", func(t *testing.T) { testUserStoreGetSystemAdminProfiles(t, ss) }) + t.Run("GetByEmail", func(t *testing.T) { testUserStoreGetByEmail(t, ss) }) + t.Run("GetByAuthData", func(t *testing.T) { testUserStoreGetByAuthData(t, ss) }) + t.Run("GetByUsername", func(t *testing.T) { testUserStoreGetByUsername(t, ss) }) + t.Run("GetForLogin", func(t *testing.T) { testUserStoreGetForLogin(t, ss) }) + t.Run("UpdatePassword", func(t *testing.T) { testUserStoreUpdatePassword(t, ss) }) + t.Run("Delete", func(t *testing.T) { testUserStoreDelete(t, ss) }) + t.Run("UpdateAuthData", func(t *testing.T) { testUserStoreUpdateAuthData(t, ss) }) + t.Run("UserUnreadCount", func(t *testing.T) { testUserUnreadCount(t, ss) }) + t.Run("UpdateMfaSecret", func(t *testing.T) { testUserStoreUpdateMfaSecret(t, ss) }) + t.Run("UpdateMfaActive", func(t *testing.T) { testUserStoreUpdateMfaActive(t, ss) }) + t.Run("GetRecentlyActiveUsersForTeam", func(t *testing.T) { testUserStoreGetRecentlyActiveUsersForTeam(t, ss) }) + t.Run("GetNewUsersForTeam", func(t *testing.T) { testUserStoreGetNewUsersForTeam(t, ss) }) + t.Run("Search", func(t *testing.T) { testUserStoreSearch(t, ss) }) + t.Run("SearchWithoutTeam", func(t *testing.T) { testUserStoreSearchWithoutTeam(t, ss) }) + t.Run("AnalyticsGetInactiveUsersCount", func(t *testing.T) { testUserStoreAnalyticsGetInactiveUsersCount(t, ss) }) + t.Run("AnalyticsGetSystemAdminCount", func(t *testing.T) { testUserStoreAnalyticsGetSystemAdminCount(t, ss) }) + t.Run("GetProfilesNotInTeam", func(t *testing.T) { testUserStoreGetProfilesNotInTeam(t, ss) }) +} + +func testUserStoreSave(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := model.User{} + u1.Email = model.NewId() + u1.Username = model.NewId() + + if err := (<-ss.User().Save(&u1)).Err; err != nil { + t.Fatal("couldn't save user", err) + } + + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + if err := (<-ss.User().Save(&u1)).Err; err == nil { + t.Fatal("shouldn't be able to update user from save") + } + + u1.Id = "" + if err := (<-ss.User().Save(&u1)).Err; err == nil { + t.Fatal("should be unique email") + } + + u1.Email = "" + if err := (<-ss.User().Save(&u1)).Err; err == nil { + t.Fatal("should be unique username") + } + + u1.Email = strings.Repeat("0123456789", 20) + u1.Username = "" + if err := (<-ss.User().Save(&u1)).Err; err == nil { + t.Fatal("should be unique username") + } + + for i := 0; i < 49; i++ { + u1.Id = "" + u1.Email = model.NewId() + u1.Username = model.NewId() + if err := (<-ss.User().Save(&u1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + } + + u1.Id = "" + u1.Email = model.NewId() + u1.Username = model.NewId() + if err := (<-ss.User().Save(&u1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})).Err; err == nil { + t.Fatal("should be the limit") + } + +} + +func testUserStoreUpdate(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.AuthService = "ldap" + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id})) + + time.Sleep(100 * time.Millisecond) + + if err := (<-ss.User().Update(u1, false)).Err; err != nil { + t.Fatal(err) + } + + u1.Id = "missing" + if err := (<-ss.User().Update(u1, false)).Err; err == nil { + t.Fatal("Update should have failed because of missing key") + } + + u1.Id = model.NewId() + if err := (<-ss.User().Update(u1, false)).Err; err == nil { + t.Fatal("Update should have faile because id change") + } + + u2.Email = model.NewId() + if err := (<-ss.User().Update(u2, false)).Err; err == nil { + t.Fatal("Update should have failed because you can't modify AD/LDAP fields") + } + + u3 := &model.User{} + u3.Email = model.NewId() + oldEmail := u3.Email + u3.AuthService = "gitlab" + store.Must(ss.User().Save(u3)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u3.Id})) + + u3.Email = model.NewId() + if result := <-ss.User().Update(u3, false); result.Err != nil { + t.Fatal("Update should not have failed") + } else { + newUser := result.Data.([2]*model.User)[0] + if newUser.Email != oldEmail { + t.Fatal("Email should not have been updated as the update is not trusted") + } + } + + if result := <-ss.User().Update(u3, true); result.Err != nil { + t.Fatal("Update should not have failed") + } else { + newUser := result.Data.([2]*model.User)[0] + if newUser.Email != u3.Email { + t.Fatal("Email should have been updated as the update is trusted") + } + } + + if result := <-ss.User().UpdateLastPictureUpdate(u1.Id); result.Err != nil { + t.Fatal("Update should not have failed") + } +} + +func testUserStoreUpdateUpdateAt(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + time.Sleep(10 * time.Millisecond) + + if err := (<-ss.User().UpdateUpdateAt(u1.Id)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.User().Get(u1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.User).UpdateAt <= u1.UpdateAt { + t.Fatal("UpdateAt not updated correctly") + } + } + +} + +func testUserStoreUpdateFailedPasswordAttempts(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + if err := (<-ss.User().UpdateFailedPasswordAttempts(u1.Id, 3)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.User().Get(u1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.User).FailedAttempts != 3 { + t.Fatal("FailedAttempts not updated correctly") + } + } + +} + +func testUserStoreGet(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + if r1 := <-ss.User().Get(u1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.User).ToJson() != u1.ToJson() { + t.Fatal("invalid returned user") + } + } + + if err := (<-ss.User().Get("")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testUserCount(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + if result := <-ss.User().GetTotalUsersCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + count := result.Data.(int64) + if count <= 0 { + t.Fatal() + } + } +} + +func testGetAllUsingAuthService(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + u1.AuthService = "someservice" + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.AuthService = "someservice" + store.Must(ss.User().Save(u2)) + + if r1 := <-ss.User().GetAllUsingAuthService(u1.AuthService); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) < 2 { + t.Fatal("invalid returned users") + } + } +} + +func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + + if r1 := <-ss.User().GetAllProfiles(0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) < 2 { + t.Fatal("invalid returned users") + } + } + + if r2 := <-ss.User().GetAllProfiles(0, 1); r2.Err != nil { + t.Fatal(r2.Err) + } else { + users := r2.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users, limit did not work") + } + } + + if r2 := <-ss.User().GetAll(); r2.Err != nil { + t.Fatal(r2.Err) + } else { + users := r2.Data.([]*model.User) + if len(users) < 2 { + t.Fatal("invalid returned users") + } + } + + etag := "" + if r2 := <-ss.User().GetEtagForAllProfiles(); r2.Err != nil { + t.Fatal(r2.Err) + } else { + etag = r2.Data.(string) + } + + u3 := &model.User{} + u3.Email = model.NewId() + store.Must(ss.User().Save(u3)) + + if r2 := <-ss.User().GetEtagForAllProfiles(); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if etag == r2.Data.(string) { + t.Fatal("etags should not match") + } + } +} + +func testUserStoreGetProfiles(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + if r1 := <-ss.User().GetProfiles(teamId, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r2 := <-ss.User().GetProfiles("123", 0, 100); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.([]*model.User)) != 0 { + t.Fatal("should have returned empty map") + } + } + + etag := "" + if r2 := <-ss.User().GetEtagForProfiles(teamId); r2.Err != nil { + t.Fatal(r2.Err) + } else { + etag = r2.Data.(string) + } + + u3 := &model.User{} + u3.Email = model.NewId() + store.Must(ss.User().Save(u3)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id})) + + if r2 := <-ss.User().GetEtagForProfiles(teamId); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if etag == r2.Data.(string) { + t.Fatal("etags should not match") + } + } +} + +func testUserStoreGetProfilesInChannel(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + c1 := model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Profiles in channel" + c1.Name = "profiles-" + model.NewId() + c1.Type = model.CHANNEL_OPEN + + c2 := model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Profiles in private" + c2.Name = "profiles-" + model.NewId() + c2.Type = model.CHANNEL_PRIVATE + + store.Must(ss.Channel().Save(&c1)) + store.Must(ss.Channel().Save(&c2)) + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = c1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + m3 := model.ChannelMember{} + m3.ChannelId = c2.Id + m3.UserId = u1.Id + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + + store.Must(ss.Channel().SaveMember(&m1)) + store.Must(ss.Channel().SaveMember(&m2)) + store.Must(ss.Channel().SaveMember(&m3)) + + if r1 := <-ss.User().GetProfilesInChannel(c1.Id, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r2 := <-ss.User().GetProfilesInChannel(c2.Id, 0, 1); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.([]*model.User)) != 1 { + t.Fatal("should have returned only 1 user") + } + } +} + +func testUserStoreGetProfilesWithoutTeam(t *testing.T, ss store.Store) { + teamId := model.NewId() + + // These usernames need to appear in the first 100 users for this to work + + u1 := &model.User{} + u1.Username = "a000000000" + model.NewId() + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + defer ss.User().PermanentDelete(u1.Id) + + u2 := &model.User{} + u2.Username = "a000000001" + model.NewId() + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + defer ss.User().PermanentDelete(u2.Id) + + if r1 := <-ss.User().GetProfilesWithoutTeam(0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + + found1 := false + found2 := false + for _, u := range users { + if u.Id == u1.Id { + found1 = true + } else if u.Id == u2.Id { + found2 = true + } + } + + if found1 { + t.Fatal("shouldn't have returned user on team") + } else if !found2 { + t.Fatal("should've returned user without any teams") + } + } +} + +func testUserStoreGetAllProfilesInChannel(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + c1 := model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Profiles in channel" + c1.Name = "profiles-" + model.NewId() + c1.Type = model.CHANNEL_OPEN + + c2 := model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Profiles in private" + c2.Name = "profiles-" + model.NewId() + c2.Type = model.CHANNEL_PRIVATE + + store.Must(ss.Channel().Save(&c1)) + store.Must(ss.Channel().Save(&c2)) + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = c1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + m3 := model.ChannelMember{} + m3.ChannelId = c2.Id + m3.UserId = u1.Id + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + + store.Must(ss.Channel().SaveMember(&m1)) + store.Must(ss.Channel().SaveMember(&m2)) + store.Must(ss.Channel().SaveMember(&m3)) + + if r1 := <-ss.User().GetAllProfilesInChannel(c1.Id, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.(map[string]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + if users[u1.Id].Id != u1.Id { + t.Fatal("invalid returned user") + } + } + + if r2 := <-ss.User().GetAllProfilesInChannel(c2.Id, false); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.(map[string]*model.User)) != 1 { + t.Fatal("should have returned empty map") + } + } + + if r2 := <-ss.User().GetAllProfilesInChannel(c2.Id, true); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.(map[string]*model.User)) != 1 { + t.Fatal("should have returned empty map") + } + } + + if r2 := <-ss.User().GetAllProfilesInChannel(c2.Id, true); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.(map[string]*model.User)) != 1 { + t.Fatal("should have returned empty map") + } + } + + ss.User().InvalidateProfilesInChannelCacheByUser(u1.Id) + ss.User().InvalidateProfilesInChannelCache(c2.Id) +} + +func testUserStoreGetProfilesNotInChannel(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + c1 := model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Profiles in channel" + c1.Name = "profiles-" + model.NewId() + c1.Type = model.CHANNEL_OPEN + + c2 := model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Profiles in private" + c2.Name = "profiles-" + model.NewId() + c2.Type = model.CHANNEL_PRIVATE + + store.Must(ss.Channel().Save(&c1)) + store.Must(ss.Channel().Save(&c2)) + + if r1 := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r2 := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, 0, 100); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.([]*model.User)) != 2 { + t.Fatal("invalid returned users") + } + } + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = c1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + m3 := model.ChannelMember{} + m3.ChannelId = c2.Id + m3.UserId = u1.Id + m3.NotifyProps = model.GetDefaultChannelNotifyProps() + + store.Must(ss.Channel().SaveMember(&m1)) + store.Must(ss.Channel().SaveMember(&m2)) + store.Must(ss.Channel().SaveMember(&m3)) + + if r1 := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 0 { + t.Fatal("invalid returned users") + } + } + + if r2 := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, 0, 100); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.([]*model.User)) != 1 { + t.Fatal("should have had 1 user not in channel") + } + } +} + +func testUserStoreGetProfilesByIds(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id}, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id}, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id, u2.Id}, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id, u2.Id}, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id, u2.Id}, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r1 := <-ss.User().GetProfileByIds([]string{u1.Id}, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users") + } + + found := false + for _, u := range users { + if u.Id == u1.Id { + found = true + } + } + + if !found { + t.Fatal("missing user") + } + } + + if r2 := <-ss.User().GetProfiles("123", 0, 100); r2.Err != nil { + t.Fatal(r2.Err) + } else { + if len(r2.Data.([]*model.User)) != 0 { + t.Fatal("should have returned empty array") + } + } +} + +func testUserStoreGetProfilesByUsernames(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Username = "username1" + model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Username = "username2" + model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + if r1 := <-ss.User().GetProfilesByUsernames([]string{u1.Username, u2.Username}, teamId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + if users[0].Id != u1.Id && users[1].Id != u1.Id { + t.Fatal("invalid returned user 1") + } + + if users[0].Id != u2.Id && users[1].Id != u2.Id { + t.Fatal("invalid returned user 2") + } + } + + if r1 := <-ss.User().GetProfilesByUsernames([]string{u1.Username}, teamId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users") + } + + if users[0].Id != u1.Id { + t.Fatal("invalid returned user") + } + } + + team2Id := model.NewId() + + u3 := &model.User{} + u3.Email = model.NewId() + u3.Username = "username3" + model.NewId() + store.Must(ss.User().Save(u3)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: team2Id, UserId: u3.Id})) + + if r1 := <-ss.User().GetProfilesByUsernames([]string{u1.Username, u3.Username}, ""); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 2 { + t.Fatal("invalid returned users") + } + + if users[0].Id != u1.Id && users[1].Id != u1.Id { + t.Fatal("invalid returned user 1") + } + + if users[0].Id != u3.Id && users[1].Id != u3.Id { + t.Fatal("invalid returned user 3") + } + } + + if r1 := <-ss.User().GetProfilesByUsernames([]string{u1.Username, u3.Username}, teamId); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + if len(users) != 1 { + t.Fatal("invalid returned users") + } + + if users[0].Id != u1.Id { + t.Fatal("invalid returned user") + } + } +} + +func testUserStoreGetSystemAdminProfiles(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Roles = model.ROLE_SYSTEM_USER.Id + " " + model.ROLE_SYSTEM_ADMIN.Id + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + if r1 := <-ss.User().GetSystemAdminProfiles(); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.(map[string]*model.User) + if len(users) <= 0 { + t.Fatal("invalid returned system admin users") + } + } +} + +func testUserStoreGetByEmail(t *testing.T, ss store.Store) { + teamid := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamid, UserId: u1.Id})) + + if err := (<-ss.User().GetByEmail(u1.Email)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.User().GetByEmail("")).Err; err == nil { + t.Fatal("Should have failed because of missing email") + } +} + +func testUserStoreGetByAuthData(t *testing.T, ss store.Store) { + teamId := model.NewId() + + auth := "123" + model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + u1.AuthData = &auth + u1.AuthService = "service" + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + if err := (<-ss.User().GetByAuth(u1.AuthData, u1.AuthService)).Err; err != nil { + t.Fatal(err) + } + + rauth := "" + if err := (<-ss.User().GetByAuth(&rauth, "")).Err; err == nil { + t.Fatal("Should have failed because of missing auth data") + } +} + +func testUserStoreGetByUsername(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + u1.Username = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + if err := (<-ss.User().GetByUsername(u1.Username)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.User().GetByUsername("")).Err; err == nil { + t.Fatal("Should have failed because of missing username") + } +} + +func testUserStoreGetForLogin(t *testing.T, ss store.Store) { + auth := model.NewId() + + u1 := &model.User{ + Email: model.NewId(), + Username: model.NewId(), + AuthService: model.USER_AUTH_SERVICE_GITLAB, + AuthData: &auth, + } + store.Must(ss.User().Save(u1)) + + auth2 := model.NewId() + + u2 := &model.User{ + Email: model.NewId(), + Username: model.NewId(), + AuthService: model.USER_AUTH_SERVICE_LDAP, + AuthData: &auth2, + } + store.Must(ss.User().Save(u2)) + + if result := <-ss.User().GetForLogin(u1.Username, true, true, true); result.Err != nil { + t.Fatal("Should have gotten user by username", result.Err) + } else if result.Data.(*model.User).Id != u1.Id { + t.Fatal("Should have gotten user1 by username") + } + + if result := <-ss.User().GetForLogin(u1.Email, true, true, true); result.Err != nil { + t.Fatal("Should have gotten user by email", result.Err) + } else if result.Data.(*model.User).Id != u1.Id { + t.Fatal("Should have gotten user1 by email") + } + + if result := <-ss.User().GetForLogin(*u2.AuthData, true, true, true); result.Err != nil { + t.Fatal("Should have gotten user by AD/LDAP AuthData", result.Err) + } else if result.Data.(*model.User).Id != u2.Id { + t.Fatal("Should have gotten user2 by AD/LDAP AuthData") + } + + // prevent getting user by AuthData when they're not an LDAP user + if result := <-ss.User().GetForLogin(*u1.AuthData, true, true, true); result.Err == nil { + t.Fatal("Should not have gotten user by non-AD/LDAP AuthData") + } + + // prevent getting user when different login methods are disabled + if result := <-ss.User().GetForLogin(u1.Username, false, true, true); result.Err == nil { + t.Fatal("Should have failed to get user1 by username") + } + + if result := <-ss.User().GetForLogin(u1.Email, true, false, true); result.Err == nil { + t.Fatal("Should have failed to get user1 by email") + } + + if result := <-ss.User().GetForLogin(*u2.AuthData, true, true, false); result.Err == nil { + t.Fatal("Should have failed to get user3 by AD/LDAP AuthData") + } + + auth3 := model.NewId() + + // test a special case where two users will have conflicting login information so we throw a special error + u3 := &model.User{ + Email: model.NewId(), + Username: model.NewId(), + AuthService: model.USER_AUTH_SERVICE_LDAP, + AuthData: &auth3, + } + store.Must(ss.User().Save(u3)) + + u4 := &model.User{ + Email: model.NewId(), + Username: model.NewId(), + AuthService: model.USER_AUTH_SERVICE_LDAP, + AuthData: &u3.Username, + } + store.Must(ss.User().Save(u4)) + + if err := (<-ss.User().GetForLogin(u3.Username, true, true, true)).Err; err == nil { + t.Fatal("Should have failed to get users with conflicting login information") + } +} + +func testUserStoreUpdatePassword(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + hashedPassword := model.HashPassword("newpwd") + + if err := (<-ss.User().UpdatePassword(u1.Id, hashedPassword)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.User().GetByEmail(u1.Email); r1.Err != nil { + t.Fatal(r1.Err) + } else { + user := r1.Data.(*model.User) + if user.Password != hashedPassword { + t.Fatal("Password was not updated correctly") + } + } +} + +func testUserStoreDelete(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id})) + + if err := (<-ss.User().PermanentDelete(u1.Id)).Err; err != nil { + t.Fatal(err) + } +} + +func testUserStoreUpdateAuthData(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + service := "someservice" + authData := model.NewId() + + if err := (<-ss.User().UpdateAuthData(u1.Id, service, &authData, "", true)).Err; err != nil { + t.Fatal(err) + } + + if r1 := <-ss.User().GetByEmail(u1.Email); r1.Err != nil { + t.Fatal(r1.Err) + } else { + user := r1.Data.(*model.User) + if user.AuthService != service { + t.Fatal("AuthService was not updated correctly") + } + if *user.AuthData != authData { + t.Fatal("AuthData was not updated correctly") + } + if user.Password != "" { + t.Fatal("Password was not cleared properly") + } + } +} + +func testUserUnreadCount(t *testing.T, ss store.Store) { + teamId := model.NewId() + + c1 := model.Channel{} + c1.TeamId = teamId + c1.DisplayName = "Unread Messages" + c1.Name = "unread-messages-" + model.NewId() + c1.Type = model.CHANNEL_OPEN + + c2 := model.Channel{} + c2.TeamId = teamId + c2.DisplayName = "Unread Direct" + c2.Name = "unread-direct-" + model.NewId() + c2.Type = model.CHANNEL_DIRECT + + u1 := &model.User{} + u1.Username = "user1" + model.NewId() + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + + u2 := &model.User{} + u2.Email = model.NewId() + u2.Username = "user2" + model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + + if err := (<-ss.Channel().Save(&c1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + m1 := model.ChannelMember{} + m1.ChannelId = c1.Id + m1.UserId = u1.Id + m1.NotifyProps = model.GetDefaultChannelNotifyProps() + + m2 := model.ChannelMember{} + m2.ChannelId = c1.Id + m2.UserId = u2.Id + m2.NotifyProps = model.GetDefaultChannelNotifyProps() + + store.Must(ss.Channel().SaveMember(&m1)) + store.Must(ss.Channel().SaveMember(&m2)) + + m1.ChannelId = c2.Id + m2.ChannelId = c2.Id + + if err := (<-ss.Channel().SaveDirectChannel(&c2, &m1, &m2)).Err; err != nil { + t.Fatal("couldn't save direct channel", err) + } + + p1 := model.Post{} + p1.ChannelId = c1.Id + p1.UserId = u1.Id + p1.Message = "this is a message for @" + u2.Username + + // Post one message with mention to open channel + store.Must(ss.Post().Save(&p1)) + store.Must(ss.Channel().IncrementMentionCount(c1.Id, u2.Id)) + + // Post 2 messages without mention to direct channel + p2 := model.Post{} + p2.ChannelId = c2.Id + p2.UserId = u1.Id + p2.Message = "first message" + store.Must(ss.Post().Save(&p2)) + store.Must(ss.Channel().IncrementMentionCount(c2.Id, u2.Id)) + + p3 := model.Post{} + p3.ChannelId = c2.Id + p3.UserId = u1.Id + p3.Message = "second message" + store.Must(ss.Post().Save(&p3)) + store.Must(ss.Channel().IncrementMentionCount(c2.Id, u2.Id)) + + badge := (<-ss.User().GetUnreadCount(u2.Id)).Data.(int64) + if badge != 3 { + t.Fatal("should have 3 unread messages") + } + + badge = (<-ss.User().GetUnreadCountForChannel(u2.Id, c1.Id)).Data.(int64) + if badge != 1 { + t.Fatal("should have 1 unread messages for that channel") + } + + badge = (<-ss.User().GetUnreadCountForChannel(u2.Id, c2.Id)).Data.(int64) + if badge != 2 { + t.Fatal("should have 2 unread messages for that channel") + } +} + +func testUserStoreUpdateMfaSecret(t *testing.T, ss store.Store) { + u1 := model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(&u1)) + + time.Sleep(100 * time.Millisecond) + + if err := (<-ss.User().UpdateMfaSecret(u1.Id, "12345")).Err; err != nil { + t.Fatal(err) + } + + // should pass, no update will occur though + if err := (<-ss.User().UpdateMfaSecret("junk", "12345")).Err; err != nil { + t.Fatal(err) + } +} + +func testUserStoreUpdateMfaActive(t *testing.T, ss store.Store) { + u1 := model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(&u1)) + + time.Sleep(100 * time.Millisecond) + + if err := (<-ss.User().UpdateMfaActive(u1.Id, true)).Err; err != nil { + t.Fatal(err) + } + + if err := (<-ss.User().UpdateMfaActive(u1.Id, false)).Err; err != nil { + t.Fatal(err) + } + + // should pass, no update will occur though + if err := (<-ss.User().UpdateMfaActive("junk", true)).Err; err != nil { + t.Fatal(err) + } +} + +func testUserStoreGetRecentlyActiveUsersForTeam(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Status().SaveOrUpdate(&model.Status{UserId: u1.Id, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""})) + tid := model.NewId() + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u1.Id})) + + if r1 := <-ss.User().GetRecentlyActiveUsersForTeam(tid, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } +} + +func testUserStoreGetNewUsersForTeam(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Status().SaveOrUpdate(&model.Status{UserId: u1.Id, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""})) + tid := model.NewId() + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u1.Id})) + + if r1 := <-ss.User().GetNewUsersForTeam(tid, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } +} + +func testUserStoreSearch(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Username = "jimbo" + model.NewId() + u1.FirstName = "Tim" + u1.LastName = "Bill" + u1.Nickname = "Rob" + u1.Email = "harold" + model.NewId() + "@simulator.amazonses.com" + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Username = "jim-bobby" + model.NewId() + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + + u3 := &model.User{} + u3.Username = "jimbo" + model.NewId() + u3.Email = model.NewId() + u3.DeleteAt = 1 + store.Must(ss.User().Save(u3)) + + u5 := &model.User{} + u5.Username = "yu" + model.NewId() + u5.FirstName = "En" + u5.LastName = "Yu" + u5.Nickname = "enyu" + u5.Email = model.NewId() + "@simulator.amazonses.com" + store.Must(ss.User().Save(u5)) + + u6 := &model.User{} + u6.Username = "underscore" + model.NewId() + u6.FirstName = "Du_" + u6.LastName = "_DE" + u6.Nickname = "lodash" + u6.Email = model.NewId() + "@simulator.amazonses.com" + store.Must(ss.User().Save(u6)) + + tid := model.NewId() + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u1.Id})) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u2.Id})) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u3.Id})) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u5.Id})) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u6.Id})) + + searchOptions := map[string]bool{} + searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true + + if r1 := <-ss.User().Search(tid, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + found2 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + + if profile.Id == u3.Id { + found2 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + + if found2 { + t.Fatal("should not have found inactive user") + } + } + + if r1 := <-ss.User().Search(tid, "en", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + for _, profile := range profiles { + if profile.Id == u5.Id { + found1 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + } + + searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = false + + if r1 := <-ss.User().Search(tid, u1.Email, searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + } + + searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true + + // * should be treated as a space + if r1 := <-ss.User().Search(tid, "jimb*", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + found2 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + + if profile.Id == u3.Id { + found2 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + + if found2 { + t.Fatal("should not have found inactive user") + } + } + + if r1 := <-ss.User().Search(tid, "harol", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + } + + if found1 { + t.Fatal("should not have found user") + } + } + + // % should be escaped and searched for. + if r1 := <-ss.User().Search(tid, "h%", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + if len(profiles) != 0 { + t.Fatal("shouldn't have found anything") + } + } + + // "_" should be properly escaped and searched for. + if r1 := <-ss.User().Search(tid, "h_", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + if len(profiles) != 0 { + t.Fatal("shouldn't have found anything") + } + } + if r1 := <-ss.User().Search(tid, "Du_", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found6 := false + for _, profile := range profiles { + if profile.Id == u6.Id { + found6 = true + } + } + + if !found6 { + t.Fatal("should have found user") + } + } + if r1 := <-ss.User().Search(tid, "_dE", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found6 := false + for _, profile := range profiles { + if profile.Id == u6.Id { + found6 = true + } + } + + if !found6 { + t.Fatal("should have found user") + } + } + + searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = true + + if r1 := <-ss.User().Search(tid, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + found2 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + + if profile.Id == u3.Id { + found2 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + + if !found2 { + t.Fatal("should have found inactive user") + } + } + + searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = false + + if r1 := <-ss.User().Search(tid, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search("", "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search("", "jim-bobb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + t.Log(profile.Username) + if profile.Id == u2.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search(tid, "", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } + + c1 := model.Channel{} + c1.TeamId = tid + c1.DisplayName = "NameName" + c1.Name = "zz" + model.NewId() + "b" + c1.Type = model.CHANNEL_OPEN + c1 = *store.Must(ss.Channel().Save(&c1)).(*model.Channel) + + if r1 := <-ss.User().SearchNotInChannel(tid, c1.Id, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().SearchNotInChannel("", c1.Id, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().SearchNotInChannel("junk", c1.Id, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if found { + t.Fatal("should not have found user") + } + } + + if r1 := <-ss.User().SearchInChannel(c1.Id, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if found { + t.Fatal("should not have found user") + } + } + + store.Must(ss.Channel().SaveMember(&model.ChannelMember{ChannelId: c1.Id, UserId: u1.Id, NotifyProps: model.GetDefaultChannelNotifyProps()})) + + if r1 := <-ss.User().SearchInChannel(c1.Id, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + searchOptions = map[string]bool{} + + if r1 := <-ss.User().Search(tid, "harol", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found1 := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } + } + + if !found1 { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search(tid, "Tim", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search(tid, "Bill", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().Search(tid, "Rob", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + // Search Users not in Team. + u4 := &model.User{} + u4.Username = "simon" + model.NewId() + u4.Email = model.NewId() + u4.DeleteAt = 0 + store.Must(ss.User().Save(u4)) + + if r1 := <-ss.User().SearchNotInTeam(tid, "simo", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u4.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-ss.User().SearchNotInTeam(tid, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if found { + t.Fatal("should not have found user") + } + } + + // Check SearchNotInTeam finds previously deleted team members. + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u4.Id})) + + if r1 := <-ss.User().SearchNotInTeam(tid, "simo", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u4.Id { + found = true + break + } + } + + if found { + t.Fatal("should not have found user") + } + } + + store.Must(ss.Team().UpdateMember(&model.TeamMember{TeamId: tid, UserId: u4.Id, DeleteAt: model.GetMillis() - 1000})) + if r1 := <-ss.User().SearchNotInTeam(tid, "simo", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u4.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } +} + +func testUserStoreSearchWithoutTeam(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Username = "jimbo" + model.NewId() + u1.FirstName = "Tim" + u1.LastName = "Bill" + u1.Nickname = "Rob" + u1.Email = "harold" + model.NewId() + "@simulator.amazonses.com" + store.Must(ss.User().Save(u1)) + + u2 := &model.User{} + u2.Username = "jim-bobby" + model.NewId() + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + + u3 := &model.User{} + u3.Username = "jimbo" + model.NewId() + u3.Email = model.NewId() + u3.DeleteAt = 1 + store.Must(ss.User().Save(u3)) + + tid := model.NewId() + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u3.Id})) + + searchOptions := map[string]bool{} + searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true + + if r1 := <-ss.User().SearchWithoutTeam("", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } + + if r1 := <-ss.User().SearchWithoutTeam("jim", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + + found1 := false + found2 := false + found3 := false + + for _, profile := range profiles { + if profile.Id == u1.Id { + found1 = true + } else if profile.Id == u2.Id { + found2 = true + } else if profile.Id == u3.Id { + found3 = true + } + } + + if !found1 { + t.Fatal("should have found user1") + } else if !found2 { + t.Fatal("should have found user2") + } else if found3 { + t.Fatal("should not have found user3") + } + } +} + +func testUserStoreAnalyticsGetInactiveUsersCount(t *testing.T, ss store.Store) { + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + + var count int64 + + if result := <-ss.User().AnalyticsGetInactiveUsersCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + count = result.Data.(int64) + } + + u2 := &model.User{} + u2.Email = model.NewId() + u2.DeleteAt = model.GetMillis() + store.Must(ss.User().Save(u2)) + + if result := <-ss.User().AnalyticsGetInactiveUsersCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + newCount := result.Data.(int64) + if count != newCount-1 { + t.Fatal("Expected 1 more inactive users but found otherwise.", count, newCount) + } + } +} + +func testUserStoreAnalyticsGetSystemAdminCount(t *testing.T, ss store.Store) { + var countBefore int64 + if result := <-ss.User().AnalyticsGetSystemAdminCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + countBefore = result.Data.(int64) + } + + u1 := model.User{} + u1.Email = model.NewId() + u1.Username = model.NewId() + u1.Roles = "system_user system_admin" + + u2 := model.User{} + u2.Email = model.NewId() + u2.Username = model.NewId() + + if err := (<-ss.User().Save(&u1)).Err; err != nil { + t.Fatal("couldn't save user", err) + } + + if err := (<-ss.User().Save(&u2)).Err; err != nil { + t.Fatal("couldn't save user", err) + } + + if result := <-ss.User().AnalyticsGetSystemAdminCount(); result.Err != nil { + t.Fatal(result.Err) + } else { + // We expect to find 1 more system admin than there was at the start of this test function. + if count := result.Data.(int64); count != countBefore+1 { + t.Fatal("Did not get the expected number of system admins. Expected, got: ", countBefore+1, count) + } + } +} + +func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) { + teamId := model.NewId() + + u1 := &model.User{} + u1.Email = model.NewId() + store.Must(ss.User().Save(u1)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id})) + store.Must(ss.User().UpdateUpdateAt(u1.Id)) + + u2 := &model.User{} + u2.Email = model.NewId() + store.Must(ss.User().Save(u2)) + store.Must(ss.User().UpdateUpdateAt(u2.Id)) + + var initialUsersNotInTeam int + var etag1, etag2, etag3 string + + if er1 := <-ss.User().GetEtagForProfilesNotInTeam(teamId); er1.Err != nil { + t.Fatal(er1.Err) + } else { + etag1 = er1.Data.(string) + } + + if r1 := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000); r1.Err != nil { + t.Fatal(r1.Err) + } else { + users := r1.Data.([]*model.User) + initialUsersNotInTeam = len(users) + if initialUsersNotInTeam < 1 { + t.Fatalf("Should be at least 1 user not in the team") + } + + found := false + for _, u := range users { + if u.Id == u2.Id { + found = true + } + if u.Id == u1.Id { + t.Fatalf("Should not have found user1") + } + } + + if !found { + t.Fatal("missing user2") + } + } + + time.Sleep(time.Millisecond * 10) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id})) + store.Must(ss.User().UpdateUpdateAt(u2.Id)) + + if er2 := <-ss.User().GetEtagForProfilesNotInTeam(teamId); er2.Err != nil { + t.Fatal(er2.Err) + } else { + etag2 = er2.Data.(string) + if etag1 == etag2 { + t.Fatalf("etag should have changed") + } + } + + if r2 := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000); r2.Err != nil { + t.Fatal(r2.Err) + } else { + users := r2.Data.([]*model.User) + + if len(users) != initialUsersNotInTeam-1 { + t.Fatalf("Should be one less user not in team") + } + + for _, u := range users { + if u.Id == u2.Id { + t.Fatalf("Should not have found user2") + } + if u.Id == u1.Id { + t.Fatalf("Should not have found user1") + } + } + } + + time.Sleep(time.Millisecond * 10) + store.Must(ss.Team().RemoveMember(teamId, u1.Id)) + store.Must(ss.Team().RemoveMember(teamId, u2.Id)) + store.Must(ss.User().UpdateUpdateAt(u1.Id)) + store.Must(ss.User().UpdateUpdateAt(u2.Id)) + + if er3 := <-ss.User().GetEtagForProfilesNotInTeam(teamId); er3.Err != nil { + t.Fatal(er3.Err) + } else { + etag3 = er3.Data.(string) + t.Log(etag3) + if etag1 == etag3 || etag3 == etag2 { + t.Fatalf("etag should have changed") + } + } + + if r3 := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000); r3.Err != nil { + t.Fatal(r3.Err) + } else { + users := r3.Data.([]*model.User) + found1, found2 := false, false + for _, u := range users { + if u.Id == u2.Id { + found2 = true + } + if u.Id == u1.Id { + found1 = true + } + } + + if !found1 || !found2 { + t.Fatal("missing user1 or user2") + } + } + + time.Sleep(time.Millisecond * 10) + u3 := &model.User{} + u3.Email = model.NewId() + store.Must(ss.User().Save(u3)) + store.Must(ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id})) + store.Must(ss.User().UpdateUpdateAt(u3.Id)) + + if er4 := <-ss.User().GetEtagForProfilesNotInTeam(teamId); er4.Err != nil { + t.Fatal(er4.Err) + } else { + etag4 := er4.Data.(string) + t.Log(etag4) + if etag4 != etag3 { + t.Fatalf("etag should be the same") + } + } +} diff --git a/store/storetest/webhook_store.go b/store/storetest/webhook_store.go new file mode 100644 index 000000000..7f87ec29d --- /dev/null +++ b/store/storetest/webhook_store.go @@ -0,0 +1,507 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package storetest + +import ( + "testing" + "time" + + "net/http" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +func TestWebhookStore(t *testing.T, ss store.Store) { + t.Run("SaveIncoming", func(t *testing.T) { testWebhookStoreSaveIncoming(t, ss) }) + t.Run("UpdateIncoming", func(t *testing.T) { testWebhookStoreUpdateIncoming(t, ss) }) + t.Run("GetIncoming", func(t *testing.T) { testWebhookStoreGetIncoming(t, ss) }) + t.Run("GetIncomingList", func(t *testing.T) { testWebhookStoreGetIncomingList(t, ss) }) + t.Run("GetIncomingByTeam", func(t *testing.T) { testWebhookStoreGetIncomingByTeam(t, ss) }) + t.Run("DeleteIncoming", func(t *testing.T) { testWebhookStoreDeleteIncoming(t, ss) }) + t.Run("DeleteIncomingByChannel", func(t *testing.T) { testWebhookStoreDeleteIncomingByChannel(t, ss) }) + t.Run("DeleteIncomingByUser", func(t *testing.T) { testWebhookStoreDeleteIncomingByUser(t, ss) }) + t.Run("SaveOutgoing", func(t *testing.T) { testWebhookStoreSaveOutgoing(t, ss) }) + t.Run("GetOutgoing", func(t *testing.T) { testWebhookStoreGetOutgoing(t, ss) }) + t.Run("GetOutgoingList", func(t *testing.T) { testWebhookStoreGetOutgoingList(t, ss) }) + t.Run("GetOutgoingByChannel", func(t *testing.T) { testWebhookStoreGetOutgoingByChannel(t, ss) }) + t.Run("GetOutgoingByTeam", func(t *testing.T) { testWebhookStoreGetOutgoingByTeam(t, ss) }) + t.Run("DeleteOutgoing", func(t *testing.T) { testWebhookStoreDeleteOutgoing(t, ss) }) + t.Run("DeleteOutgoingByChannel", func(t *testing.T) { testWebhookStoreDeleteOutgoingByChannel(t, ss) }) + t.Run("DeleteOutgoingByUser", func(t *testing.T) { testWebhookStoreDeleteOutgoingByUser(t, ss) }) + t.Run("UpdateOutgoing", func(t *testing.T) { testWebhookStoreUpdateOutgoing(t, ss) }) + t.Run("CountIncoming", func(t *testing.T) { testWebhookStoreCountIncoming(t, ss) }) + t.Run("CountOutgoing", func(t *testing.T) { testWebhookStoreCountOutgoing(t, ss) }) +} + +func testWebhookStoreSaveIncoming(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + + if err := (<-ss.Webhook().SaveIncoming(o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Webhook().SaveIncoming(o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func testWebhookStoreUpdateIncoming(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + previousUpdatedAt := o1.UpdateAt + + o1.DisplayName = "TestHook" + time.Sleep(10 * time.Millisecond) + + if result := (<-ss.Webhook().UpdateIncoming(o1)); result.Err != nil { + t.Fatal("updation of incoming hook failed", result.Err) + } else { + if result.Data.(*model.IncomingWebhook).UpdateAt == previousUpdatedAt { + t.Fatal("should have updated the UpdatedAt of the hook") + } + + if result.Data.(*model.IncomingWebhook).DisplayName != "TestHook" { + t.Fatal("display name is not updated") + } + } +} + +func testWebhookStoreGetIncoming(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncoming(o1.Id, false); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r1 := <-ss.Webhook().GetIncoming(o1.Id, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if err := (<-ss.Webhook().GetIncoming("123", false)).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + if err := (<-ss.Webhook().GetIncoming("123", true)).Err; err == nil { + t.Fatal("Missing id should have failed") + } + + if err := (<-ss.Webhook().GetIncoming("123", true)).Err; err.StatusCode != http.StatusNotFound { + t.Fatal("Should have set the status as not found for missing id") + } +} + +func testWebhookStoreGetIncomingList(t *testing.T, ss store.Store) { + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncomingList(0, 1000); r1.Err != nil { + t.Fatal(r1.Err) + } else { + found := false + hooks := r1.Data.([]*model.IncomingWebhook) + for _, hook := range hooks { + if hook.Id == o1.Id { + found = true + } + } + if !found { + t.Fatal("missing webhook") + } + } + + if result := <-ss.Webhook().GetIncomingList(0, 1); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.IncomingWebhook)) != 1 { + t.Fatal("only 1 should be returned") + } + } +} + +func testWebhookStoreGetIncomingByTeam(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncomingByTeam(o1.TeamId, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.IncomingWebhook)[0].CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if result := <-ss.Webhook().GetIncomingByTeam("123", 0, 100); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.IncomingWebhook)) != 0 { + t.Fatal("no webhooks should have returned") + } + } +} + +func testWebhookStoreDeleteIncoming(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncoming(o1.Id, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().DeleteIncoming(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetIncoming(o1.Id, true)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreDeleteIncomingByChannel(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncoming(o1.Id, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().PermanentDeleteIncomingByChannel(o1.ChannelId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetIncoming(o1.Id, true)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreDeleteIncomingByUser(t *testing.T, ss store.Store) { + o1 := buildIncomingWebhook() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r1 := <-ss.Webhook().GetIncoming(o1.Id, true); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.IncomingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().PermanentDeleteIncomingByUser(o1.UserId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetIncoming(o1.Id, true)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func buildIncomingWebhook() *model.IncomingWebhook { + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + return o1 +} + +func testWebhookStoreSaveOutgoing(t *testing.T, ss store.Store) { + o1 := model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + if err := (<-ss.Webhook().SaveOutgoing(&o1)).Err; err != nil { + t.Fatal("couldn't save item", err) + } + + if err := (<-ss.Webhook().SaveOutgoing(&o1)).Err; err == nil { + t.Fatal("shouldn't be able to update from save") + } +} + +func testWebhookStoreGetOutgoing(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoing(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.OutgoingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if err := (<-ss.Webhook().GetOutgoing("123")).Err; err == nil { + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreGetOutgoingList(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + o2 := &model.OutgoingWebhook{} + o2.ChannelId = model.NewId() + o2.CreatorId = model.NewId() + o2.TeamId = model.NewId() + o2.CallbackURLs = []string{"http://nowhere.com/"} + + o2 = (<-ss.Webhook().SaveOutgoing(o2)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoingList(0, 1000); r1.Err != nil { + t.Fatal(r1.Err) + } else { + hooks := r1.Data.([]*model.OutgoingWebhook) + found1 := false + found2 := false + + for _, hook := range hooks { + if hook.CreateAt != o1.CreateAt { + found1 = true + } + + if hook.CreateAt != o2.CreateAt { + found2 = true + } + } + + if !found1 { + t.Fatal("missing hook1") + } + if !found2 { + t.Fatal("missing hook2") + } + } + + if result := <-ss.Webhook().GetOutgoingList(0, 2); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.OutgoingWebhook)) != 2 { + t.Fatal("wrong number of hooks returned") + } + } +} + +func testWebhookStoreGetOutgoingByChannel(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoingByChannel(o1.ChannelId, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if result := <-ss.Webhook().GetOutgoingByChannel("123", -1, -1); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.OutgoingWebhook)) != 0 { + t.Fatal("no webhooks should have returned") + } + } +} + +func testWebhookStoreGetOutgoingByTeam(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoingByTeam(o1.TeamId, 0, 100); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if result := <-ss.Webhook().GetOutgoingByTeam("123", -1, -1); result.Err != nil { + t.Fatal(result.Err) + } else { + if len(result.Data.([]*model.OutgoingWebhook)) != 0 { + t.Fatal("no webhooks should have returned") + } + } +} + +func testWebhookStoreDeleteOutgoing(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoing(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.OutgoingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().DeleteOutgoing(o1.Id, model.GetMillis()); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetOutgoing(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreDeleteOutgoingByChannel(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoing(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.OutgoingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().PermanentDeleteOutgoingByChannel(o1.ChannelId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetOutgoing(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreDeleteOutgoingByUser(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r1 := <-ss.Webhook().GetOutgoing(o1.Id); r1.Err != nil { + t.Fatal(r1.Err) + } else { + if r1.Data.(*model.OutgoingWebhook).CreateAt != o1.CreateAt { + t.Fatal("invalid returned webhook") + } + } + + if r2 := <-ss.Webhook().PermanentDeleteOutgoingByUser(o1.CreatorId); r2.Err != nil { + t.Fatal(r2.Err) + } + + if r3 := (<-ss.Webhook().GetOutgoing(o1.Id)); r3.Err == nil { + t.Log(r3.Data) + t.Fatal("Missing id should have failed") + } +} + +func testWebhookStoreUpdateOutgoing(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + o1.Token = model.NewId() + + if r2 := <-ss.Webhook().UpdateOutgoing(o1); r2.Err != nil { + t.Fatal(r2.Err) + } +} + +func testWebhookStoreCountIncoming(t *testing.T, ss store.Store) { + o1 := &model.IncomingWebhook{} + o1.ChannelId = model.NewId() + o1.UserId = model.NewId() + o1.TeamId = model.NewId() + + o1 = (<-ss.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook) + + if r := <-ss.Webhook().AnalyticsIncomingCount(""); r.Err != nil { + t.Fatal(r.Err) + } else { + if r.Data.(int64) == 0 { + t.Fatal("should have at least 1 incoming hook") + } + } +} + +func testWebhookStoreCountOutgoing(t *testing.T, ss store.Store) { + o1 := &model.OutgoingWebhook{} + o1.ChannelId = model.NewId() + o1.CreatorId = model.NewId() + o1.TeamId = model.NewId() + o1.CallbackURLs = []string{"http://nowhere.com/"} + + o1 = (<-ss.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook) + + if r := <-ss.Webhook().AnalyticsOutgoingCount(""); r.Err != nil { + t.Fatal(r.Err) + } else { + if r.Data.(int64) == 0 { + t.Fatal("should have at least 1 outgoing hook") + } + } +} |