...

Source file src/github.com/launchdarkly/go-server-sdk/v6/internal/sharedtest/mocks/spy_data_store.go

Documentation: github.com/launchdarkly/go-server-sdk/v6/internal/sharedtest/mocks

     1  package mocks
     2  
     3  import (
     4  	"encoding/json"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/launchdarkly/go-server-sdk/v6/internal/datakinds"
    10  	"github.com/launchdarkly/go-server-sdk/v6/subsystems"
    11  	"github.com/launchdarkly/go-server-sdk/v6/subsystems/ldstoretypes"
    12  	"github.com/launchdarkly/go-server-sdk/v6/testhelpers/ldservices"
    13  
    14  	th "github.com/launchdarkly/go-test-helpers/v3"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  // CapturingDataStore is a DataStore implementation that records update operations for testing.
    20  type CapturingDataStore struct {
    21  	realStore               subsystems.DataStore
    22  	statusMonitoringEnabled bool
    23  	fakeError               error
    24  	inits                   chan []ldstoretypes.Collection
    25  	upserts                 chan UpsertParams
    26  	lock                    sync.Mutex
    27  }
    28  
    29  // UpsertParams holds the parameters of an Upsert operation captured by CapturingDataStore.
    30  type UpsertParams struct {
    31  	Kind ldstoretypes.DataKind
    32  	Key  string
    33  	Item ldstoretypes.ItemDescriptor
    34  }
    35  
    36  // NewCapturingDataStore creates an instance of CapturingDataStore.
    37  func NewCapturingDataStore(realStore subsystems.DataStore) *CapturingDataStore {
    38  	return &CapturingDataStore{
    39  		realStore:               realStore,
    40  		inits:                   make(chan []ldstoretypes.Collection, 10),
    41  		upserts:                 make(chan UpsertParams, 10),
    42  		statusMonitoringEnabled: true,
    43  	}
    44  }
    45  
    46  // Init is a standard DataStore method.
    47  func (d *CapturingDataStore) Init(allData []ldstoretypes.Collection) error {
    48  	for _, coll := range allData {
    49  		AssertNotNil(coll.Kind)
    50  	}
    51  	d.inits <- allData
    52  	_ = d.realStore.Init(allData)
    53  	d.lock.Lock()
    54  	defer d.lock.Unlock()
    55  	return d.fakeError
    56  }
    57  
    58  // Get is a standard DataStore method.
    59  func (d *CapturingDataStore) Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.ItemDescriptor, error) {
    60  	AssertNotNil(kind)
    61  	if d.fakeError != nil {
    62  		return ldstoretypes.ItemDescriptor{}.NotFound(), d.fakeError
    63  	}
    64  	return d.realStore.Get(kind, key)
    65  }
    66  
    67  // GetAll is a standard DataStore method.
    68  func (d *CapturingDataStore) GetAll(kind ldstoretypes.DataKind) ([]ldstoretypes.KeyedItemDescriptor, error) {
    69  	AssertNotNil(kind)
    70  	if d.fakeError != nil {
    71  		return nil, d.fakeError
    72  	}
    73  	return d.realStore.GetAll(kind)
    74  }
    75  
    76  // Upsert in this test type does nothing but capture its parameters.
    77  func (d *CapturingDataStore) Upsert(
    78  	kind ldstoretypes.DataKind,
    79  	key string,
    80  	newItem ldstoretypes.ItemDescriptor,
    81  ) (bool, error) {
    82  	AssertNotNil(kind)
    83  	d.upserts <- UpsertParams{kind, key, newItem}
    84  	updated, _ := d.realStore.Upsert(kind, key, newItem)
    85  	d.lock.Lock()
    86  	defer d.lock.Unlock()
    87  	return updated, d.fakeError
    88  }
    89  
    90  // IsInitialized in this test type always returns true.
    91  func (d *CapturingDataStore) IsInitialized() bool {
    92  	return true
    93  }
    94  
    95  // IsStatusMonitoringEnabled in this test type returns true by default, but can be changed
    96  // with SetStatusMonitoringEnabled.
    97  func (d *CapturingDataStore) IsStatusMonitoringEnabled() bool {
    98  	d.lock.Lock()
    99  	defer d.lock.Unlock()
   100  	return d.statusMonitoringEnabled
   101  }
   102  
   103  // Close in this test type is a no-op.
   104  func (d *CapturingDataStore) Close() error {
   105  	return nil
   106  }
   107  
   108  // SetStatusMonitoringEnabled changes the value returned by IsStatusMonitoringEnabled.
   109  func (d *CapturingDataStore) SetStatusMonitoringEnabled(statusMonitoringEnabled bool) {
   110  	d.lock.Lock()
   111  	defer d.lock.Unlock()
   112  	d.statusMonitoringEnabled = statusMonitoringEnabled
   113  }
   114  
   115  // SetFakeError causes subsequent Init or Upsert calls to return an error.
   116  func (d *CapturingDataStore) SetFakeError(fakeError error) {
   117  	d.lock.Lock()
   118  	defer d.lock.Unlock()
   119  	d.fakeError = fakeError
   120  }
   121  
   122  // WaitForNextInit waits for an Init call.
   123  func (d *CapturingDataStore) WaitForNextInit(
   124  	t *testing.T,
   125  	timeout time.Duration,
   126  ) []ldstoretypes.Collection {
   127  	return th.RequireValue(t, d.inits, timeout, "timed out before receiving expected init")
   128  }
   129  
   130  // WaitForInit waits for an Init call and verifies that it matches the expected data.
   131  func (d *CapturingDataStore) WaitForInit(
   132  	t *testing.T,
   133  	data *ldservices.ServerSDKData,
   134  	timeout time.Duration,
   135  ) {
   136  	inited := th.RequireValue(t, d.inits, timeout, "timed out before receiving expected init")
   137  	assertReceivedInitDataEquals(t, data, inited)
   138  }
   139  
   140  // WaitForNextUpsert waits for an Upsert call.
   141  func (d *CapturingDataStore) WaitForNextUpsert(
   142  	t *testing.T,
   143  	timeout time.Duration,
   144  ) UpsertParams {
   145  	return th.RequireValue(t, d.upserts, timeout, "timed out before receiving expected update")
   146  }
   147  
   148  // WaitForUpsert waits for an Upsert call and verifies that it matches the expected data.
   149  func (d *CapturingDataStore) WaitForUpsert(
   150  	t *testing.T,
   151  	kind ldstoretypes.DataKind,
   152  	key string,
   153  	version int,
   154  	timeout time.Duration,
   155  ) UpsertParams {
   156  	upserted := d.WaitForNextUpsert(t, timeout)
   157  	assert.Equal(t, kind, upserted.Kind)
   158  	assert.Equal(t, key, upserted.Key)
   159  	assert.Equal(t, version, upserted.Item.Version)
   160  	assert.NotNil(t, upserted.Item.Item)
   161  	return upserted
   162  }
   163  
   164  // WaitForDelete waits for an Upsert call that is expected to delete a data item.
   165  func (d *CapturingDataStore) WaitForDelete(
   166  	t *testing.T,
   167  	kind ldstoretypes.DataKind,
   168  	key string,
   169  	version int,
   170  	timeout time.Duration,
   171  ) {
   172  	upserted := d.WaitForNextUpsert(t, timeout)
   173  	assert.Equal(t, kind, upserted.Kind)
   174  	assert.Equal(t, key, upserted.Key)
   175  	assert.Equal(t, version, upserted.Item.Version)
   176  	assert.Nil(t, upserted.Item.Item)
   177  }
   178  
   179  func assertReceivedInitDataEquals(
   180  	t *testing.T,
   181  	expected *ldservices.ServerSDKData,
   182  	received []ldstoretypes.Collection,
   183  ) {
   184  	assert.Equal(t, 2, len(received))
   185  	for _, coll := range received {
   186  		var itemsMap map[string]interface{}
   187  		switch coll.Kind {
   188  		case datakinds.Features:
   189  			itemsMap = expected.FlagsMap
   190  		case datakinds.Segments:
   191  			itemsMap = expected.SegmentsMap
   192  		default:
   193  			assert.Fail(t, "received unknown data kind: %s", coll.Kind)
   194  		}
   195  		assert.Equal(t, len(itemsMap), len(coll.Items))
   196  		for _, item := range coll.Items {
   197  			found, ok := itemsMap[item.Key]
   198  			assert.True(t, ok, item.Key)
   199  			bytes, _ := json.Marshal(found)
   200  			var props map[string]interface{}
   201  			assert.NoError(t, json.Unmarshal(bytes, &props))
   202  			assert.Equal(t, props["version"].(float64), float64(item.Item.Version))
   203  		}
   204  	}
   205  }
   206  

View as plain text