public async Task MemoizeAsync_NotCachedCallWithoutRevokeShouldCacheDataSourceValue()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1
            });

            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns((DateTime?)null);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //get cached value
        }
        public async Task MemoizeAsync_AfterRefreshRevokeKeysArePreservedInReverseIndexCorrectly()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSource = Substitute.For <IThingFrobber>();
            var result1    = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x", "y"
                }
            };
            var result2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x", "y"
                }
            };

            dataSource.ThingifyTaskRevokable("someString").Returns(result1, result2);

            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0));//refresh in next call
            cache.RevokeKeysCount.ShouldBe(2);
            cache.CacheKeyCount.ShouldBe(2);

            await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            cache.CacheKeyCount.ShouldBe(2);
            cache.RevokeKeysCount.ShouldBe(2);
        }
        public async Task MemoizeAsync_RefreshWithIntefiringRevokeShouldMarkCacheValueAsStaleAndTriggerACallToDataSource()
        {
            var cache    = CreateCache(new OneTimeSynchronousSourceBlock <string>());
            var memoizer = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult3 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result3"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1, dataSourceResult2, dataSourceResult3
            });

            //second call to data source will receive a revoke!!!
            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns((DateTime?)null, TimeFake.UtcNow, (DateTime?)null);

            var refreshBehavior = RefreshBehavior.TryFetchNewValueOrUseOld;
            //refresh in next call
            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0, refreshBehavior: refreshBehavior));

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //refresh triggered, get second value

            //previous refresh received revoke while in call to remote service and marked item as stale

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult3.Value.Id); //because cached value is stale, another call to data source is triggered and its value is returned

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult3.Value.Id); //latest refresh value was cached and returned
        }
        public async Task MemoizeAsync_NotCachedCallWithIntefiringRevokeShouldMarkCacheValueAsStaleAndTriggerACallToDataSource()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1, dataSourceResult2
            });

            //first call to data source will receive a revoke!!!
            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns(TimeFake.UtcNow, (DateTime?)null);

            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            //revoke received while in first call to data source

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //call trigger a call to data source because value is stale and a new data source value is returned

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings());
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //cached value is returned
        }
        public async Task MemoizeAsync_RefreshWithoutRevokeShouldCacheNewValue()
        {
            var revokesSource = new OneTimeSynchronousSourceBlock <string>();
            var cache         = CreateCache(revokesSource);
            var memoizer      = CreateMemoizer(cache);

            var dataSourceDelay   = 1;
            var dataSourceResult1 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result1"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSourceResult2 = new Revocable <Thing> {
                Value = new Thing {
                    Id = "result2"
                }, RevokeKeys = new List <string> {
                    "x"
                }
            };
            var dataSource = new ThingFrobber(dataSourceDelay, new List <Revocable <Thing> > {
                dataSourceResult1, dataSourceResult2
            });

            RecentlyRevokesCache.TryGetRecentlyRevokedTime(Arg.Any <string>(), Arg.Any <DateTime>()).Returns((DateTime?)null);

            var refreshBehavior = RefreshBehavior.TryFetchNewValueOrUseOld;
            //refresh in next call
            var result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshTimeSeconds: 0, refreshBehavior: refreshBehavior));

            result.Value.Id.ShouldBe(dataSourceResult1.Value.Id); //call data source and get first value

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //refresh triggered, get second value from data source and cache

            result = await(Task <Revocable <Thing> >) memoizer.Memoize(dataSource, ThingifyTaskRevokabkle, new object[] { "someString" }, GetCachingSettings(refreshBehavior: refreshBehavior));
            result.Value.Id.ShouldBe(dataSourceResult2.Value.Id); //return refreshed cached value
        }