// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package localcachelayer import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/plugin/plugintest/mock" "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/store/storetest" "github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks" cmocks "github.com/mattermost/mattermost/server/v8/platform/services/cache/mocks" ) func TestChannelStore(t *testing.T) { StoreTestWithSqlStore(t, storetest.TestReactionStore) } func TestChannelStoreChannelMemberCountsCache(t *testing.T) { countResult := int64(10) logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) count, err := cachedStore.Channel().GetMemberCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) count, err = cachedStore.Channel().GetMemberCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) }) t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) cachedStore.Channel().GetMemberCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 2) }) t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetMemberCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 2) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 2) }) t.Run("first call with GetMemberCountFromCache not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) count := cachedStore.Channel().GetMemberCountFromCache("id") assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) count = cachedStore.Channel().GetMemberCountFromCache("id") assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) }) t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) cachedStore.Channel().ClearCaches() cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 2) }) t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 1) cachedStore.Channel().InvalidateMemberCount("id") cachedStore.Channel().GetMemberCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMemberCount", 2) }) } func TestChannelStoreChannelsMemberCountCache(t *testing.T) { channelsCountResult := map[string]int64{ "channel1": 10, "channel2": 20, } logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) channelsCount, err := cachedStore.Channel().GetChannelsMemberCount([]string{"channel1", "channel2"}) require.NoError(t, err) assert.Equal(t, channelsCount, channelsCountResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetChannelsMemberCount", 1) channelsCount, err = cachedStore.Channel().GetChannelsMemberCount([]string{"channel1", "channel2"}) require.NoError(t, err) assert.Equal(t, channelsCount, channelsCountResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetChannelsMemberCount", 1) }) t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetChannelsMemberCount([]string{"channel1", "channel2"}) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetChannelsMemberCount", 1) cachedStore.Channel().InvalidateMemberCount("channel1") cachedStore.Channel().InvalidateMemberCount("channel2") cachedStore.Channel().GetChannelsMemberCount([]string{"channel1", "channel2"}) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetChannelsMemberCount", 2) }) } func TestChannelStoreChannelPinnedPostsCountsCache(t *testing.T) { countResult := int64(10) logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) count, err := cachedStore.Channel().GetPinnedPostCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) count, err = cachedStore.Channel().GetPinnedPostCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) }) t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) cachedStore.Channel().GetPinnedPostCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 2) }) t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetPinnedPostCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 2) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 2) }) t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) cachedStore.Channel().ClearCaches() cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 2) }) t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 1) cachedStore.Channel().InvalidatePinnedPostCount("id") cachedStore.Channel().GetPinnedPostCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetPinnedPostCount", 2) }) } func TestChannelStoreGuestCountCache(t *testing.T) { countResult := int64(12) logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) count, err := cachedStore.Channel().GetGuestCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) count, err = cachedStore.Channel().GetGuestCount("id", true) require.NoError(t, err) assert.Equal(t, count, countResult) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) }) t.Run("first call not cached, second force not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) cachedStore.Channel().GetGuestCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 2) }) t.Run("first call force not cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetGuestCount("id", false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 2) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 2) }) t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) cachedStore.Channel().ClearCaches() cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 2) }) t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 1) cachedStore.Channel().InvalidateGuestCount("id") cachedStore.Channel().GetGuestCount("id", true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetGuestCount", 2) }) } func TestChannelStoreChannel(t *testing.T) { channelId := "channel1" fakeChannel := model.Channel{Id: channelId, Name: "channel1-name"} logger := mlog.CreateConsoleTestLogger(t) t.Run("first call by id not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) channel, err := cachedStore.Channel().Get(channelId, true) require.NoError(t, err) assert.Equal(t, channel, &fakeChannel) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) channel, err = cachedStore.Channel().Get(channelId, true) require.NoError(t, err) assert.Equal(t, channel, &fakeChannel) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) }) t.Run("first call not cached, second force no cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().Get(channelId, false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 2) }) t.Run("first call force no cached, second not cached, third cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().Get(channelId, false) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 2) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 2) }) t.Run("first call not cached, clear cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().ClearCaches() cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 2) }) t.Run("first call not cached, invalidate cache, second call not cached", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 1) cachedStore.Channel().InvalidateChannel(channelId) cachedStore.Channel().Get(channelId, true) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "Get", 2) }) } func TestChannelStoreGetManyCache(t *testing.T) { logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) fakeChannel := model.Channel{Id: "channel1", Name: "channel1-name"} fakeChannel2 := model.Channel{Id: "channel2", Name: "channel2-name"} channels, err := cachedStore.Channel().GetMany([]string{fakeChannel.Id}, true) require.NoError(t, err) assert.ElementsMatch(t, model.ChannelList{&fakeChannel}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMany", 1) channels, err = cachedStore.Channel().GetMany([]string{fakeChannel.Id}, true) require.NoError(t, err) assert.ElementsMatch(t, model.ChannelList{&fakeChannel}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMany", 1) channels, err = cachedStore.Channel().GetMany([]string{fakeChannel.Id, fakeChannel2.Id}, true) require.NoError(t, err) assert.ElementsMatch(t, model.ChannelList{&fakeChannel, &fakeChannel2}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMany", 2) }) t.Run("passing allowCache=false should bypass cache", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) fakeChannel := model.Channel{Id: "channel1", Name: "channel1-name"} channels, err := cachedStore.Channel().GetMany([]string{fakeChannel.Id}, false) require.NoError(t, err) assert.ElementsMatch(t, model.ChannelList{&fakeChannel}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetMany", 1) }) } func TestChannelStoreGetByNamesCache(t *testing.T) { logger := mlog.CreateConsoleTestLogger(t) t.Run("first call not cached, second cached and returning same data", func(t *testing.T) { mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) fakeChannel := model.Channel{Id: "channel1", Name: "channel1-name"} fakeChannel2 := model.Channel{Id: "channel2", Name: "channel2-name"} channels, err := cachedStore.Channel().GetByNames("team1", []string{fakeChannel.Name}, true) require.NoError(t, err) assert.ElementsMatch(t, []*model.Channel{&fakeChannel}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetByNames", 1) channels, err = cachedStore.Channel().GetByNames("team1", []string{fakeChannel.Name}, true) require.NoError(t, err) assert.ElementsMatch(t, []*model.Channel{&fakeChannel}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetByNames", 1) channels, err = cachedStore.Channel().GetByNames("team1", []string{fakeChannel.Name, fakeChannel2.Name}, true) require.NoError(t, err) assert.ElementsMatch(t, []*model.Channel{&fakeChannel, &fakeChannel2}, channels) mockStore.Channel().(*mocks.ChannelStore).AssertNumberOfCalls(t, "GetByNames", 2) }) } func TestChannelStoreGetAllChannelMembersForUser(t *testing.T) { logger := mlog.CreateConsoleTestLogger(t) mockStore := getMockStore(t) mockCacheProvider := getMockCacheProvider() cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger) require.NoError(t, err) cmock := cmocks.NewCache(t) cmock.On("Get", "u1", mock.AnythingOfType("*model.StringMap")).Return(nil) cachedStore.channel.rootStore.channelMembersForUserCache = cmock _, err = cachedStore.Channel().GetAllChannelMembersForUser(request.TestContext(t), "u1", true, false) require.NoError(t, err) }