Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
763 lines
26 KiB
Go
763 lines
26 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package storetest
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRemoteClusterStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("RemoteClusterGetAllInChannel", func(t *testing.T) { testRemoteClusterGetAllInChannel(t, rctx, ss) })
|
|
t.Run("RemoteClusterGetAllNotInChannel", func(t *testing.T) { testRemoteClusterGetAllNotInChannel(t, rctx, ss) })
|
|
t.Run("RemoteClusterSave", func(t *testing.T) { testRemoteClusterSave(t, rctx, ss) })
|
|
t.Run("RemoteClusterDelete", func(t *testing.T) { testRemoteClusterDelete(t, rctx, ss) })
|
|
t.Run("RemoteClusterGet", func(t *testing.T) { testRemoteClusterGet(t, rctx, ss) })
|
|
t.Run("RemoteClusterGetByPluginID", func(t *testing.T) { testRemoteClusterGetByPluginID(t, rctx, ss) })
|
|
t.Run("RemoteClusterGetAll", func(t *testing.T) { testRemoteClusterGetAll(t, rctx, ss) })
|
|
t.Run("RemoteClusterGetByTopic", func(t *testing.T) { testRemoteClusterGetByTopic(t, rctx, ss) })
|
|
t.Run("RemoteClusterUpdateTopics", func(t *testing.T) { testRemoteClusterUpdateTopics(t, rctx, ss) })
|
|
}
|
|
|
|
func makeSiteURL() string {
|
|
return "www.example.com/" + model.NewId()
|
|
}
|
|
|
|
func testRemoteClusterSave(t *testing.T, _ request.CTX, ss store.Store) {
|
|
t.Run("Save", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "some_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: model.NewId(),
|
|
}
|
|
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rc.Name, rcSaved.Name)
|
|
require.Equal(t, rc.SiteURL, rcSaved.SiteURL)
|
|
require.Greater(t, rc.CreateAt, int64(0))
|
|
require.Equal(t, rc.LastPingAt, int64(0))
|
|
require.Equal(t, rc.PluginID, rcSaved.PluginID)
|
|
require.Equal(t, rc.Options, model.Bitmask(0))
|
|
})
|
|
|
|
t.Run("Save missing display name", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
}
|
|
_, err := ss.RemoteCluster().Save(rc)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("Save missing creator id", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "some_remote_2",
|
|
SiteURL: makeSiteURL(),
|
|
}
|
|
_, err := ss.RemoteCluster().Save(rc)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("Save pluginID collision", func(t *testing.T) {
|
|
const testPluginID = "com.example.collision"
|
|
|
|
rc := &model.RemoteCluster{
|
|
Name: "some_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: testPluginID,
|
|
}
|
|
_, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
rc2 := &model.RemoteCluster{
|
|
Name: "another_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: testPluginID,
|
|
}
|
|
|
|
rcSaved, err := ss.RemoteCluster().Save(rc2)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, rcSaved)
|
|
|
|
// original remotecluster should be returned
|
|
require.Equal(t, rc.Name, rcSaved.Name)
|
|
require.Equal(t, rc.SiteURL, rcSaved.SiteURL)
|
|
require.Greater(t, rc.CreateAt, int64(0))
|
|
require.Equal(t, rc.PluginID, rcSaved.PluginID)
|
|
})
|
|
|
|
t.Run("Save multiple with blank pluginID", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: model.NewId(),
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
}
|
|
_, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
rc2 := &model.RemoteCluster{
|
|
Name: model.NewId(),
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
}
|
|
_, err = ss.RemoteCluster().Save(rc2)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("Save for plugin with options", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "plugin_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: model.NewId(),
|
|
Options: model.BitflagOptionAutoShareDMs,
|
|
}
|
|
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rc.PluginID, rcSaved.PluginID)
|
|
require.Equal(t, model.BitflagOptionAutoShareDMs, rcSaved.Options)
|
|
require.True(t, rcSaved.IsOptionFlagSet(model.BitflagOptionAutoShareDMs))
|
|
|
|
rc.Name = "plugin_remote_2"
|
|
rc.SiteURL = makeSiteURL()
|
|
rc.PluginID = model.NewId()
|
|
rc.SiteURL = "plugin2.example.com"
|
|
rc.UnsetOptionFlag(model.BitflagOptionAutoShareDMs)
|
|
|
|
rcSaved, err = ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rc.PluginID, rcSaved.PluginID)
|
|
require.Equal(t, model.Bitmask(0), rcSaved.Options)
|
|
require.False(t, rcSaved.IsOptionFlagSet(model.BitflagOptionAutoShareDMs))
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterDelete(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("Delete", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "shortlived_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
}
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
deleted, err := ss.RemoteCluster().Delete(rcSaved.RemoteId)
|
|
require.NoError(t, err)
|
|
require.True(t, deleted)
|
|
|
|
deletedRC, err := ss.RemoteCluster().Get(rcSaved.RemoteId, true)
|
|
require.NoError(t, err)
|
|
require.NotZero(t, deletedRC.DeleteAt)
|
|
})
|
|
|
|
t.Run("Delete with shared channel remotes", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "shortlived_remote",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
}
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
// we create a shared channel remote for the remote cluster
|
|
channel, err := createTestChannel(ss, rctx, "test_delete")
|
|
require.NoError(t, err)
|
|
|
|
sc := &model.SharedChannel{
|
|
ChannelId: channel.Id,
|
|
TeamId: channel.TeamId,
|
|
CreatorId: model.NewId(),
|
|
ShareName: "testshare",
|
|
RemoteId: model.NewId(),
|
|
}
|
|
|
|
_, err = ss.SharedChannel().Save(sc)
|
|
require.NoError(t, err, "couldn't save shared channel", err)
|
|
|
|
scr := &model.SharedChannelRemote{
|
|
ChannelId: channel.Id,
|
|
CreatorId: model.NewId(),
|
|
RemoteId: rc.RemoteId,
|
|
}
|
|
scrSaved, err := ss.SharedChannel().SaveRemote(scr)
|
|
require.NoError(t, err)
|
|
|
|
// and then we delete the cluster, expecting the shared
|
|
// channel remote to be deleted as well
|
|
deleted, err := ss.RemoteCluster().Delete(rcSaved.RemoteId)
|
|
require.NoError(t, err)
|
|
require.True(t, deleted)
|
|
|
|
deletedRC, err := ss.RemoteCluster().Get(rcSaved.RemoteId, true)
|
|
require.NoError(t, err)
|
|
require.NotZero(t, deletedRC.DeleteAt)
|
|
|
|
deletedSCR, err := ss.SharedChannel().GetRemote(scrSaved.Id)
|
|
require.NoError(t, err)
|
|
require.NotZero(t, deletedSCR.DeleteAt)
|
|
})
|
|
|
|
t.Run("Delete nonexistent", func(t *testing.T) {
|
|
deleted, err := ss.RemoteCluster().Delete(model.NewId())
|
|
require.NoError(t, err)
|
|
require.False(t, deleted)
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterGet(t *testing.T, _ request.CTX, ss store.Store) {
|
|
t.Run("Get", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "shortlived_remote_2",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: model.NewId(),
|
|
}
|
|
rc.SetOptionFlag(model.BitflagOptionAutoShareDMs)
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
rcGet, err := ss.RemoteCluster().Get(rcSaved.RemoteId, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rcSaved.RemoteId, rcGet.RemoteId)
|
|
require.Equal(t, rcSaved.PluginID, rcGet.PluginID)
|
|
require.True(t, rcGet.IsOptionFlagSet(model.BitflagOptionAutoShareDMs))
|
|
})
|
|
|
|
t.Run("Get deleted", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "shortlived_remote_3",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: model.NewId(),
|
|
DeleteAt: 123,
|
|
}
|
|
rc.SetOptionFlag(model.BitflagOptionAutoShareDMs)
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
rcGet, err := ss.RemoteCluster().Get(rcSaved.RemoteId, false)
|
|
require.Error(t, err)
|
|
require.Empty(t, rcGet)
|
|
|
|
rcGetDeleted, err := ss.RemoteCluster().Get(rcSaved.RemoteId, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rcSaved.RemoteId, rcGetDeleted.RemoteId)
|
|
require.Equal(t, rcSaved.PluginID, rcGetDeleted.PluginID)
|
|
require.True(t, rcGetDeleted.IsOptionFlagSet(model.BitflagOptionAutoShareDMs))
|
|
})
|
|
|
|
t.Run("Get not found", func(t *testing.T) {
|
|
_, err := ss.RemoteCluster().Get(model.NewId(), false)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterGetByPluginID(t *testing.T, _ request.CTX, ss store.Store) {
|
|
const pluginID = "com.acme.bogus.plugin"
|
|
|
|
t.Run("GetByPluginID", func(t *testing.T) {
|
|
rc := &model.RemoteCluster{
|
|
Name: "shortlived_remote_4",
|
|
SiteURL: makeSiteURL(),
|
|
CreatorId: model.NewId(),
|
|
PluginID: pluginID,
|
|
}
|
|
rcSaved, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
rcGet, err := ss.RemoteCluster().GetByPluginID(pluginID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, rcSaved.RemoteId, rcGet.RemoteId)
|
|
require.Equal(t, pluginID, rcGet.PluginID)
|
|
})
|
|
|
|
t.Run("GetByPluginID not found", func(t *testing.T) {
|
|
_, err := ss.RemoteCluster().GetByPluginID(model.NewId())
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterGetAll(t *testing.T, _ request.CTX, ss store.Store) {
|
|
ss.DropAllTables()
|
|
|
|
userId := model.NewId()
|
|
now := model.GetMillis()
|
|
pingLongAgo := model.GetMillis() - (model.RemoteOfflineAfterMillis * 3)
|
|
|
|
data := []*model.RemoteCluster{
|
|
{Name: "offline_remote", CreatorId: userId, SiteURL: makeSiteURL(), LastPingAt: pingLongAgo, Topics: " shared incident "},
|
|
{Name: "some_online_remote", CreatorId: userId, SiteURL: makeSiteURL(), LastPingAt: now, Topics: " shared incident "},
|
|
{Name: "another_online_remote", CreatorId: model.NewId(), SiteURL: makeSiteURL(), LastPingAt: now, Topics: ""},
|
|
{Name: "another_offline_remote", CreatorId: model.NewId(), SiteURL: makeSiteURL(), LastPingAt: pingLongAgo, Topics: " shared "},
|
|
{Name: "brand_new_offline_remote", CreatorId: userId, SiteURL: "", LastPingAt: 0, Topics: " bogus shared stuff "},
|
|
{Name: "offline_plugin_remote", CreatorId: model.NewId(), SiteURL: makeSiteURL(), PluginID: model.NewId(), LastPingAt: 0, Topics: " pluginshare "},
|
|
{Name: "online_plugin_remote", CreatorId: model.NewId(), SiteURL: makeSiteURL(), PluginID: model.NewId(), LastPingAt: now, Topics: " pluginshare "},
|
|
{Name: "deleted_remote", CreatorId: model.NewId(), SiteURL: "", LastPingAt: 0, DeleteAt: 123},
|
|
}
|
|
|
|
idsAll := make([]string, 0)
|
|
idsNotDeleted := make([]string, 0)
|
|
idsOnline := make([]string, 0)
|
|
idsShareTopic := make([]string, 0)
|
|
idsPlugin := make([]string, 0)
|
|
idsNotPlugin := make([]string, 0)
|
|
idsConfirmed := make([]string, 0)
|
|
|
|
for _, item := range data {
|
|
online := item.LastPingAt == now
|
|
saved, err := ss.RemoteCluster().Save(item)
|
|
require.NoError(t, err)
|
|
idsAll = append(idsAll, saved.RemoteId)
|
|
if item.DeleteAt == 0 {
|
|
idsNotDeleted = append(idsNotDeleted, saved.RemoteId)
|
|
|
|
// only include non-deleted items in other counts
|
|
if online {
|
|
idsOnline = append(idsOnline, saved.RemoteId)
|
|
}
|
|
if strings.Contains(saved.Topics, " shared ") {
|
|
idsShareTopic = append(idsShareTopic, saved.RemoteId)
|
|
}
|
|
if item.PluginID != "" {
|
|
idsPlugin = append(idsPlugin, saved.RemoteId)
|
|
} else {
|
|
idsNotPlugin = append(idsNotPlugin, saved.RemoteId)
|
|
}
|
|
if item.SiteURL != "" {
|
|
idsConfirmed = append(idsConfirmed, saved.RemoteId)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Run("GetAll", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{IncludeDeleted: true}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure all the test data remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsAll)
|
|
})
|
|
|
|
t.Run("GetAllNotDeleted", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure all the test data remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsNotDeleted)
|
|
})
|
|
|
|
t.Run("GetAll online only", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
ExcludeOffline: true,
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure all the online remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsOnline)
|
|
})
|
|
|
|
t.Run("GetAll by topic", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
Topic: "shared",
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only correct topic returned
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsShareTopic)
|
|
})
|
|
|
|
t.Run("GetAll online by topic", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
ExcludeOffline: true,
|
|
Topic: "shared",
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only online remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.Subset(t, idsOnline, ids)
|
|
// make sure correct topic returned
|
|
assert.Subset(t, idsShareTopic, ids)
|
|
assert.Len(t, ids, 1)
|
|
})
|
|
|
|
t.Run("GetAll by Creator", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
CreatorId: userId,
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only correct creator returned
|
|
assert.Len(t, remotes, 3)
|
|
for _, rc := range remotes {
|
|
assert.Equal(t, userId, rc.CreatorId)
|
|
}
|
|
})
|
|
|
|
t.Run("GetAll by Confirmed", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
OnlyConfirmed: true,
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only confirmed returned
|
|
for _, rc := range remotes {
|
|
assert.NotEmpty(t, rc.SiteURL)
|
|
}
|
|
// make sure all confirmed returned
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsConfirmed)
|
|
})
|
|
|
|
t.Run("GetAll only plugins", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
OnlyPlugins: true,
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only plugin remotes returned
|
|
for _, rc := range remotes {
|
|
assert.NotEmpty(t, rc.PluginID)
|
|
assert.True(t, rc.IsPlugin())
|
|
}
|
|
// make sure all the plugin remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsPlugin)
|
|
})
|
|
|
|
t.Run("GetAll excluding plugins", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
ExcludePlugins: true,
|
|
}
|
|
remotes, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
// make sure only non plugin remotes returned
|
|
for _, rc := range remotes {
|
|
assert.Empty(t, rc.PluginID)
|
|
assert.False(t, rc.IsPlugin())
|
|
}
|
|
// make sure all of the non plugin remotes were returned.
|
|
ids := getIds(remotes)
|
|
assert.ElementsMatch(t, ids, idsNotPlugin)
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterGetAllInChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
const (
|
|
testPluginID_1 = "com.sample.blap"
|
|
testPluginID_2 = "com.sample.bloop"
|
|
)
|
|
|
|
ss.DropAllTables()
|
|
now := model.GetMillis()
|
|
|
|
userId := model.NewId()
|
|
|
|
channel1, err := createTestChannel(ss, rctx, "channel_1")
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := createTestChannel(ss, rctx, "channel_2")
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := createTestChannel(ss, rctx, "channel_3")
|
|
require.NoError(t, err)
|
|
|
|
// Create shared channels
|
|
scData := []*model.SharedChannel{
|
|
{ChannelId: channel1.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_1", CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_2", CreatorId: model.NewId()},
|
|
{ChannelId: channel3.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_3", CreatorId: model.NewId()},
|
|
}
|
|
for _, item := range scData {
|
|
_, err := ss.SharedChannel().Save(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create some remote clusters
|
|
rcData := []*model.RemoteCluster{
|
|
{Name: "AAAA_Inc", CreatorId: userId, SiteURL: "aaaa.com", RemoteId: model.NewId(), LastPingAt: now, PluginID: testPluginID_1},
|
|
{Name: "BBBB_Inc", CreatorId: userId, SiteURL: "bbbb.com", RemoteId: model.NewId(), LastPingAt: 0, PluginID: testPluginID_2},
|
|
{Name: "CCCC_Inc", CreatorId: userId, SiteURL: "cccc.com", RemoteId: model.NewId(), LastPingAt: now},
|
|
{Name: "DDDD_Inc", CreatorId: userId, SiteURL: "dddd.com", RemoteId: model.NewId(), LastPingAt: now},
|
|
{Name: "EEEE_Inc", CreatorId: userId, SiteURL: "eeee.com", RemoteId: model.NewId(), LastPingAt: 0},
|
|
}
|
|
for _, item := range rcData {
|
|
_, err := ss.RemoteCluster().Save(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create some shared channel remotes
|
|
scrData := []*model.SharedChannelRemote{
|
|
{ChannelId: channel1.Id, RemoteId: rcData[0].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel1.Id, RemoteId: rcData[1].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, RemoteId: rcData[2].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, RemoteId: rcData[3].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, RemoteId: rcData[4].RemoteId, CreatorId: model.NewId()},
|
|
}
|
|
for _, item := range scrData {
|
|
_, err := ss.SharedChannel().SaveRemote(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
t.Run("Channel 1", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
InChannel: channel1.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 2, "channel 1 should have 2 remote clusters")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId}, ids)
|
|
require.Equal(t, testPluginID_1, rcData[0].PluginID)
|
|
require.Equal(t, testPluginID_2, rcData[1].PluginID)
|
|
})
|
|
|
|
t.Run("Channel 1 online only", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
ExcludeOffline: true,
|
|
InChannel: channel1.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 1, "channel 1 should have 1 online remote clusters")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[0].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel 2", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
InChannel: channel2.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 3, "channel 2 should have 3 remote clusters")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId, rcData[4].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel 2 online only", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
ExcludeOffline: true,
|
|
InChannel: channel2.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 2, "channel 2 should have 2 online remote clusters")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel 3", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
InChannel: channel3.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Empty(t, list, "channel 3 should have 0 remote clusters")
|
|
})
|
|
}
|
|
|
|
func testRemoteClusterGetAllNotInChannel(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
ss.DropAllTables()
|
|
|
|
userId := model.NewId()
|
|
|
|
channel1, err := createTestChannel(ss, rctx, "channel_1")
|
|
require.NoError(t, err)
|
|
|
|
channel2, err := createTestChannel(ss, rctx, "channel_2")
|
|
require.NoError(t, err)
|
|
|
|
channel3, err := createTestChannel(ss, rctx, "channel_3")
|
|
require.NoError(t, err)
|
|
|
|
// Create shared channels
|
|
scData := []*model.SharedChannel{
|
|
{ChannelId: channel1.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_1", CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_2", CreatorId: model.NewId()},
|
|
{ChannelId: channel3.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_3", CreatorId: model.NewId()},
|
|
}
|
|
for _, item := range scData {
|
|
_, err := ss.SharedChannel().Save(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create some remote clusters
|
|
rcData := []*model.RemoteCluster{
|
|
{Name: "AAAA_Inc", CreatorId: userId, SiteURL: "aaaa.com", RemoteId: model.NewId()},
|
|
{Name: "BBBB_Inc", CreatorId: userId, SiteURL: "bbbb.com", RemoteId: model.NewId()},
|
|
{Name: "CCCC_Inc", CreatorId: userId, SiteURL: "cccc.com", RemoteId: model.NewId()},
|
|
{Name: "DDDD_Inc", CreatorId: userId, SiteURL: "dddd.com", RemoteId: model.NewId()},
|
|
{Name: "EEEE_Inc", CreatorId: userId, SiteURL: "eeee.com", RemoteId: model.NewId()},
|
|
}
|
|
for _, item := range rcData {
|
|
_, err := ss.RemoteCluster().Save(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create some shared channel remotes
|
|
scrData := []*model.SharedChannelRemote{
|
|
{ChannelId: channel1.Id, RemoteId: rcData[0].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel1.Id, RemoteId: rcData[1].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, RemoteId: rcData[2].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel2.Id, RemoteId: rcData[3].RemoteId, CreatorId: model.NewId()},
|
|
{ChannelId: channel3.Id, RemoteId: rcData[4].RemoteId, CreatorId: model.NewId()},
|
|
}
|
|
for _, item := range scrData {
|
|
_, err := ss.SharedChannel().SaveRemote(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
t.Run("Channel 1", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
NotInChannel: channel1.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 3, "channel 1 should have 3 remote clusters that are not already members")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId, rcData[4].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel 2", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
NotInChannel: channel2.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 3, "channel 2 should have 3 remote clusters that are not already members")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[4].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel 3", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
NotInChannel: channel3.Id,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 4, "channel 3 should have 4 remote clusters that are not already members")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[2].RemoteId, rcData[3].RemoteId}, ids)
|
|
})
|
|
|
|
t.Run("Channel with no share remotes", func(t *testing.T) {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
NotInChannel: model.NewId(),
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 5, "should have 5 remote clusters that are not already members")
|
|
ids := getIds(list)
|
|
require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[2].RemoteId, rcData[3].RemoteId,
|
|
rcData[4].RemoteId}, ids)
|
|
})
|
|
}
|
|
|
|
func getIds(remotes []*model.RemoteCluster) []string {
|
|
ids := make([]string, 0, len(remotes))
|
|
for _, r := range remotes {
|
|
ids = append(ids, r.RemoteId)
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func testRemoteClusterGetByTopic(t *testing.T, _ request.CTX, ss store.Store) {
|
|
ss.DropAllTables()
|
|
|
|
rcData := []*model.RemoteCluster{
|
|
{Name: "AAAA_Inc", CreatorId: model.NewId(), SiteURL: "aaaa.com", RemoteId: model.NewId(), Topics: ""},
|
|
{Name: "BBBB_Inc", CreatorId: model.NewId(), SiteURL: "bbbb.com", RemoteId: model.NewId(), Topics: " share "},
|
|
{Name: "CCCC_Inc", CreatorId: model.NewId(), SiteURL: "cccc.com", RemoteId: model.NewId(), Topics: " incident share "},
|
|
{Name: "DDDD_Inc", CreatorId: model.NewId(), SiteURL: "dddd.com", RemoteId: model.NewId(), Topics: " bogus "},
|
|
{Name: "EEEE_Inc", CreatorId: model.NewId(), SiteURL: "eeee.com", RemoteId: model.NewId(), Topics: " logs share incident "},
|
|
{Name: "FFFF_Inc", CreatorId: model.NewId(), SiteURL: "ffff.com", RemoteId: model.NewId(), Topics: " bogus incident "},
|
|
{Name: "GGGG_Inc", CreatorId: model.NewId(), SiteURL: "gggg.com", RemoteId: model.NewId(), Topics: "*"},
|
|
}
|
|
for _, item := range rcData {
|
|
_, err := ss.RemoteCluster().Save(item)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
testData := []struct {
|
|
topic string
|
|
expectedCount int
|
|
expectError bool
|
|
}{
|
|
{topic: "", expectedCount: 7, expectError: false},
|
|
{topic: " ", expectedCount: 0, expectError: true},
|
|
{topic: "share", expectedCount: 4},
|
|
{topic: " share ", expectedCount: 4},
|
|
{topic: "bogus", expectedCount: 3},
|
|
{topic: "non-existent", expectedCount: 1},
|
|
{topic: "*", expectedCount: 0, expectError: true}, // can't query with wildcard
|
|
}
|
|
|
|
for _, tt := range testData {
|
|
filter := model.RemoteClusterQueryFilter{
|
|
Topic: tt.topic,
|
|
}
|
|
list, err := ss.RemoteCluster().GetAll(0, 999999, filter)
|
|
if tt.expectError {
|
|
assert.Errorf(t, err, "expected error for topic=%s", tt.topic)
|
|
} else {
|
|
assert.NoErrorf(t, err, "expected no error for topic=%s", tt.topic)
|
|
}
|
|
assert.Lenf(t, list, tt.expectedCount, "topic=%s", tt.topic)
|
|
}
|
|
}
|
|
|
|
func testRemoteClusterUpdateTopics(t *testing.T, _ request.CTX, ss store.Store) {
|
|
remoteId := model.NewId()
|
|
rc := &model.RemoteCluster{
|
|
DisplayName: "Blap Inc",
|
|
Name: "blap",
|
|
SiteURL: "blap.com",
|
|
RemoteId: remoteId,
|
|
Topics: "",
|
|
CreatorId: model.NewId(),
|
|
}
|
|
|
|
_, err := ss.RemoteCluster().Save(rc)
|
|
require.NoError(t, err)
|
|
|
|
testData := []struct {
|
|
topics string
|
|
expected string
|
|
}{
|
|
{topics: "", expected: ""},
|
|
{topics: " ", expected: ""},
|
|
{topics: "share", expected: " share "},
|
|
{topics: " share ", expected: " share "},
|
|
{topics: "share incident", expected: " share incident "},
|
|
{topics: " share incident ", expected: " share incident "},
|
|
}
|
|
|
|
for _, tt := range testData {
|
|
_, err = ss.RemoteCluster().UpdateTopics(remoteId, tt.topics)
|
|
require.NoError(t, err)
|
|
|
|
rcUpdated, err := ss.RemoteCluster().Get(remoteId, false)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tt.expected, rcUpdated.Topics)
|
|
}
|
|
}
|