github.com/mattermost/mattermost-server/v5@v5.39.3/store/storetest/remote_cluster_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package storetest 5 6 import ( 7 "strings" 8 "testing" 9 10 "github.com/mattermost/mattermost-server/v5/model" 11 "github.com/mattermost/mattermost-server/v5/store" 12 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestRemoteClusterStore(t *testing.T, ss store.Store) { 18 t.Run("RemoteClusterGetAllInChannel", func(t *testing.T) { testRemoteClusterGetAllInChannel(t, ss) }) 19 t.Run("RemoteClusterGetAllNotInChannel", func(t *testing.T) { testRemoteClusterGetAllNotInChannel(t, ss) }) 20 t.Run("RemoteClusterSave", func(t *testing.T) { testRemoteClusterSave(t, ss) }) 21 t.Run("RemoteClusterDelete", func(t *testing.T) { testRemoteClusterDelete(t, ss) }) 22 t.Run("RemoteClusterGet", func(t *testing.T) { testRemoteClusterGet(t, ss) }) 23 t.Run("RemoteClusterGetAll", func(t *testing.T) { testRemoteClusterGetAll(t, ss) }) 24 t.Run("RemoteClusterGetByTopic", func(t *testing.T) { testRemoteClusterGetByTopic(t, ss) }) 25 t.Run("RemoteClusterUpdateTopics", func(t *testing.T) { testRemoteClusterUpdateTopics(t, ss) }) 26 } 27 28 func testRemoteClusterSave(t *testing.T, ss store.Store) { 29 30 t.Run("Save", func(t *testing.T) { 31 rc := &model.RemoteCluster{ 32 Name: "some_remote", 33 SiteURL: "somewhere.com", 34 CreatorId: model.NewId(), 35 } 36 37 rcSaved, err := ss.RemoteCluster().Save(rc) 38 require.NoError(t, err) 39 require.Equal(t, rc.Name, rcSaved.Name) 40 require.Equal(t, rc.SiteURL, rcSaved.SiteURL) 41 require.Greater(t, rc.CreateAt, int64(0)) 42 require.Equal(t, rc.LastPingAt, int64(0)) 43 }) 44 45 t.Run("Save missing display name", func(t *testing.T) { 46 rc := &model.RemoteCluster{ 47 SiteURL: "somewhere.com", 48 CreatorId: model.NewId(), 49 } 50 _, err := ss.RemoteCluster().Save(rc) 51 require.Error(t, err) 52 }) 53 54 t.Run("Save missing creator id", func(t *testing.T) { 55 rc := &model.RemoteCluster{ 56 Name: "some_remote_2", 57 SiteURL: "somewhere.com", 58 } 59 _, err := ss.RemoteCluster().Save(rc) 60 require.Error(t, err) 61 }) 62 } 63 64 func testRemoteClusterDelete(t *testing.T, ss store.Store) { 65 t.Run("Delete", func(t *testing.T) { 66 rc := &model.RemoteCluster{ 67 Name: "shortlived_remote", 68 SiteURL: "nowhere.com", 69 CreatorId: model.NewId(), 70 } 71 rcSaved, err := ss.RemoteCluster().Save(rc) 72 require.NoError(t, err) 73 74 deleted, err := ss.RemoteCluster().Delete(rcSaved.RemoteId) 75 require.NoError(t, err) 76 require.True(t, deleted) 77 }) 78 79 t.Run("Delete nonexistent", func(t *testing.T) { 80 deleted, err := ss.RemoteCluster().Delete(model.NewId()) 81 require.NoError(t, err) 82 require.False(t, deleted) 83 }) 84 } 85 86 func testRemoteClusterGet(t *testing.T, ss store.Store) { 87 t.Run("Get", func(t *testing.T) { 88 rc := &model.RemoteCluster{ 89 Name: "shortlived_remote_2", 90 SiteURL: "nowhere.com", 91 CreatorId: model.NewId(), 92 } 93 rcSaved, err := ss.RemoteCluster().Save(rc) 94 require.NoError(t, err) 95 96 rcGet, err := ss.RemoteCluster().Get(rcSaved.RemoteId) 97 require.NoError(t, err) 98 require.Equal(t, rcSaved.RemoteId, rcGet.RemoteId) 99 }) 100 101 t.Run("Get not found", func(t *testing.T) { 102 _, err := ss.RemoteCluster().Get(model.NewId()) 103 require.Error(t, err) 104 }) 105 } 106 107 func testRemoteClusterGetAll(t *testing.T, ss store.Store) { 108 require.NoError(t, clearRemoteClusters(ss)) 109 110 userId := model.NewId() 111 now := model.GetMillis() 112 pingLongAgo := model.GetMillis() - (model.RemoteOfflineAfterMillis * 3) 113 114 data := []*model.RemoteCluster{ 115 {Name: "offline_remote", CreatorId: userId, SiteURL: "somewhere.com", LastPingAt: pingLongAgo, Topics: " shared incident "}, 116 {Name: "some_online_remote", CreatorId: userId, SiteURL: "nowhere.com", LastPingAt: now, Topics: " shared incident "}, 117 {Name: "another_online_remote", CreatorId: model.NewId(), SiteURL: "underwhere.com", LastPingAt: now, Topics: ""}, 118 {Name: "another_offline_remote", CreatorId: model.NewId(), SiteURL: "knowhere.com", LastPingAt: pingLongAgo, Topics: " shared "}, 119 {Name: "brand_new_offline_remote", CreatorId: userId, SiteURL: "", LastPingAt: 0, Topics: " bogus shared stuff "}, 120 } 121 122 idsAll := make([]string, 0) 123 idsOnline := make([]string, 0) 124 idsOffline := make([]string, 0) 125 idsShareTopic := make([]string, 0) 126 127 for _, item := range data { 128 online := item.LastPingAt == now 129 saved, err := ss.RemoteCluster().Save(item) 130 require.NoError(t, err) 131 idsAll = append(idsAll, saved.RemoteId) 132 if online { 133 idsOnline = append(idsOnline, saved.RemoteId) 134 } else { 135 idsOffline = append(idsOffline, saved.RemoteId) 136 } 137 if strings.Contains(saved.Topics, " shared ") { 138 idsShareTopic = append(idsShareTopic, saved.RemoteId) 139 } 140 } 141 142 t.Run("GetAll", func(t *testing.T) { 143 filter := model.RemoteClusterQueryFilter{} 144 remotes, err := ss.RemoteCluster().GetAll(filter) 145 require.NoError(t, err) 146 // make sure all the test data remotes were returned. 147 ids := getIds(remotes) 148 assert.ElementsMatch(t, ids, idsAll) 149 }) 150 151 t.Run("GetAll online only", func(t *testing.T) { 152 filter := model.RemoteClusterQueryFilter{ 153 ExcludeOffline: true, 154 } 155 remotes, err := ss.RemoteCluster().GetAll(filter) 156 require.NoError(t, err) 157 // make sure all the online remotes were returned. 158 ids := getIds(remotes) 159 assert.ElementsMatch(t, ids, idsOnline) 160 }) 161 162 t.Run("GetAll by topic", func(t *testing.T) { 163 filter := model.RemoteClusterQueryFilter{ 164 Topic: "shared", 165 } 166 remotes, err := ss.RemoteCluster().GetAll(filter) 167 require.NoError(t, err) 168 // make sure only correct topic returned 169 ids := getIds(remotes) 170 assert.ElementsMatch(t, ids, idsShareTopic) 171 }) 172 173 t.Run("GetAll online by topic", func(t *testing.T) { 174 filter := model.RemoteClusterQueryFilter{ 175 ExcludeOffline: true, 176 Topic: "shared", 177 } 178 remotes, err := ss.RemoteCluster().GetAll(filter) 179 require.NoError(t, err) 180 // make sure only online remotes were returned. 181 ids := getIds(remotes) 182 assert.Subset(t, idsOnline, ids) 183 // make sure correct topic returned 184 assert.Subset(t, idsShareTopic, ids) 185 assert.Len(t, ids, 1) 186 }) 187 188 t.Run("GetAll by Creator", func(t *testing.T) { 189 filter := model.RemoteClusterQueryFilter{ 190 CreatorId: userId, 191 } 192 remotes, err := ss.RemoteCluster().GetAll(filter) 193 require.NoError(t, err) 194 // make sure only correct creator returned 195 assert.Len(t, remotes, 3) 196 for _, rc := range remotes { 197 assert.Equal(t, userId, rc.CreatorId) 198 } 199 }) 200 201 t.Run("GetAll by Confirmed", func(t *testing.T) { 202 filter := model.RemoteClusterQueryFilter{ 203 OnlyConfirmed: true, 204 } 205 remotes, err := ss.RemoteCluster().GetAll(filter) 206 require.NoError(t, err) 207 // make sure only confirmed returned 208 assert.Len(t, remotes, 4) 209 for _, rc := range remotes { 210 assert.NotEmpty(t, rc.SiteURL) 211 } 212 }) 213 } 214 215 func testRemoteClusterGetAllInChannel(t *testing.T, ss store.Store) { 216 require.NoError(t, clearRemoteClusters(ss)) 217 now := model.GetMillis() 218 219 userId := model.NewId() 220 221 channel1, err := createTestChannel(ss, "channel_1") 222 require.NoError(t, err) 223 224 channel2, err := createTestChannel(ss, "channel_2") 225 require.NoError(t, err) 226 227 channel3, err := createTestChannel(ss, "channel_3") 228 require.NoError(t, err) 229 230 // Create shared channels 231 scData := []*model.SharedChannel{ 232 {ChannelId: channel1.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_1", CreatorId: model.NewId()}, 233 {ChannelId: channel2.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_2", CreatorId: model.NewId()}, 234 {ChannelId: channel3.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_3", CreatorId: model.NewId()}, 235 } 236 for _, item := range scData { 237 _, err := ss.SharedChannel().Save(item) 238 require.NoError(t, err) 239 } 240 241 // Create some remote clusters 242 rcData := []*model.RemoteCluster{ 243 {Name: "AAAA_Inc", CreatorId: userId, SiteURL: "aaaa.com", RemoteId: model.NewId(), LastPingAt: now}, 244 {Name: "BBBB_Inc", CreatorId: userId, SiteURL: "bbbb.com", RemoteId: model.NewId(), LastPingAt: 0}, 245 {Name: "CCCC_Inc", CreatorId: userId, SiteURL: "cccc.com", RemoteId: model.NewId(), LastPingAt: now}, 246 {Name: "DDDD_Inc", CreatorId: userId, SiteURL: "dddd.com", RemoteId: model.NewId(), LastPingAt: now}, 247 {Name: "EEEE_Inc", CreatorId: userId, SiteURL: "eeee.com", RemoteId: model.NewId(), LastPingAt: 0}, 248 } 249 for _, item := range rcData { 250 _, err := ss.RemoteCluster().Save(item) 251 require.NoError(t, err) 252 } 253 254 // Create some shared channel remotes 255 scrData := []*model.SharedChannelRemote{ 256 {ChannelId: channel1.Id, RemoteId: rcData[0].RemoteId, CreatorId: model.NewId()}, 257 {ChannelId: channel1.Id, RemoteId: rcData[1].RemoteId, CreatorId: model.NewId()}, 258 {ChannelId: channel2.Id, RemoteId: rcData[2].RemoteId, CreatorId: model.NewId()}, 259 {ChannelId: channel2.Id, RemoteId: rcData[3].RemoteId, CreatorId: model.NewId()}, 260 {ChannelId: channel2.Id, RemoteId: rcData[4].RemoteId, CreatorId: model.NewId()}, 261 } 262 for _, item := range scrData { 263 _, err := ss.SharedChannel().SaveRemote(item) 264 require.NoError(t, err) 265 } 266 267 t.Run("Channel 1", func(t *testing.T) { 268 filter := model.RemoteClusterQueryFilter{ 269 InChannel: channel1.Id, 270 } 271 list, err := ss.RemoteCluster().GetAll(filter) 272 require.NoError(t, err) 273 require.Len(t, list, 2, "channel 1 should have 2 remote clusters") 274 ids := getIds(list) 275 require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId}, ids) 276 }) 277 278 t.Run("Channel 1 online only", func(t *testing.T) { 279 filter := model.RemoteClusterQueryFilter{ 280 ExcludeOffline: true, 281 InChannel: channel1.Id, 282 } 283 list, err := ss.RemoteCluster().GetAll(filter) 284 require.NoError(t, err) 285 require.Len(t, list, 1, "channel 1 should have 1 online remote clusters") 286 ids := getIds(list) 287 require.ElementsMatch(t, []string{rcData[0].RemoteId}, ids) 288 }) 289 290 t.Run("Channel 2", func(t *testing.T) { 291 filter := model.RemoteClusterQueryFilter{ 292 InChannel: channel2.Id, 293 } 294 list, err := ss.RemoteCluster().GetAll(filter) 295 require.NoError(t, err) 296 require.Len(t, list, 3, "channel 2 should have 3 remote clusters") 297 ids := getIds(list) 298 require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId, rcData[4].RemoteId}, ids) 299 }) 300 301 t.Run("Channel 2 online only", func(t *testing.T) { 302 filter := model.RemoteClusterQueryFilter{ 303 ExcludeOffline: true, 304 InChannel: channel2.Id, 305 } 306 list, err := ss.RemoteCluster().GetAll(filter) 307 require.NoError(t, err) 308 require.Len(t, list, 2, "channel 2 should have 2 online remote clusters") 309 ids := getIds(list) 310 require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId}, ids) 311 }) 312 313 t.Run("Channel 3", func(t *testing.T) { 314 filter := model.RemoteClusterQueryFilter{ 315 InChannel: channel3.Id, 316 } 317 list, err := ss.RemoteCluster().GetAll(filter) 318 require.NoError(t, err) 319 require.Empty(t, list, "channel 3 should have 0 remote clusters") 320 }) 321 } 322 323 func testRemoteClusterGetAllNotInChannel(t *testing.T, ss store.Store) { 324 require.NoError(t, clearRemoteClusters(ss)) 325 326 userId := model.NewId() 327 328 channel1, err := createTestChannel(ss, "channel_1") 329 require.NoError(t, err) 330 331 channel2, err := createTestChannel(ss, "channel_2") 332 require.NoError(t, err) 333 334 channel3, err := createTestChannel(ss, "channel_3") 335 require.NoError(t, err) 336 337 // Create shared channels 338 scData := []*model.SharedChannel{ 339 {ChannelId: channel1.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_1", CreatorId: model.NewId()}, 340 {ChannelId: channel2.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_2", CreatorId: model.NewId()}, 341 {ChannelId: channel3.Id, TeamId: model.NewId(), Home: true, ShareName: "test_chan_3", CreatorId: model.NewId()}, 342 } 343 for _, item := range scData { 344 _, err := ss.SharedChannel().Save(item) 345 require.NoError(t, err) 346 } 347 348 // Create some remote clusters 349 rcData := []*model.RemoteCluster{ 350 {Name: "AAAA_Inc", CreatorId: userId, SiteURL: "aaaa.com", RemoteId: model.NewId()}, 351 {Name: "BBBB_Inc", CreatorId: userId, SiteURL: "bbbb.com", RemoteId: model.NewId()}, 352 {Name: "CCCC_Inc", CreatorId: userId, SiteURL: "cccc.com", RemoteId: model.NewId()}, 353 {Name: "DDDD_Inc", CreatorId: userId, SiteURL: "dddd.com", RemoteId: model.NewId()}, 354 {Name: "EEEE_Inc", CreatorId: userId, SiteURL: "eeee.com", RemoteId: model.NewId()}, 355 } 356 for _, item := range rcData { 357 _, err := ss.RemoteCluster().Save(item) 358 require.NoError(t, err) 359 } 360 361 // Create some shared channel remotes 362 scrData := []*model.SharedChannelRemote{ 363 {ChannelId: channel1.Id, RemoteId: rcData[0].RemoteId, CreatorId: model.NewId()}, 364 {ChannelId: channel1.Id, RemoteId: rcData[1].RemoteId, CreatorId: model.NewId()}, 365 {ChannelId: channel2.Id, RemoteId: rcData[2].RemoteId, CreatorId: model.NewId()}, 366 {ChannelId: channel2.Id, RemoteId: rcData[3].RemoteId, CreatorId: model.NewId()}, 367 {ChannelId: channel3.Id, RemoteId: rcData[4].RemoteId, CreatorId: model.NewId()}, 368 } 369 for _, item := range scrData { 370 _, err := ss.SharedChannel().SaveRemote(item) 371 require.NoError(t, err) 372 } 373 374 t.Run("Channel 1", func(t *testing.T) { 375 filter := model.RemoteClusterQueryFilter{ 376 NotInChannel: channel1.Id, 377 } 378 list, err := ss.RemoteCluster().GetAll(filter) 379 require.NoError(t, err) 380 require.Len(t, list, 3, "channel 1 should have 3 remote clusters that are not already members") 381 ids := getIds(list) 382 require.ElementsMatch(t, []string{rcData[2].RemoteId, rcData[3].RemoteId, rcData[4].RemoteId}, ids) 383 }) 384 385 t.Run("Channel 2", func(t *testing.T) { 386 filter := model.RemoteClusterQueryFilter{ 387 NotInChannel: channel2.Id, 388 } 389 list, err := ss.RemoteCluster().GetAll(filter) 390 require.NoError(t, err) 391 require.Len(t, list, 3, "channel 2 should have 3 remote clusters that are not already members") 392 ids := getIds(list) 393 require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[4].RemoteId}, ids) 394 }) 395 396 t.Run("Channel 3", func(t *testing.T) { 397 filter := model.RemoteClusterQueryFilter{ 398 NotInChannel: channel3.Id, 399 } 400 list, err := ss.RemoteCluster().GetAll(filter) 401 require.NoError(t, err) 402 require.Len(t, list, 4, "channel 3 should have 4 remote clusters that are not already members") 403 ids := getIds(list) 404 require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[2].RemoteId, rcData[3].RemoteId}, ids) 405 }) 406 407 t.Run("Channel with no share remotes", func(t *testing.T) { 408 filter := model.RemoteClusterQueryFilter{ 409 NotInChannel: model.NewId(), 410 } 411 list, err := ss.RemoteCluster().GetAll(filter) 412 require.NoError(t, err) 413 require.Len(t, list, 5, "should have 5 remote clusters that are not already members") 414 ids := getIds(list) 415 require.ElementsMatch(t, []string{rcData[0].RemoteId, rcData[1].RemoteId, rcData[2].RemoteId, rcData[3].RemoteId, 416 rcData[4].RemoteId}, ids) 417 }) 418 } 419 420 func getIds(remotes []*model.RemoteCluster) []string { 421 ids := make([]string, 0, len(remotes)) 422 for _, r := range remotes { 423 ids = append(ids, r.RemoteId) 424 } 425 return ids 426 } 427 428 func testRemoteClusterGetByTopic(t *testing.T, ss store.Store) { 429 require.NoError(t, clearRemoteClusters(ss)) 430 431 rcData := []*model.RemoteCluster{ 432 {Name: "AAAA_Inc", CreatorId: model.NewId(), SiteURL: "aaaa.com", RemoteId: model.NewId(), Topics: ""}, 433 {Name: "BBBB_Inc", CreatorId: model.NewId(), SiteURL: "bbbb.com", RemoteId: model.NewId(), Topics: " share "}, 434 {Name: "CCCC_Inc", CreatorId: model.NewId(), SiteURL: "cccc.com", RemoteId: model.NewId(), Topics: " incident share "}, 435 {Name: "DDDD_Inc", CreatorId: model.NewId(), SiteURL: "dddd.com", RemoteId: model.NewId(), Topics: " bogus "}, 436 {Name: "EEEE_Inc", CreatorId: model.NewId(), SiteURL: "eeee.com", RemoteId: model.NewId(), Topics: " logs share incident "}, 437 {Name: "FFFF_Inc", CreatorId: model.NewId(), SiteURL: "ffff.com", RemoteId: model.NewId(), Topics: " bogus incident "}, 438 {Name: "GGGG_Inc", CreatorId: model.NewId(), SiteURL: "gggg.com", RemoteId: model.NewId(), Topics: "*"}, 439 } 440 for _, item := range rcData { 441 _, err := ss.RemoteCluster().Save(item) 442 require.NoError(t, err) 443 } 444 445 testData := []struct { 446 topic string 447 expectedCount int 448 expectError bool 449 }{ 450 {topic: "", expectedCount: 7, expectError: false}, 451 {topic: " ", expectedCount: 0, expectError: true}, 452 {topic: "share", expectedCount: 4}, 453 {topic: " share ", expectedCount: 4}, 454 {topic: "bogus", expectedCount: 3}, 455 {topic: "non-existent", expectedCount: 1}, 456 {topic: "*", expectedCount: 0, expectError: true}, // can't query with wildcard 457 } 458 459 for _, tt := range testData { 460 filter := model.RemoteClusterQueryFilter{ 461 Topic: tt.topic, 462 } 463 list, err := ss.RemoteCluster().GetAll(filter) 464 if tt.expectError { 465 assert.Errorf(t, err, "expected error for topic=%s", tt.topic) 466 } else { 467 assert.NoErrorf(t, err, "expected no error for topic=%s", tt.topic) 468 } 469 assert.Lenf(t, list, tt.expectedCount, "topic=%s", tt.topic) 470 } 471 } 472 473 func testRemoteClusterUpdateTopics(t *testing.T, ss store.Store) { 474 remoteId := model.NewId() 475 rc := &model.RemoteCluster{ 476 DisplayName: "Blap Inc", 477 Name: "blap", 478 SiteURL: "blap.com", 479 RemoteId: remoteId, 480 Topics: "", 481 CreatorId: model.NewId(), 482 } 483 484 _, err := ss.RemoteCluster().Save(rc) 485 require.NoError(t, err) 486 487 testData := []struct { 488 topics string 489 expected string 490 }{ 491 {topics: "", expected: ""}, 492 {topics: " ", expected: ""}, 493 {topics: "share", expected: " share "}, 494 {topics: " share ", expected: " share "}, 495 {topics: "share incident", expected: " share incident "}, 496 {topics: " share incident ", expected: " share incident "}, 497 } 498 499 for _, tt := range testData { 500 _, err = ss.RemoteCluster().UpdateTopics(remoteId, tt.topics) 501 require.NoError(t, err) 502 503 rcUpdated, err := ss.RemoteCluster().Get(remoteId) 504 require.NoError(t, err) 505 506 require.Equal(t, tt.expected, rcUpdated.Topics) 507 } 508 } 509 510 func clearRemoteClusters(ss store.Store) error { 511 list, err := ss.RemoteCluster().GetAll(model.RemoteClusterQueryFilter{}) 512 if err != nil { 513 return err 514 } 515 516 for _, rc := range list { 517 if _, err := ss.RemoteCluster().Delete(rc.RemoteId); err != nil { 518 return err 519 } 520 } 521 return nil 522 }