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  }