mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* MM-68830: Preserve unknown permissions during migrations on downgrade A server that was upgraded to a newer release (which introduced new permissions and wrote them into roles) and then downgraded fails fatally at startup: the permissions migration re-saves every role, and Role.Save() rejects any permission the older binary does not recognize, making the downgrade unrecoverable. Add RoleStore.SavePreservingUnknownPermissions, used only by doPermissionsMigration, which tolerates and preserves permissions this build does not recognize (logging a warning) instead of rejecting the role. The regular Save() — and therefore the role API path — stays strict, so unknown permissions cannot be introduced through user input. Unrecognized permissions are kept on disk so they are not lost on a later re-upgrade. * MM-68830: assert save forwarding in role cache tests Address review feedback: assert the underlying store's Save and SavePreservingUnknownPermissions are actually invoked (the cache invalidation defer fires regardless of forwarding), and check the returned errors. * MM-68830: address review feedback - Shorten log message in validateForSave - Rename validationRole -> roleCopy for clarity - Trim doc comments to describe behavior only - List all unknown permissions in IsValidWithoutId error - Assert specific error type in storetest * MM-68830: add Role.Clone and use it in validateForSave * MM-68830: add tests for Role.Clone * MM-68830: fix scheme id deep copy assertion in Role.Clone test
131 lines
5.4 KiB
Go
131 lines
5.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package localcachelayer
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
|
|
)
|
|
|
|
func TestRoleStore(t *testing.T) {
|
|
StoreTestWithSqlStore(t, storetest.TestRoleStore)
|
|
}
|
|
|
|
func TestRoleStoreCache(t *testing.T) {
|
|
fakeRole := model.Role{Id: "123", Name: "role-name"}
|
|
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)
|
|
|
|
role, err := cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, role, &fakeRole)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, role, &fakeRole)
|
|
cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
})
|
|
|
|
t.Run("first call not cached, save, and then not cached again", func(t *testing.T) {
|
|
mockStore := getMockStore(t)
|
|
mockCacheProvider := getMockCacheProvider()
|
|
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger)
|
|
require.NoError(t, err)
|
|
|
|
_, err = cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
_, err = cachedStore.Role().Save(&fakeRole)
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "Save", 1)
|
|
_, err = cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2)
|
|
})
|
|
|
|
t.Run("first call not cached, save preserving unknown permissions, and then not cached again", func(t *testing.T) {
|
|
mockStore := getMockStore(t)
|
|
mockCacheProvider := getMockCacheProvider()
|
|
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger)
|
|
require.NoError(t, err)
|
|
|
|
_, err = cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
_, err = cachedStore.Role().SavePreservingUnknownPermissions(&fakeRole)
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "SavePreservingUnknownPermissions", 1)
|
|
_, err = cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
require.NoError(t, err)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2)
|
|
})
|
|
|
|
t.Run("first call not cached, delete, and then not cached again", func(t *testing.T) {
|
|
mockStore := getMockStore(t)
|
|
mockCacheProvider := getMockCacheProvider()
|
|
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger)
|
|
require.NoError(t, err)
|
|
|
|
cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
cachedStore.Role().Delete("123")
|
|
cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2)
|
|
})
|
|
|
|
t.Run("first call not cached, permanent delete all, and then not cached again", func(t *testing.T) {
|
|
mockStore := getMockStore(t)
|
|
mockCacheProvider := getMockCacheProvider()
|
|
cachedStore, err := NewLocalCacheLayer(mockStore, nil, nil, mockCacheProvider, logger)
|
|
require.NoError(t, err)
|
|
|
|
cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 1)
|
|
cachedStore.Role().PermanentDeleteAll()
|
|
cachedStore.Role().GetByName(context.Background(), "role-name")
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByName", 2)
|
|
})
|
|
}
|
|
|
|
func TestRoleStoreGetByNamesCache(t *testing.T) {
|
|
fakeRole := model.Role{Id: "123", Name: "role-name"}
|
|
fakeRole2 := model.Role{Id: "456", Name: "role-name2"}
|
|
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)
|
|
|
|
roles, err := cachedStore.Role().GetByNames([]string{"role-name"})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, roles[0], &fakeRole)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByNames", 1)
|
|
|
|
roles, err = cachedStore.Role().GetByNames([]string{"role-name"})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, roles[0], &fakeRole)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByNames", 1)
|
|
|
|
roles, err = cachedStore.Role().GetByNames([]string{"role-name", "role-name2"})
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []*model.Role{&fakeRole, &fakeRole2}, roles)
|
|
mockStore.Role().(*mocks.RoleStore).AssertNumberOfCalls(t, "GetByNames", 2)
|
|
})
|
|
}
|