github.com/xmidt-org/webpa-common@v1.11.9/secure/key/cache_test.go (about)

     1  package key
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/mock"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  func makeExpectedPairs(count int) (expectedKeyIDs []string, expectedPairs map[string]Pair) {
    14  	expectedPairs = make(map[string]Pair, count)
    15  	for index := 0; index < count; index++ {
    16  		keyID := fmt.Sprintf("key#%d", index)
    17  		expectedKeyIDs = append(expectedKeyIDs, keyID)
    18  		expectedPairs[keyID] = &MockPair{}
    19  	}
    20  
    21  	return
    22  }
    23  
    24  func assertExpectationsForPairs(t *testing.T, pairs map[string]Pair) {
    25  	for _, pair := range pairs {
    26  		if mockPair, ok := pair.(*MockPair); ok {
    27  			mock.AssertExpectationsForObjects(t, mockPair.Mock)
    28  		}
    29  	}
    30  }
    31  
    32  func TestBasicCacheStoreAndLoad(t *testing.T) {
    33  	assert := assert.New(t)
    34  
    35  	cache := basicCache{}
    36  	assert.Nil(cache.load())
    37  	cache.store(123)
    38  	assert.Equal(123, cache.load())
    39  }
    40  
    41  func TestSingleCacheResolveKey(t *testing.T) {
    42  	assert := assert.New(t)
    43  
    44  	const keyID = "TestSingleCacheResolveKey"
    45  	expectedPair := &MockPair{}
    46  	resolver := &MockResolver{}
    47  	resolver.On("ResolveKey", keyID).Return(expectedPair, nil).Once()
    48  
    49  	cache := singleCache{
    50  		basicCache{
    51  			delegate: resolver,
    52  		},
    53  	}
    54  
    55  	waitGroup := &sync.WaitGroup{}
    56  	waitGroup.Add(2)
    57  	barrier := make(chan struct{})
    58  
    59  	for repeat := 0; repeat < 2; repeat++ {
    60  		go func() {
    61  			defer waitGroup.Done()
    62  			<-barrier
    63  			actualPair, err := cache.ResolveKey(keyID)
    64  			assert.Equal(expectedPair, actualPair)
    65  			assert.Nil(err)
    66  		}()
    67  	}
    68  
    69  	close(barrier)
    70  	waitGroup.Wait()
    71  
    72  	mock.AssertExpectationsForObjects(t, expectedPair.Mock)
    73  	mock.AssertExpectationsForObjects(t, resolver.Mock)
    74  	assert.Equal(expectedPair, cache.load())
    75  }
    76  
    77  func TestSingleCacheResolveKeyError(t *testing.T) {
    78  	assert := assert.New(t)
    79  
    80  	const keyID = "TestSingleCacheResolveKeyError"
    81  	expectedError := errors.New("TestSingleCacheResolveKeyError")
    82  	resolver := &MockResolver{}
    83  	resolver.On("ResolveKey", keyID).Return(nil, expectedError).Twice()
    84  
    85  	cache := singleCache{
    86  		basicCache{
    87  			delegate: resolver,
    88  		},
    89  	}
    90  
    91  	waitGroup := &sync.WaitGroup{}
    92  	waitGroup.Add(2)
    93  	barrier := make(chan struct{})
    94  
    95  	for repeat := 0; repeat < 2; repeat++ {
    96  		go func() {
    97  			defer waitGroup.Done()
    98  			<-barrier
    99  			pair, err := cache.ResolveKey(keyID)
   100  			assert.Nil(pair)
   101  			assert.Equal(expectedError, err)
   102  		}()
   103  	}
   104  
   105  	close(barrier)
   106  	waitGroup.Wait()
   107  
   108  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   109  	assert.Nil(cache.load())
   110  }
   111  
   112  func TestSingleCacheUpdateKeys(t *testing.T) {
   113  	assert := assert.New(t)
   114  
   115  	expectedPair := &MockPair{}
   116  	resolver := &MockResolver{}
   117  	resolver.On("ResolveKey", dummyKeyId).Return(expectedPair, nil).Once()
   118  
   119  	cache := singleCache{
   120  		basicCache{
   121  			delegate: resolver,
   122  		},
   123  	}
   124  
   125  	count, errors := cache.UpdateKeys()
   126  	mock.AssertExpectationsForObjects(t, expectedPair.Mock)
   127  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   128  	assert.Equal(1, count)
   129  	assert.Nil(errors)
   130  }
   131  
   132  func TestSingleCacheUpdateKeysError(t *testing.T) {
   133  	assert := assert.New(t)
   134  
   135  	expectedError := errors.New("TestSingleCacheUpdateKeysError")
   136  	resolver := &MockResolver{}
   137  	resolver.On("ResolveKey", dummyKeyId).Return(nil, expectedError).Once()
   138  
   139  	cache := singleCache{
   140  		basicCache{
   141  			delegate: resolver,
   142  		},
   143  	}
   144  
   145  	count, errors := cache.UpdateKeys()
   146  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   147  	assert.Equal(1, count)
   148  	assert.Equal([]error{expectedError}, errors)
   149  
   150  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   151  }
   152  
   153  func TestSingleCacheUpdateKeysSequence(t *testing.T) {
   154  	assert := assert.New(t)
   155  
   156  	const keyID = "TestSingleCacheUpdateKeysSequence"
   157  	expectedError := errors.New("TestSingleCacheUpdateKeysSequence")
   158  	oldPair := &MockPair{}
   159  	newPair := &MockPair{}
   160  	resolver := &MockResolver{}
   161  	resolver.On("ResolveKey", keyID).Return(oldPair, nil).Once()
   162  	resolver.On("ResolveKey", dummyKeyId).Return(nil, expectedError).Once()
   163  	resolver.On("ResolveKey", dummyKeyId).Return(newPair, nil).Once()
   164  
   165  	cache := singleCache{
   166  		basicCache{
   167  			delegate: resolver,
   168  		},
   169  	}
   170  
   171  	firstPair, err := cache.ResolveKey(keyID)
   172  	assert.Equal(oldPair, firstPair)
   173  	assert.Nil(err)
   174  
   175  	count, errors := cache.UpdateKeys()
   176  	assert.Equal(1, count)
   177  	assert.Equal([]error{expectedError}, errors)
   178  
   179  	// resolving should pull the key from the cache
   180  	firstPair, err = cache.ResolveKey(keyID)
   181  	assert.Equal(oldPair, firstPair)
   182  	assert.Nil(err)
   183  
   184  	// this time, the mock will succeed
   185  	count, errors = cache.UpdateKeys()
   186  	assert.Equal(1, count)
   187  	assert.Len(errors, 0)
   188  
   189  	// resolving should pull the *new* key from the cache
   190  	secondPair, err := cache.ResolveKey(keyID)
   191  	assert.Equal(newPair, secondPair)
   192  	assert.Nil(err)
   193  
   194  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   195  }
   196  
   197  func TestMultiCacheResolveKey(t *testing.T) {
   198  	assert := assert.New(t)
   199  
   200  	expectedKeyIDs, expectedPairs := makeExpectedPairs(2)
   201  	resolver := &MockResolver{}
   202  	for _, keyID := range expectedKeyIDs {
   203  		resolver.On("ResolveKey", keyID).Return(expectedPairs[keyID], nil).Once()
   204  	}
   205  
   206  	cache := multiCache{
   207  		basicCache{
   208  			delegate: resolver,
   209  		},
   210  	}
   211  
   212  	// spawn twice the number of routines as keys so
   213  	// that we test concurrently resolving keys from the cache
   214  	// and from the delegate
   215  	waitGroup := &sync.WaitGroup{}
   216  	waitGroup.Add(5 * len(expectedKeyIDs))
   217  	barrier := make(chan struct{})
   218  
   219  	for repeat := 0; repeat < 5; repeat++ {
   220  		for _, keyID := range expectedKeyIDs {
   221  			go func(keyID string, expectedPair Pair) {
   222  				t.Logf("keyID=%s", keyID)
   223  				defer waitGroup.Done()
   224  				<-barrier
   225  				pair, err := cache.ResolveKey(keyID)
   226  				assert.Equal(expectedPair, pair)
   227  				assert.Nil(err)
   228  			}(keyID, expectedPairs[keyID])
   229  		}
   230  	}
   231  
   232  	close(barrier)
   233  	waitGroup.Wait()
   234  
   235  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   236  	assertExpectationsForPairs(t, expectedPairs)
   237  }
   238  
   239  func TestMultiCacheResolveKeyError(t *testing.T) {
   240  	assert := assert.New(t)
   241  
   242  	expectedError := errors.New("TestMultiCacheResolveKeyError")
   243  	expectedKeyIDs, _ := makeExpectedPairs(2)
   244  	resolver := &MockResolver{}
   245  	for _, keyID := range expectedKeyIDs {
   246  		resolver.On("ResolveKey", keyID).Return(nil, expectedError).Twice()
   247  	}
   248  
   249  	cache := multiCache{
   250  		basicCache{
   251  			delegate: resolver,
   252  		},
   253  	}
   254  
   255  	// spawn twice the number of routines as keys so
   256  	// that we test concurrently resolving keys from the cache
   257  	// and from the delegate
   258  	waitGroup := &sync.WaitGroup{}
   259  	waitGroup.Add(2 * len(expectedKeyIDs))
   260  	barrier := make(chan struct{})
   261  
   262  	for repeat := 0; repeat < 2; repeat++ {
   263  		for _, keyID := range expectedKeyIDs {
   264  			go func(keyID string) {
   265  				defer waitGroup.Done()
   266  				<-barrier
   267  				key, err := cache.ResolveKey(keyID)
   268  				assert.Nil(key)
   269  				assert.Equal(expectedError, err)
   270  			}(keyID)
   271  		}
   272  	}
   273  
   274  	close(barrier)
   275  	waitGroup.Wait()
   276  
   277  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   278  }
   279  
   280  func TestMultiCacheUpdateKeys(t *testing.T) {
   281  	assert := assert.New(t)
   282  
   283  	resolver := &MockResolver{}
   284  	expectedKeyIDs, expectedPairs := makeExpectedPairs(2)
   285  	t.Logf("expectedKeyIDs: %s", expectedKeyIDs)
   286  
   287  	for _, keyID := range expectedKeyIDs {
   288  		resolver.On("ResolveKey", keyID).Return(expectedPairs[keyID], nil).Twice()
   289  	}
   290  
   291  	cache := multiCache{
   292  		basicCache{
   293  			delegate: resolver,
   294  		},
   295  	}
   296  
   297  	count, errors := cache.UpdateKeys()
   298  	assert.Equal(0, count)
   299  	assert.Len(errors, 0)
   300  
   301  	for _, keyID := range expectedKeyIDs {
   302  		pair, err := cache.ResolveKey(keyID)
   303  		assert.Equal(expectedPairs[keyID], pair)
   304  		assert.Nil(err)
   305  	}
   306  
   307  	count, errors = cache.UpdateKeys()
   308  	assert.Equal(len(expectedKeyIDs), count)
   309  	assert.Len(errors, 0)
   310  
   311  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   312  	assertExpectationsForPairs(t, expectedPairs)
   313  }
   314  
   315  func TestMultiCacheUpdateKeysError(t *testing.T) {
   316  	assert := assert.New(t)
   317  
   318  	expectedError := errors.New("TestMultiCacheUpdateKeysError")
   319  	expectedKeyIDs, _ := makeExpectedPairs(2)
   320  	resolver := &MockResolver{}
   321  	for _, keyID := range expectedKeyIDs {
   322  		resolver.On("ResolveKey", keyID).Return(nil, expectedError).Once()
   323  	}
   324  
   325  	cache := multiCache{
   326  		basicCache{
   327  			delegate: resolver,
   328  		},
   329  	}
   330  
   331  	count, errors := cache.UpdateKeys()
   332  	assert.Equal(0, count)
   333  	assert.Len(errors, 0)
   334  
   335  	for _, keyID := range expectedKeyIDs {
   336  		key, err := cache.ResolveKey(keyID)
   337  		assert.Nil(key)
   338  		assert.Equal(expectedError, err)
   339  	}
   340  
   341  	// UpdateKeys should still not do anything, because
   342  	// nothing should be in the cache due to errors
   343  	count, errors = cache.UpdateKeys()
   344  	assert.Equal(0, count)
   345  	assert.Len(errors, 0)
   346  
   347  	mock.AssertExpectationsForObjects(t, resolver.Mock)
   348  }
   349  
   350  func TestMultiCacheUpdateKeysSequence(t *testing.T) {
   351  	assert := assert.New(t)
   352  
   353  	const keyID = "TestMultiCacheUpdateKeysSequence"
   354  	expectedError := errors.New("TestMultiCacheUpdateKeysSequence")
   355  	oldPair := &MockPair{}
   356  	newPair := &MockPair{}
   357  
   358  	resolver := &MockResolver{}
   359  	resolver.On("ResolveKey", keyID).Return(oldPair, nil).Once()
   360  	resolver.On("ResolveKey", keyID).Return(nil, expectedError).Once()
   361  	resolver.On("ResolveKey", keyID).Return(newPair, nil).Once()
   362  
   363  	cache := multiCache{
   364  		basicCache{
   365  			delegate: resolver,
   366  		},
   367  	}
   368  
   369  	pair, err := cache.ResolveKey(keyID)
   370  	assert.Equal(oldPair, pair)
   371  	assert.Nil(err)
   372  
   373  	// an error should leave the existing key alone
   374  	count, errors := cache.UpdateKeys()
   375  	assert.Equal(1, count)
   376  	assert.Equal([]error{expectedError}, errors)
   377  
   378  	// the key should resolve to the old key from the cache
   379  	pair, err = cache.ResolveKey(keyID)
   380  	assert.Equal(oldPair, pair)
   381  	assert.Nil(err)
   382  
   383  	// again, this time the mock will succeed
   384  	count, errors = cache.UpdateKeys()
   385  	assert.Equal(1, count)
   386  	assert.Len(errors, 0)
   387  
   388  	// resolving a key should show the new value now
   389  	pair, err = cache.ResolveKey(keyID)
   390  	assert.Equal(newPair, pair)
   391  	assert.Nil(err)
   392  
   393  	mock.AssertExpectationsForObjects(t, resolver.Mock, oldPair.Mock, newPair.Mock)
   394  }
   395  
   396  func TestNewUpdaterNoRunnable(t *testing.T) {
   397  	assert := assert.New(t)
   398  
   399  	keyCache := &MockCache{}
   400  
   401  	var testData = []struct {
   402  		updateInterval time.Duration
   403  		keyCache       Cache
   404  	}{
   405  		{
   406  			updateInterval: -1,
   407  			keyCache:       keyCache,
   408  		},
   409  		{
   410  			updateInterval: 0,
   411  			keyCache:       keyCache,
   412  		},
   413  		{
   414  			updateInterval: 1232354,
   415  			keyCache:       nil,
   416  		},
   417  	}
   418  
   419  	for _, record := range testData {
   420  		t.Log(record)
   421  		updater := NewUpdater(record.updateInterval, record.keyCache)
   422  		assert.Nil(updater)
   423  	}
   424  
   425  	mock.AssertExpectationsForObjects(t, keyCache.Mock)
   426  }
   427  
   428  func TestNewUpdater(t *testing.T) {
   429  	assert := assert.New(t)
   430  
   431  	updateKeysCalled := make(chan struct{})
   432  	runner := func(mock.Arguments) {
   433  		defer func() {
   434  			recover() // ignore panics from multiple closes
   435  		}()
   436  
   437  		close(updateKeysCalled)
   438  	}
   439  
   440  	keyCache := &MockCache{}
   441  	keyCache.On("UpdateKeys").Return(0, nil).Run(runner)
   442  
   443  	if updater := NewUpdater(100*time.Millisecond, keyCache); assert.NotNil(updater) {
   444  		waitGroup := &sync.WaitGroup{}
   445  		shutdown := make(chan struct{})
   446  		updater.Run(waitGroup, shutdown)
   447  
   448  		// we only care that the updater called UpdateKeys() at least once
   449  		<-updateKeysCalled
   450  		close(shutdown)
   451  		waitGroup.Wait()
   452  	}
   453  }