github.com/vmware/transport-go@v1.3.4/bus/store_sync_service_test.go (about) 1 // Copyright 2019-2020 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 package bus 5 6 import ( 7 "github.com/google/uuid" 8 "github.com/stretchr/testify/assert" 9 "github.com/vmware/transport-go/model" 10 "reflect" 11 "strings" 12 "sync" 13 "testing" 14 ) 15 16 func testStoreSyncService() (*storeSyncService, EventBus) { 17 bus := newTestEventBus() 18 return newStoreSyncService(bus), bus 19 } 20 21 func TestStoreSyncService_NewConnection(t *testing.T) { 22 service, bus := testStoreSyncService() 23 24 // verify that the service ignores non transport-store-sync events 25 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, "galactic-channel", nil) 26 assert.Equal(t, len(service.syncClients), 0) 27 28 syncChan := "transport-store-sync.1" 29 30 bus.GetChannelManager().CreateChannel(syncChan) 31 32 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 33 assert.Equal(t, len(service.syncClients), 1) 34 } 35 36 func TestStoreSyncService_OpenStoreErrors(t *testing.T) { 37 _, bus := testStoreSyncService() 38 39 syncChan := "transport-store-sync.1" 40 bus.GetChannelManager().CreateChannel(syncChan) 41 42 mh, _ := bus.ListenStream(syncChan) 43 wg := sync.WaitGroup{} 44 var errors []*model.Response 45 mh.Handle(func(message *model.Message) { 46 errors = append(errors, message.Payload.(*model.Response)) 47 wg.Done() 48 }, func(e error) { 49 assert.Fail(t, "Unexpected error") 50 }) 51 52 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 53 id := uuid.New() 54 bus.SendRequestMessage(syncChan, "invalid-request", nil) 55 bus.SendRequestMessage(syncChan, &model.Request{ 56 Request: openStoreRequest, 57 Payload: "invalid-payload", 58 Id: &id, 59 }, nil) 60 61 wg.Add(1) 62 bus.SendRequestMessage(syncChan, &model.Request{ 63 Request: openStoreRequest, 64 Payload: make(map[string]interface{}), 65 Id: &id, 66 }, nil) 67 wg.Wait() 68 69 assert.Equal(t, errors[0].Id, &id) 70 assert.True(t, errors[0].Error) 71 assert.Equal(t, errors[0].ErrorMessage, "Invalid OpenStoreRequest") 72 73 wg.Add(1) 74 bus.SendRequestMessage(syncChan, &model.Request{ 75 Request: openStoreRequest, 76 Payload: map[string]interface{}{"storeId": "non-existing-store"}, 77 Id: &id, 78 }, nil) 79 wg.Wait() 80 81 assert.Equal(t, errors[1].Id, &id) 82 assert.True(t, errors[1].Error) 83 assert.Equal(t, errors[1].ErrorMessage, "Cannot open non-existing store: non-existing-store") 84 } 85 86 func TestStoreSyncService_OpenStore(t *testing.T) { 87 service, bus := testStoreSyncService() 88 89 store := bus.GetStoreManager().CreateStoreWithType( 90 "test-store", reflect.TypeOf(&MockStoreItem{})) 91 store.Populate(map[string]interface{}{ 92 "item1": &MockStoreItem{From: "test", Message: "test-message"}, 93 "item2": &MockStoreItem{From: "test2", Message: uuid.New().String()}, 94 }) 95 96 syncChan := "transport-store-sync.1" 97 bus.GetChannelManager().CreateChannel(syncChan) 98 99 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 100 101 wg := sync.WaitGroup{} 102 var syncResp []interface{} 103 104 mh, _ := bus.ListenStream(syncChan) 105 mh.Handle(func(message *model.Message) { 106 syncResp = append(syncResp, message.Payload) 107 wg.Done() 108 }, func(e error) { 109 assert.Fail(t, "Unexpected error") 110 }) 111 112 wg.Add(1) 113 bus.SendRequestMessage(syncChan, &model.Request{ 114 Request: openStoreRequest, 115 Payload: map[string]interface{}{"storeId": "test-store"}, 116 }, nil) 117 wg.Wait() 118 119 assert.Equal(t, len(service.syncClients[syncChan].openStores), 1) 120 assert.Equal(t, len(service.syncStoreListeners), 1) 121 assert.Equal(t, service.syncStoreListeners["test-store"].clientSyncChannels[syncChan], true) 122 123 resp := syncResp[0].(*model.StoreContentResponse) 124 125 assert.Equal(t, resp.StoreId, "test-store") 126 items, version := store.AllValuesAndVersion() 127 128 assert.Equal(t, resp.StoreVersion, version) 129 assert.Equal(t, resp.Items, items) 130 assert.Equal(t, resp.ResponseType, "storeContentResponse") 131 132 // try subscribing to the same sync channel again 133 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 134 assert.Equal(t, len(service.syncClients[syncChan].openStores), 1) 135 136 wg.Add(1) 137 bus.SendRequestMessage(syncChan, &model.Request{ 138 Request: openStoreRequest, 139 Payload: map[string]interface{}{"storeId": "test-store"}, 140 }, nil) 141 wg.Wait() 142 143 assert.Equal(t, len(syncResp), 2) 144 assert.Equal(t, syncResp[1].(*model.StoreContentResponse).ResponseType, "storeContentResponse") 145 146 syncChan2 := "transport-store-sync.2" 147 bus.GetChannelManager().CreateChannel(syncChan2) 148 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan2, nil) 149 150 mh2, _ := bus.ListenStream(syncChan2) 151 mh2.Handle(func(message *model.Message) { 152 syncResp = append(syncResp, message.Payload) 153 wg.Done() 154 }, func(e error) { 155 assert.Fail(t, "Unexpected error") 156 }) 157 158 wg.Add(1) 159 bus.SendRequestMessage(syncChan2, &model.Request{ 160 Request: openStoreRequest, 161 Payload: map[string]interface{}{"storeId": "test-store"}, 162 }, nil) 163 wg.Wait() 164 165 assert.Equal(t, len(syncResp), 3) 166 assert.Equal(t, syncResp[2].(*model.StoreContentResponse).ResponseType, "storeContentResponse") 167 168 assert.Equal(t, len(service.syncClients), 2) 169 assert.Equal(t, len(service.syncClients[syncChan].openStores), 1) 170 assert.Equal(t, len(service.syncClients[syncChan2].openStores), 1) 171 assert.Equal(t, service.syncClients[syncChan2].openStores["test-store"], true) 172 173 assert.Equal(t, len(service.syncStoreListeners["test-store"].clientSyncChannels), 2) 174 assert.Equal(t, service.syncStoreListeners["test-store"].clientSyncChannels[syncChan2], true) 175 176 bus.SendMonitorEvent(ChannelDestroyedEvt, syncChan, nil) 177 178 assert.Equal(t, len(service.syncClients), 1) 179 assert.Equal(t, len(service.syncClients[syncChan2].openStores), 1) 180 assert.Equal(t, service.syncClients[syncChan2].openStores["test-store"], true) 181 assert.Equal(t, len(service.syncStoreListeners["test-store"].clientSyncChannels), 1) 182 assert.Equal(t, service.syncStoreListeners["test-store"].clientSyncChannels[syncChan2], true) 183 184 bus.SendMonitorEvent(ChannelDestroyedEvt, syncChan2, nil) 185 186 assert.Equal(t, len(service.syncClients), 0) 187 assert.Equal(t, len(service.syncStoreListeners), 0) 188 189 // try closing the syncChan2 again 190 bus.SendMonitorEvent(ChannelDestroyedEvt, syncChan2, nil) 191 } 192 193 func TestStoreSyncService_CloseStore(t *testing.T) { 194 service, bus := testStoreSyncService() 195 196 store := bus.GetStoreManager().CreateStoreWithType( 197 "test-store", reflect.TypeOf(&MockStoreItem{})) 198 store.Populate(map[string]interface{}{ 199 "item1": &MockStoreItem{From: "test", Message: "test-message"}, 200 "item2": &MockStoreItem{From: "test2", Message: uuid.New().String()}, 201 }) 202 203 syncChan := "transport-store-sync.1" 204 bus.GetChannelManager().CreateChannel(syncChan) 205 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 206 207 syncChan2 := "transport-store-sync.2" 208 bus.GetChannelManager().CreateChannel(syncChan2) 209 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan2, nil) 210 211 wg := sync.WaitGroup{} 212 var syncResp1 []interface{} 213 214 mh, _ := bus.ListenStream(syncChan) 215 mh.Handle(func(message *model.Message) { 216 syncResp1 = append(syncResp1, message.Payload) 217 wg.Done() 218 }, func(e error) { 219 assert.Fail(t, "Unexpected error") 220 }) 221 222 mh2, _ := bus.ListenStream(syncChan2) 223 mh2.Handle(func(message *model.Message) { 224 wg.Done() 225 }, func(e error) { 226 assert.Fail(t, "Unexpected error") 227 }) 228 229 id := uuid.New() 230 231 wg.Add(2) 232 bus.SendRequestMessage(syncChan, &model.Request{ 233 Request: openStoreRequest, 234 Payload: map[string]interface{}{"storeId": "test-store"}, 235 }, nil) 236 bus.SendRequestMessage(syncChan2, &model.Request{ 237 Request: openStoreRequest, 238 Payload: map[string]interface{}{"storeId": "test-store"}, 239 }, nil) 240 wg.Wait() 241 242 assert.Equal(t, len(service.syncStoreListeners["test-store"].clientSyncChannels), 2) 243 244 bus.SendRequestMessage(syncChan, &model.Request{ 245 Request: closeStoreRequest, 246 Payload: map[string]interface{}{"storeId": "test-store"}, 247 Id: &id, 248 }, nil) 249 250 wg.Add(2) 251 bus.SendRequestMessage(syncChan, &model.Request{ 252 Request: closeStoreRequest, 253 Payload: make(map[string]interface{}), 254 Id: &id, 255 }, nil) 256 bus.SendRequestMessage(syncChan2, &model.Request{ 257 Request: closeStoreRequest, 258 Payload: map[string]interface{}{"storeId": ""}, 259 Id: &id, 260 }, nil) 261 wg.Wait() 262 263 assert.Equal(t, syncResp1[1].(*model.Response).ErrorMessage, "Invalid CloseStoreRequest") 264 assert.Equal(t, syncResp1[1].(*model.Response).Id, &id) 265 assert.Equal(t, syncResp1[1].(*model.Response).Error, true) 266 267 service.lock.Lock() 268 assert.Equal(t, len(service.syncStoreListeners["test-store"].clientSyncChannels), 1) 269 assert.Equal(t, service.syncStoreListeners["test-store"].clientSyncChannels[syncChan2], true) 270 assert.Equal(t, len(service.syncClients[syncChan].openStores), 0) 271 assert.Equal(t, len(service.syncClients[syncChan2].openStores), 1) 272 service.lock.Unlock() 273 274 bus.SendRequestMessage(syncChan2, &model.Request{ 275 Request: closeStoreRequest, 276 Payload: map[string]interface{}{"storeId": "test-store"}, 277 Id: &id, 278 }, nil) 279 280 wg.Add(2) 281 bus.SendRequestMessage(syncChan2, &model.Request{ 282 Request: closeStoreRequest, 283 Payload: make(map[string]interface{}), 284 Id: &id, 285 }, nil) 286 bus.SendRequestMessage(syncChan, &model.Request{ 287 Request: closeStoreRequest, 288 Payload: map[string]interface{}{"storeId": ""}, 289 Id: &id, 290 }, nil) 291 wg.Wait() 292 293 assert.Equal(t, syncResp1[2].(*model.Response).ErrorMessage, "Invalid CloseStoreRequest") 294 assert.Equal(t, syncResp1[2].(*model.Response).Id, &id) 295 assert.Equal(t, syncResp1[2].(*model.Response).Error, true) 296 297 service.lock.Lock() 298 assert.Equal(t, len(service.syncStoreListeners), 0) 299 assert.Equal(t, len(service.syncClients[syncChan].openStores), 0) 300 assert.Equal(t, len(service.syncClients[syncChan2].openStores), 0) 301 service.lock.Unlock() 302 } 303 304 func TestStoreSyncService_UpdateStoreErrors(t *testing.T) { 305 _, bus := testStoreSyncService() 306 307 syncChan := "transport-store-sync.1" 308 bus.GetChannelManager().CreateChannel(syncChan) 309 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 310 311 wg := sync.WaitGroup{} 312 var syncResp []interface{} 313 314 mh, _ := bus.ListenStream(syncChan) 315 mh.Handle(func(message *model.Message) { 316 syncResp = append(syncResp, message.Payload) 317 wg.Done() 318 }, func(e error) { 319 assert.Fail(t, "Unexpected error") 320 }) 321 322 id := uuid.New() 323 324 wg.Add(1) 325 bus.SendRequestMessage(syncChan, &model.Request{ 326 Request: updateStoreRequest, 327 Payload: map[string]interface{}{}, 328 Id: &id, 329 }, nil) 330 wg.Wait() 331 332 assert.Equal(t, syncResp[0].(*model.Response).ErrorMessage, "Invalid UpdateStoreRequest: missing storeId") 333 334 wg.Add(1) 335 bus.SendRequestMessage(syncChan, &model.Request{ 336 Request: updateStoreRequest, 337 Payload: map[string]interface{}{"storeId": "test-store"}, 338 Id: &id, 339 }, nil) 340 wg.Wait() 341 342 assert.Equal(t, syncResp[1].(*model.Response).ErrorMessage, "Invalid UpdateStoreRequest: missing itemId") 343 344 wg.Add(1) 345 bus.SendRequestMessage(syncChan, &model.Request{ 346 Request: updateStoreRequest, 347 Payload: map[string]interface{}{"storeId": "test-store", "itemId": "item1"}, 348 Id: &id, 349 }, nil) 350 wg.Wait() 351 352 assert.Equal(t, syncResp[2].(*model.Response).ErrorMessage, "Cannot update non-existing store: test-store") 353 } 354 355 func TestStoreSyncService_UpdateStore(t *testing.T) { 356 _, bus := testStoreSyncService() 357 358 store := bus.GetStoreManager().CreateStoreWithType( 359 "test-store", reflect.TypeOf(&MockStoreItem{})) 360 store.Populate(map[string]interface{}{ 361 "item1": &MockStoreItem{From: "test", Message: "test-message"}, 362 "item2": &MockStoreItem{From: "test2", Message: uuid.New().String()}, 363 }) 364 365 syncChan := "transport-store-sync.1" 366 bus.GetChannelManager().CreateChannel(syncChan) 367 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan, nil) 368 369 syncChan2 := "transport-store-sync.2" 370 bus.GetChannelManager().CreateChannel(syncChan2) 371 bus.SendMonitorEvent(FabricEndpointSubscribeEvt, syncChan2, nil) 372 373 wg := sync.WaitGroup{} 374 var syncResp1 []interface{} 375 var syncResp2 []interface{} 376 377 mh, _ := bus.ListenStream(syncChan) 378 mh.Handle(func(message *model.Message) { 379 syncResp1 = append(syncResp1, message.Payload) 380 wg.Done() 381 }, func(e error) { 382 assert.Fail(t, "Unexpected error") 383 }) 384 385 mh2, _ := bus.ListenStream(syncChan2) 386 mh2.Handle(func(message *model.Message) { 387 syncResp2 = append(syncResp2, message.Payload) 388 wg.Done() 389 }, func(e error) { 390 assert.Fail(t, "Unexpected error") 391 }) 392 393 wg.Add(2) 394 bus.SendRequestMessage(syncChan, &model.Request{ 395 Request: openStoreRequest, 396 Payload: map[string]interface{}{"storeId": "test-store"}, 397 }, nil) 398 bus.SendRequestMessage(syncChan2, &model.Request{ 399 Request: openStoreRequest, 400 Payload: map[string]interface{}{"storeId": "test-store"}, 401 }, nil) 402 wg.Wait() 403 404 assert.Equal(t, len(syncResp1), 1) 405 assert.Equal(t, len(syncResp2), 1) 406 407 wg.Add(2) 408 409 bus.SendRequestMessage(syncChan, &model.Request{ 410 Request: updateStoreRequest, 411 Payload: map[string]interface{}{ 412 "storeId": "test-store", 413 "itemId": "item3", 414 "newItemValue": map[string]interface{}{ 415 "From": "test3", 416 "Message": "test-message3", 417 }}, 418 }, nil) 419 420 wg.Wait() 421 422 assert.Equal(t, len(syncResp1), 2) 423 assert.Equal(t, len(syncResp2), 2) 424 425 assert.Equal(t, syncResp1[1].(*model.UpdateStoreResponse).ResponseType, "updateStoreResponse") 426 assert.Equal(t, syncResp1[1].(*model.UpdateStoreResponse).StoreId, "test-store") 427 assert.Equal(t, syncResp1[1].(*model.UpdateStoreResponse).StoreVersion, int64(2)) 428 assert.Equal(t, syncResp1[1].(*model.UpdateStoreResponse).NewItemValue, &MockStoreItem{ 429 From: "test3", 430 Message: "test-message3", 431 }) 432 433 assert.Equal(t, syncResp1[1], syncResp2[1]) 434 435 assert.Equal(t, store.GetValue("item3"), &MockStoreItem{ 436 From: "test3", 437 Message: "test-message3", 438 }) 439 440 wg.Add(2) 441 store.Remove("item2", "test-remove") 442 wg.Wait() 443 444 assert.Equal(t, len(syncResp1), 3) 445 assert.Equal(t, len(syncResp2), 3) 446 447 assert.Equal(t, syncResp1[2].(*model.UpdateStoreResponse).ResponseType, "updateStoreResponse") 448 assert.Equal(t, syncResp1[2].(*model.UpdateStoreResponse).StoreId, "test-store") 449 assert.Equal(t, syncResp1[2].(*model.UpdateStoreResponse).ItemId, "item2") 450 assert.Equal(t, syncResp1[2].(*model.UpdateStoreResponse).StoreVersion, int64(3)) 451 assert.Equal(t, syncResp1[2].(*model.UpdateStoreResponse).NewItemValue, nil) 452 453 assert.Equal(t, syncResp1[2], syncResp2[2]) 454 455 wg.Add(2) 456 store.Put("item1", &MockStoreItem{From: "u1", Message: "m1"}, nil) 457 wg.Wait() 458 459 assert.Equal(t, len(syncResp1), 4) 460 assert.Equal(t, len(syncResp2), 4) 461 462 assert.Equal(t, syncResp1[3].(*model.UpdateStoreResponse).ResponseType, "updateStoreResponse") 463 assert.Equal(t, syncResp1[3].(*model.UpdateStoreResponse).StoreId, "test-store") 464 assert.Equal(t, syncResp1[3].(*model.UpdateStoreResponse).ItemId, "item1") 465 assert.Equal(t, syncResp1[3].(*model.UpdateStoreResponse).StoreVersion, int64(4)) 466 assert.Equal(t, syncResp1[3].(*model.UpdateStoreResponse).NewItemValue, 467 &MockStoreItem{From: "u1", Message: "m1"}) 468 469 assert.Equal(t, syncResp1[3], syncResp2[3]) 470 471 bus.SendRequestMessage(syncChan, &model.Request{ 472 Request: updateStoreRequest, 473 Payload: map[string]interface{}{ 474 "storeId": "test-store", 475 "itemId": "item4", 476 "newItemValue": nil}, 477 }, nil) 478 479 wg.Add(2) 480 bus.SendRequestMessage(syncChan, &model.Request{ 481 Request: updateStoreRequest, 482 Payload: map[string]interface{}{ 483 "storeId": "test-store", 484 "itemId": "item3", 485 "newItemValue": nil}, 486 }, nil) 487 wg.Wait() 488 489 assert.Equal(t, len(syncResp1), 5) 490 assert.Equal(t, len(syncResp2), 5) 491 492 assert.Equal(t, syncResp1[4].(*model.UpdateStoreResponse).ResponseType, "updateStoreResponse") 493 assert.Equal(t, syncResp1[4].(*model.UpdateStoreResponse).StoreId, "test-store") 494 assert.Equal(t, syncResp1[4].(*model.UpdateStoreResponse).ItemId, "item3") 495 assert.Equal(t, syncResp1[4].(*model.UpdateStoreResponse).StoreVersion, int64(5)) 496 assert.Equal(t, syncResp1[4].(*model.UpdateStoreResponse).NewItemValue, nil) 497 498 assert.Equal(t, syncResp1[4], syncResp2[4]) 499 500 assert.Equal(t, store.GetValue("item3"), nil) 501 502 wg.Add(1) 503 bus.SendRequestMessage(syncChan, &model.Request{ 504 Request: updateStoreRequest, 505 Payload: map[string]interface{}{ 506 "storeId": "test-store", 507 "itemId": "item3", 508 "newItemValue": "test"}, 509 }, nil) 510 wg.Wait() 511 assert.Equal(t, len(syncResp1), 6) 512 assert.True(t, strings.HasPrefix(syncResp1[5].(*model.Response).ErrorMessage, 513 "Cannot deserialize UpdateStoreRequest item value:")) 514 }