mattermost-community-enterp.../channels/store/storetest/remote_cluster_store.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
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>
2025-12-17 23:59:07 +09:00

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)
}
}