public void GetCurrentKeyRing_WithExpiredExistingKeyRing_AllowsOneThreadToUpdate_ReturnsExistingKeyRingToOtherCallersWithoutBlocking() { // Arrange var originalKeyRing = new Mock <IKeyRing>().Object; var originalKeyRingTime = StringToDateTime("2015-03-01 00:00:00Z"); var updatedKeyRing = new Mock <IKeyRing>().Object; var updatedKeyRingTime = StringToDateTime("2015-03-02 00:00:00Z"); var mockCacheableKeyRingProvider = new Mock <ICacheableKeyRingProvider>(); var keyRingProvider = CreateKeyRingProvider(mockCacheableKeyRingProvider.Object); // In this test, the foreground thread acquires the critial section in GetCurrentKeyRing, // and the background thread returns the original key ring rather than blocking while // waiting for the foreground thread to update the key ring. TimeSpan testTimeout = TimeSpan.FromSeconds(10); IKeyRing keyRingReturnedToBackgroundThread = null; mockCacheableKeyRingProvider.Setup(o => o.GetCacheableKeyRing(originalKeyRingTime)) .Returns(new CacheableKeyRing(CancellationToken.None, StringToDateTime("2015-03-02 00:00:00Z"), originalKeyRing)); mockCacheableKeyRingProvider.Setup(o => o.GetCacheableKeyRing(updatedKeyRingTime)) .Returns <DateTimeOffset>(dto => { // at this point we're inside the critical section - spawn the background thread now var backgroundGetKeyRingTask = Task.Run(() => { keyRingReturnedToBackgroundThread = keyRingProvider.GetCurrentKeyRingCore(updatedKeyRingTime); }); Assert.True(backgroundGetKeyRingTask.Wait(testTimeout), "Test timed out."); return(new CacheableKeyRing(CancellationToken.None, StringToDateTime("2015-03-03 00:00:00Z"), updatedKeyRing)); }); // Assert - underlying provider only should have been called once with the updated time (by the foreground thread) Assert.Same(originalKeyRing, keyRingProvider.GetCurrentKeyRingCore(originalKeyRingTime)); Assert.Same(updatedKeyRing, keyRingProvider.GetCurrentKeyRingCore(updatedKeyRingTime)); Assert.Same(originalKeyRing, keyRingReturnedToBackgroundThread); mockCacheableKeyRingProvider.Verify(o => o.GetCacheableKeyRing(updatedKeyRingTime), Times.Once); }
internal CacheableKeyRing(CancellationToken expirationToken, DateTimeOffset expirationTime, IKeyRing keyRing) { _expirationToken = expirationToken; ExpirationTimeUtc = expirationTime.UtcDateTime; KeyRing = keyRing; }