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 }