public void TryGet_ChecksLocalCacheThenDistributedCache()
        {
            var localCache       = new MockLocalCache <int, int>();
            var distributedCache = new MockDistributedCache <int, int>();

            var twoTierCache = new TwoTierCache <int, int>(localCache, distributedCache, EqualityComparer <int> .Default);

            for (var i = 1; i <= 100; i++)
            {
                twoTierCache.TryGet(i).Result.Success.Should().BeFalse();
                localCache.TryGetExecutionCount.Should().Be(i);
                distributedCache.TryGetExecutionCount.Should().Be(i);
            }

            for (var i = 1; i <= 100; i++)
            {
                twoTierCache.Set(i, i, TimeSpan.FromSeconds(1)).AsTask().Wait();
                localCache.SetExecutionCount.Should().Be(i);
                distributedCache.SetExecutionCount.Should().Be(i);
            }

            for (var i = 1; i <= 100; i++)
            {
                var(success, value, _) = twoTierCache.TryGet(i).Result;
                success.Should().BeTrue();
                value.Should().Be(i);

                localCache.TryGetExecutionCount.Should().Be(100 + i);
                distributedCache.TryGetExecutionCount.Should().Be(100);
            }
        }
        public void WithTimeToLiveFactory_SetsTimeToLiveCorrectly()
        {
            Func <int, int> originalFunction = key => key;

            var cache = new MockLocalCache <int, int>();

            var cachedFunction = CachedFunctionFactory
                                 .ConfigureFor(originalFunction)
                                 .WithLocalCache(cache)
                                 .WithTimeToLiveFactory(key => TimeSpan.FromMilliseconds(key))
                                 .Build();

            foreach (var key in new[] { 250, 500, 1000 })
            {
                CheckTimeToLiveForKey(key);
            }

            void CheckTimeToLiveForKey(int key)
            {
                cachedFunction(key);

                Thread.Sleep(TimeSpan.FromMilliseconds(key / 2));

                cache.TryGet(key, out _).Should().BeTrue();

                Thread.Sleep(TimeSpan.FromMilliseconds(key));

                cache.TryGet(key, out _).Should().BeFalse();
            }
        }
        public void DisableCaching_WorksAsExpected(bool disableCaching)
        {
            Func <int, int> originalFunction = key => key;

            var cache = new MockLocalCache <int, int>();

            var cachedFunction = CachedFunctionFactory
                                 .ConfigureFor(originalFunction)
                                 .WithLocalCache(cache)
                                 .WithTimeToLive(TimeSpan.FromSeconds(1))
                                 .DisableCaching(disableCaching)
                                 .Build();

            cachedFunction(1);

            if (disableCaching)
            {
                cache.TryGetExecutionCount.Should().Be(0);
                cache.SetExecutionCount.Should().Be(0);
            }
            else
            {
                cache.TryGetExecutionCount.Should().Be(1);
                cache.SetExecutionCount.Should().Be(1);
            }
        }
        public void GetMany_SetsValueInLocalCacheIfFoundInDistributedCache()
        {
            var localCache       = new MockLocalCache <int, int>();
            var distributedCache = new MockDistributedCache <int, int>();

            var twoTierCache = new TwoTierCache <int, int>(localCache, distributedCache, EqualityComparer <int> .Default);

            distributedCache
            .SetMany(Enumerable.Range(0, 50).Select(i => new KeyValuePair <int, int>(i, i)).ToArray(), TimeSpan.FromSeconds(1))
            .Wait();

            distributedCache.SetManyExecutionCount.Should().Be(1);

            var values1 = twoTierCache.GetMany(Enumerable.Range(0, 100).ToArray()).Result;

            values1.Select(kv => kv.Key).Should().BeEquivalentTo(Enumerable.Range(0, 50));

            localCache.GetManyExecutionCount.Should().Be(1);
            localCache.SetExecutionCount.Should().Be(50);
            distributedCache.GetManyExecutionCount.Should().Be(1);

            var values2 = twoTierCache.GetMany(Enumerable.Range(0, 50).ToArray()).Result;

            values2.Select(kv => kv.Key).Should().BeEquivalentTo(Enumerable.Range(0, 50));

            localCache.GetManyExecutionCount.Should().Be(2);
            distributedCache.GetManyExecutionCount.Should().Be(1);
        }
        public void IfAnyMethodsNotConfigured_ThrowsException()
        {
            var originalImpl = new DummySingleKeyInterfaceImpl();

            var cache = new MockLocalCache <int, int>();

            Action action = () => CachedInterfaceFactory.For <IDummySingleKeyInterface>(originalImpl)
                            .Configure <int, int>(x => x.GetAsync, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache))
                            .Configure <int, int>(x => x.GetAsyncCanx, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache))
                            .Configure <int, int>(x => x.GetSync, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache))
                            .Build();

            action.Should().Throw <Exception>().Where(ex => ex.Message.Contains(nameof(IDummySingleKeyInterface.GetSyncCanx)));
        }
        public void OuterKeyAndInnerEnumerableKey_WorksForAllMethodTypes()
        {
            var originalImpl = new DummyOuterKeyAndInnerEnumerableKeyInterfaceImpl();

            var cache1 = new MockLocalCache <int, int, int>();
            var cache2 = new MockLocalCache <int, int, int>();
            var cache3 = new MockLocalCache <int, int, int>();
            var cache4 = new MockLocalCache <int, int, int>();
            var cache5 = new MockLocalCache <int, int, int>();
            var cache6 = new MockLocalCache <int, int, int>();

            var cachedInterface = CachedInterfaceFactory.For <IDummyOuterKeyAndInnerEnumerableKeyInterface>(originalImpl)
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetAsync, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache1))
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetAsyncCanx, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache2))
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetSync, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache3))
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetSyncCanx, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache4))
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetValueTask, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache5))
                                  .Configure <int, IEnumerable <int>, Dictionary <int, int> >(x => x.GetValueTaskCanx, c => c.WithEnumerableKeys <int, IEnumerable <int>, Dictionary <int, int>, int, int>().UseFirstParamAsOuterCacheKey().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache6))
                                  .Build();

            cachedInterface.GetAsync(0, new[] { 1 }).Result.Single().Should().Be(new KeyValuePair <int, int>(1, 1));
            cache1.GetMany(0, new[] { 1 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(1, 1));

            cachedInterface.GetAsyncCanx(0, new[] { 2 }, CancellationToken.None).Result.Single().Should().Be(new KeyValuePair <int, int>(2, 2));
            cache2.GetMany(0, new[] { 2 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(2, 2));

            cachedInterface.GetSync(0, new[] { 3 }).Single().Should().Be(new KeyValuePair <int, int>(3, 3));
            cache3.GetMany(0, new[] { 3 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(3, 3));

            cachedInterface.GetSyncCanx(0, new[] { 4 }, CancellationToken.None).Single().Should().Be(new KeyValuePair <int, int>(4, 4));
            cache4.GetMany(0, new[] { 4 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(4, 4));

            cachedInterface.GetValueTask(0, new[] { 5 }).Result.Single().Should().Be(new KeyValuePair <int, int>(5, 5));
            cache5.GetMany(0, new[] { 5 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(5, 5));

            cachedInterface.GetValueTaskCanx(0, new[] { 6 }, CancellationToken.None).Result.Single().Should().Be(new KeyValuePair <int, int>(6, 6));
            cache6.GetMany(0, new[] { 6 }).Should().BeEquivalentTo(new KeyValuePair <int, int>(6, 6));
        }
        public void EnumerableKey_WorksForAllMethodTypes()
        {
            var originalImpl = new DummyEnumerableKeyInterfaceImpl();

            var cache1 = new MockLocalCache <int, int>();
            var cache2 = new MockLocalCache <int, int>();
            var cache3 = new MockLocalCache <int, int>();
            var cache4 = new MockLocalCache <int, int>();
            var cache5 = new MockLocalCache <int, int>();
            var cache6 = new MockLocalCache <int, int>();

            var cachedInterface = CachedInterfaceFactory.For <IDummyEnumerableKeyInterface>(originalImpl)
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetAsync, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache1))
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetAsyncCanx, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache2))
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetSync, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache3))
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetSyncCanx, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache4))
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetValueTask, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache5))
                                  .Configure <IEnumerable <int>, Dictionary <int, int> >(x => x.GetValueTaskCanx, c => c.WithEnumerableKeys <IEnumerable <int>, Dictionary <int, int>, int, int>().WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache6))
                                  .Build();

            cachedInterface.GetAsync(new[] { 1 }).Result.Single().Should().Be(new KeyValuePair <int, int>(1, 1));
            cache1.TryGet(1, out _).Should().BeTrue();

            cachedInterface.GetAsyncCanx(new[] { 2 }, CancellationToken.None).Result.Single().Should().Be(new KeyValuePair <int, int>(2, 2));
            cache2.TryGet(2, out _).Should().BeTrue();

            cachedInterface.GetSync(new[] { 3 }).Single().Should().Be(new KeyValuePair <int, int>(3, 3));
            cache3.TryGet(3, out _).Should().BeTrue();

            cachedInterface.GetSyncCanx(new[] { 4 }, CancellationToken.None).Single().Should().Be(new KeyValuePair <int, int>(4, 4));
            cache4.TryGet(4, out _).Should().BeTrue();

            cachedInterface.GetValueTask(new[] { 5 }).Result.Single().Should().Be(new KeyValuePair <int, int>(5, 5));
            cache5.TryGet(5, out _).Should().BeTrue();

            cachedInterface.GetValueTaskCanx(new[] { 6 }, CancellationToken.None).Result.Single().Should().Be(new KeyValuePair <int, int>(6, 6));
            cache6.TryGet(6, out _).Should().BeTrue();
        }
        public void SingleKey_WorksForAllMethodTypes()
        {
            var originalImpl = new DummySingleKeyInterfaceImpl();

            var cache1 = new MockLocalCache <int, int>();
            var cache2 = new MockLocalCache <int, int>();
            var cache3 = new MockLocalCache <int, int>();
            var cache4 = new MockLocalCache <int, int>();
            var cache5 = new MockLocalCache <int, int>();
            var cache6 = new MockLocalCache <int, int>();

            var cachedInterface = CachedInterfaceFactory.For <IDummySingleKeyInterface>(originalImpl)
                                  .Configure <int, int>(x => x.GetAsync, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache1))
                                  .Configure <int, int>(x => x.GetAsyncCanx, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache2))
                                  .Configure <int, int>(x => x.GetSync, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache3))
                                  .Configure <int, int>(x => x.GetSyncCanx, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache4))
                                  .Configure <int, int>(x => x.GetValueTask, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache5))
                                  .Configure <int, int>(x => x.GetValueTaskCanx, c => c.WithTimeToLive(TimeSpan.FromSeconds(1)).WithLocalCache(cache6))
                                  .Build();

            cachedInterface.GetAsync(1).Result.Should().Be(1);
            cache1.TryGet(1, out _).Should().BeTrue();

            cachedInterface.GetAsyncCanx(2, CancellationToken.None).Result.Should().Be(2);
            cache2.TryGet(2, out _).Should().BeTrue();

            cachedInterface.GetSync(3).Should().Be(3);
            cache3.TryGet(3, out _).Should().BeTrue();

            cachedInterface.GetSyncCanx(4, CancellationToken.None).Should().Be(4);
            cache4.TryGet(4, out _).Should().BeTrue();

            cachedInterface.GetValueTask(5).Result.Should().Be(5);
            cache5.TryGet(5, out _).Should().BeTrue();

            cachedInterface.GetValueTaskCanx(6, CancellationToken.None).Result.Should().Be(6);
            cache6.TryGet(6, out _).Should().BeTrue();
        }
        public void TryGet_SetsValueInLocalCacheIfFoundInDistributedCache()
        {
            var localCache       = new MockLocalCache <int, int>();
            var distributedCache = new MockDistributedCache <int, int>();

            var twoTierCache = new TwoTierCache <int, int>(localCache, distributedCache, EqualityComparer <int> .Default);

            for (var i = 1; i <= 100; i++)
            {
                distributedCache.Set(i, i, TimeSpan.FromSeconds(100));
                distributedCache.SetExecutionCount.Should().Be(i);
            }

            for (var i = 1; i <= 100; i++)
            {
                var(success, value, _) = twoTierCache.TryGet(i).Result;
                success.Should().BeTrue();
                value.Should().Be(i);
                localCache.SetExecutionCount.Should().Be(i);
                localCache.TryGet(i, out var fromLocalCache).Should().BeTrue();
                fromLocalCache.Should().Be(i);
            }
        }
        public void WithTimeToLive_SetsTimeToLiveCorrectly(int timeToLiveMs)
        {
            Func <int, int> originalFunction = key => key;

            var cache = new MockLocalCache <int, int>();

            var cachedFunction = CachedFunctionFactory
                                 .ConfigureFor(originalFunction)
                                 .WithLocalCache(cache)
                                 .WithTimeToLive(TimeSpan.FromMilliseconds(timeToLiveMs))
                                 .Build();

            cachedFunction(1);

            Thread.Sleep(timeToLiveMs / 2);

            cachedFunction(1);
            cache.HitsCount.Should().Be(1);

            Thread.Sleep(timeToLiveMs);

            cachedFunction(1);
            cache.HitsCount.Should().Be(1);
        }
        public void GetMany_ChecksLocalCacheThenDistributedCache()
        {
            var localCache       = new MockLocalCache <int, int>();
            var distributedCache = new MockDistributedCache <int, int>();

            var twoTierCache = new TwoTierCache <int, int>(localCache, distributedCache, EqualityComparer <int> .Default);

            twoTierCache.GetMany(Enumerable.Range(0, 100).ToArray()).Result.Should().BeEmpty();
            localCache.GetManyExecutionCount.Should().Be(1);
            distributedCache.GetManyExecutionCount.Should().Be(1);

            twoTierCache.SetMany(Enumerable.Range(0, 50).Select(i => new KeyValuePair <int, int>(i, i)).ToArray(), TimeSpan.FromSeconds(1));
            localCache.SetManyExecutionCount.Should().Be(1);
            distributedCache.SetManyExecutionCount.Should().Be(1);

            var results1 = twoTierCache.GetMany(Enumerable.Range(0, 100).ToArray()).Result;

            results1.Select(kv => kv.Key).Should().BeEquivalentTo(Enumerable.Range(0, 50));
            foreach (var(key, value) in results1)
            {
                value.Should().Be(key);
            }
            localCache.GetManyExecutionCount.Should().Be(2);
            distributedCache.GetManyExecutionCount.Should().Be(2);

            var results2 = twoTierCache.GetMany(Enumerable.Range(0, 50).ToArray()).Result;

            results2.Select(kv => kv.Key).Should().BeEquivalentTo(Enumerable.Range(0, 50));
            foreach (var(key, value) in results2)
            {
                value.Should().Be(key);
            }

            localCache.GetManyExecutionCount.Should().Be(3);
            distributedCache.GetManyExecutionCount.Should().Be(2);
        }
        public void CacheStats_PopulatedCorrectly(bool localCacheEnabled, bool distributedCacheEnabled)
        {
            CacheGetStats lastCacheStats = default;

            Func <int, int> originalFunction = key => key;

            var config = CachedFunctionFactory.ConfigureFor(originalFunction)
                         .WithTimeToLive(TimeSpan.FromMinutes(1))
                         .OnResult(r => lastCacheStats = r.CacheStats)
                         .DontGetFromCacheWhen(k => k == 1)
                         .DontGetFromLocalCacheWhen(k => k == 2)
                         .DontGetFromDistributedCacheWhen(k => k == 3);

            if (localCacheEnabled)
            {
                var localCache = new MockLocalCache <int, int>();
                localCache.Set(4, 4, TimeSpan.FromMinutes(1));

                config.WithLocalCache(localCache);
            }

            if (distributedCacheEnabled)
            {
                var distributedCache = new MockDistributedCache <int, int>();
                distributedCache.Set(4, 4, TimeSpan.FromMinutes(1));
                distributedCache.Set(5, 5, TimeSpan.FromMinutes(1));

                config.WithDistributedCache(distributedCache);
            }

            var cachedFunction = config.Build();

            var cacheEnabled = localCacheEnabled || distributedCacheEnabled;

            for (var i = 1; i <= 5; i++)
            {
                cachedFunction(i).Should().Be(i);

                VerifyCacheStats(i);
            }

            void VerifyCacheStats(int key)
            {
                var localCacheKeyRequested = localCacheEnabled && key != 1 && key != 2;
                var localCacheHit          = localCacheEnabled && key == 4;

                var distributedCacheKeyRequested = distributedCacheEnabled && !localCacheHit && key != 1 && key != 3;
                var distributedCacheHit          = distributedCacheKeyRequested && (key == 4 || key == 5);

                var cacheKeyRequested = localCacheKeyRequested || distributedCacheKeyRequested;
                var cacheHit          = localCacheHit || distributedCacheHit;

                lastCacheStats.CacheEnabled.Should().Be(cacheEnabled);
                lastCacheStats.CacheKeyRequested.Should().Be(cacheKeyRequested);
                lastCacheStats.CacheSkipped.Should().Be(cacheEnabled && !cacheKeyRequested);
                lastCacheStats.CacheHit.Should().Be(cacheHit);
                lastCacheStats.CacheMiss.Should().Be(cacheKeyRequested && !cacheHit);

                lastCacheStats.LocalCacheEnabled.Should().Be(localCacheEnabled);
                lastCacheStats.LocalCacheKeyRequested.Should().Be(localCacheKeyRequested);
                lastCacheStats.LocalCacheSkipped.Should().Be(localCacheEnabled && !localCacheKeyRequested);
                lastCacheStats.LocalCacheHit.Should().Be(localCacheHit);
                lastCacheStats.LocalCacheMiss.Should().Be(localCacheKeyRequested && !localCacheHit);

                lastCacheStats.DistributedCacheEnabled.Should().Be(distributedCacheEnabled);
                lastCacheStats.DistributedCacheKeyRequested.Should().Be(distributedCacheKeyRequested);
                lastCacheStats.DistributedCacheSkipped.Should().Be(distributedCacheEnabled && !localCacheHit && !distributedCacheKeyRequested);
                lastCacheStats.DistributedCacheHit.Should().Be(distributedCacheHit);
                lastCacheStats.DistributedCacheMiss.Should().Be(distributedCacheKeyRequested && !distributedCacheHit);
            }
        }
        public void OnResult_EventsAreTriggeredAsExpected()
        {
            var cache = new MockLocalCache <string, int>();
            SuccessfulRequestEvent <int, string, int> lastSuccess = default;
            ExceptionEvent <int, string> lastException            = default;

            var first            = true;
            var exceptionMessage = Guid.NewGuid().ToString();

            Func <int, CancellationToken, int> originalFunction = (p, cancellationToken) =>
            {
                ThrowIfFirst();
                return(-p);
            };

            var cachedFunction = CachedFunctionFactory
                                 .ConfigureFor(originalFunction)
                                 .WithCacheKey(x => x.ToString())
                                 .WithLocalCache(cache)
                                 .WithTimeToLive(TimeSpan.FromSeconds(1))
                                 .OnResult(r => lastSuccess = r, ex => lastException = ex)
                                 .Build();

            Func <int> func = () => cachedFunction(1, CancellationToken.None);

            func.Should().Throw <Exception>();
            CheckException();
            func().Should().Be(-1);
            CheckSuccess(false);
            func().Should().Be(-1);
            CheckSuccess(true);

            void ThrowIfFirst()
            {
                if (!first)
                {
                    return;
                }

                first = false;
                throw new Exception(exceptionMessage);
            }

            void CheckSuccess(bool wasCached)
            {
                var now = DateTime.UtcNow;

                lastSuccess.Parameters.Should().Be(1);
                lastSuccess.Key.Should().Be("1");
                lastSuccess.Value.Should().Be(-1);
                lastSuccess.Start.Should().BeWithin(TimeSpan.FromMilliseconds(100)).Before(now);
                lastSuccess.Duration.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
                lastSuccess.CacheStats.CacheHit.Should().Be(wasCached);
            }

            void CheckException()
            {
                var now = DateTime.UtcNow;

                lastException.Parameters.Should().Be(1);
                lastException.Key.Should().Be("1");
                lastException.Start.Should().BeWithin(TimeSpan.FromMilliseconds(100)).Before(now);
                lastException.Duration.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
                lastException.Exception.Message.Should().Be(exceptionMessage);
            }
        }
        public void DontGetFromOrStoreInLocalCacheWhen_ForSingleTierCache_WorksAsExpected(
            bool flag1, bool flag2, bool flag3, bool flag4)
        {
            Func <int, int> originalFunction = key => key;

            var cache = new MockLocalCache <int, int>();

            var config = CachedFunctionFactory
                         .ConfigureFor(originalFunction)
                         .WithLocalCache(cache)
                         .WithTimeToLive(TimeSpan.FromSeconds(1));

            if (flag1)
            {
                config.DontGetFromLocalCacheWhen(key => key % 2 == 0);
            }

            if (flag2)
            {
                config.DontStoreInLocalCacheWhen((key, _) => key % 3 == 0);
            }

            if (flag3)
            {
                config.DontGetFromLocalCacheWhen(key => key % 5 == 0);
            }

            if (flag4)
            {
                config.DontStoreInLocalCacheWhen((key, _) => key % 7 == 0);
            }

            var cachedFunction = config.Build();

            var previousTryGetExecutionCount = 0;
            var previousSetExecutionCount    = 0;

            for (var i = 0; i < 100; i++)
            {
                cachedFunction(i);

                var currentTryGetExecutionCount = cache.TryGetExecutionCount;
                var currentSetExecutionCount    = cache.SetExecutionCount;

                var skipGet = flag1 && i % 2 == 0 || flag3 && i % 5 == 0;
                var skipSet = flag2 && i % 3 == 0 || flag4 && i % 7 == 0;

                if (skipGet)
                {
                    currentTryGetExecutionCount.Should().Be(previousTryGetExecutionCount);
                }
                else
                {
                    currentTryGetExecutionCount.Should().Be(previousTryGetExecutionCount + 1);
                }

                if (skipSet)
                {
                    currentSetExecutionCount.Should().Be(previousSetExecutionCount);
                }
                else
                {
                    currentSetExecutionCount.Should().Be(previousSetExecutionCount + 1);
                }

                previousTryGetExecutionCount = currentTryGetExecutionCount;
                previousSetExecutionCount    = currentSetExecutionCount;
            }
        }
        public void DontGetFromOrStoreInLocalCacheWhen_DontGetFromOrStoreInDistributedCacheWhen_WorksAsExpected(
            bool flag1, bool flag2, bool flag3, bool flag4, bool flag5, bool flag6, bool flag7, bool flag8)
        {
            Func <int, int> originalFunction = key => key;

            var localCache       = new MockLocalCache <int, int>();
            var distributedCache = new MockDistributedCache <int, int>();

            var config = CachedFunctionFactory
                         .ConfigureFor(originalFunction)
                         .WithLocalCache(localCache)
                         .WithDistributedCache(distributedCache)
                         .WithTimeToLive(TimeSpan.FromSeconds(1));

            if (flag1)
            {
                config.DontGetFromLocalCacheWhen(key => key % 2 == 0);
            }

            if (flag2)
            {
                config.DontStoreInLocalCacheWhen((key, _) => key % 3 == 0);
            }

            if (flag3)
            {
                config.DontGetFromLocalCacheWhen(key => key % 5 == 0);
            }

            if (flag4)
            {
                config.DontStoreInLocalCacheWhen((key, _) => key % 7 == 0);
            }

            if (flag5)
            {
                config.DontGetFromDistributedCacheWhen(key => key % 11 == 0);
            }

            if (flag6)
            {
                config.DontStoreInDistributedCacheWhen((key, _) => key % 13 == 0);
            }

            if (flag7)
            {
                config.DontGetFromDistributedCacheWhen(key => key % 17 == 0);
            }

            if (flag8)
            {
                config.DontStoreInDistributedCacheWhen((key, _) => key % 19 == 0);
            }

            var cachedFunction = config.Build();

            var previousLocalTryGetExecutionCount       = 0;
            var previousLocalSetExecutionCount          = 0;
            var previousDistributedTryGetExecutionCount = 0;
            var previousDistributedSetExecutionCount    = 0;

            for (var i = 0; i < 100; i++)
            {
                cachedFunction(i);

                var currentLocalTryGetExecutionCount       = localCache.TryGetExecutionCount;
                var currentLocalSetExecutionCount          = localCache.SetExecutionCount;
                var currentDistributedTryGetExecutionCount = distributedCache.TryGetExecutionCount;
                var currentDistributedSetExecutionCount    = distributedCache.SetExecutionCount;

                var skipLocalGet       = flag1 && i % 2 == 0 || flag3 && i % 5 == 0;
                var skipLocalSet       = flag2 && i % 3 == 0 || flag4 && i % 7 == 0;
                var skipDistributedGet = flag5 && i % 11 == 0 || flag7 && i % 17 == 0;
                var skipDistributedSet = flag6 && i % 13 == 0 || flag8 && i % 19 == 0;

                if (skipLocalGet)
                {
                    currentLocalTryGetExecutionCount.Should().Be(previousLocalTryGetExecutionCount);
                }
                else
                {
                    currentLocalTryGetExecutionCount.Should().Be(previousLocalTryGetExecutionCount + 1);
                }

                if (skipLocalSet)
                {
                    currentLocalSetExecutionCount.Should().Be(previousLocalSetExecutionCount);
                }
                else
                {
                    currentLocalSetExecutionCount.Should().Be(previousLocalSetExecutionCount + 1);
                }

                if (skipDistributedGet)
                {
                    currentDistributedTryGetExecutionCount.Should().Be(previousDistributedTryGetExecutionCount);
                }
                else
                {
                    currentDistributedTryGetExecutionCount.Should().Be(previousDistributedTryGetExecutionCount + 1);
                }

                if (skipDistributedSet)
                {
                    currentDistributedSetExecutionCount.Should().Be(previousDistributedSetExecutionCount);
                }
                else
                {
                    currentDistributedSetExecutionCount.Should().Be(previousDistributedSetExecutionCount + 1);
                }

                previousLocalTryGetExecutionCount       = currentLocalTryGetExecutionCount;
                previousLocalSetExecutionCount          = currentLocalSetExecutionCount;
                previousDistributedTryGetExecutionCount = currentDistributedTryGetExecutionCount;
                previousDistributedSetExecutionCount    = currentDistributedSetExecutionCount;
            }
        }
Example #16
0
        public async Task With1Param_WorksAsExpected(string functionType, bool hasCancellationToken)
        {
            var cache = new MockLocalCache <string, int>();

            switch (functionType)
            {
            case "async" when hasCancellationToken:
            {
                Func <int, CancellationToken, Task <int> > originalFunction = (p, cancellationToken) => Task.FromResult(p);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, CancellationToken.None).ConfigureAwait(false)).Should().Be(1);
                break;
            }

            case "async":
            {
                Func <int, Task <int> > originalFunction = Task.FromResult;

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1).ConfigureAwait(false)).Should().Be(1);
                break;
            }

            case "sync" when hasCancellationToken:
            {
                Func <int, CancellationToken, int> originalFunction = (p, cancellationToken) => p;

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, CancellationToken.None).Should().Be(1);
                break;
            }

            case "sync":
            {
                Func <int, int> originalFunction = p => p;

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1).Should().Be(1);
                break;
            }

            case "valuetask" when hasCancellationToken:
            {
                Func <int, CancellationToken, ValueTask <int> > originalFunction = (p, cancellationToken) => new ValueTask <int>(p);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, CancellationToken.None).ConfigureAwait(false)).Should().Be(1);
                break;
            }

            case "valuetask":
            {
                Func <int, ValueTask <int> > originalFunction = p => new ValueTask <int>(p);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey(p => p.ToString())
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1).ConfigureAwait(false)).Should().Be(1);
                break;
            }
            }

            cache.TryGet("1", out var value).Should().BeTrue();
            value.Should().Be(1);
        }
Example #17
0
        public async Task With8Params_WorksAsExpected(string functionType, bool hasCancellationToken)
        {
            var cache = new MockLocalCache <int, int>();

            switch (functionType)
            {
            case "async" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, int, CancellationToken, Task <int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => Task.FromResult(p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, 6, 7, 8, CancellationToken.None).ConfigureAwait(false)).Should().Be(36);
                break;
            }

            case "async":
            {
                Func <int, int, int, int, int, int, int, int, Task <int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => Task.FromResult(p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, 6, 7, 8).ConfigureAwait(false)).Should().Be(36);
                break;
            }

            case "sync" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, int, CancellationToken, int> originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, 8, CancellationToken.None).Should().Be(36);
                break;
            }

            case "sync":
            {
                Func <int, int, int, int, int, int, int, int, int> originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8;

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, 8).Should().Be(36);
                break;
            }

            case "valuetask" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, int, CancellationToken, ValueTask <int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => new ValueTask <int>(p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, 8, CancellationToken.None).Result.Should().Be(36);
                break;
            }

            case "valuetask":
            {
                Func <int, int, int, int, int, int, int, int, ValueTask <int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => new ValueTask <int>(p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithCacheKey((p1, p2, p3, p4, p5, p6, p7, p8) => p1)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, 8).Result.Should().Be(36);
                break;
            }
            }

            cache.TryGet(1, out var value).Should().BeTrue();
            value.Should().Be(36);
        }
Example #18
0
        public async Task With8Params_WorksAsExpected(string functionType, bool hasCancellationToken)
        {
            var cache = new MockLocalCache <int, int, int>();

            var input          = Enumerable.Range(1, 10).ToArray();
            var expectedOutput = input.ToDictionary(x => x);

            switch (functionType)
            {
            case "async" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, CancellationToken, Task <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => Task.FromResult(p8.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, 6, 7, input, CancellationToken.None).ConfigureAwait(false)).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "async":
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, Task <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => Task.FromResult(p8.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, 6, 7, input).ConfigureAwait(false)).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "sync" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, CancellationToken, Dictionary <int, int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => p8.ToDictionary(x => x);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, input, CancellationToken.None).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "sync":
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int> > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => p8.ToDictionary(x => x);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, input).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "valuetask" when hasCancellationToken:
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, CancellationToken, ValueTask <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8, cancellationToken) => new ValueTask <Dictionary <int, int> >(p8.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, input, CancellationToken.None).Result.Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "valuetask":
            {
                Func <int, int, int, int, int, int, int, IEnumerable <int>, ValueTask <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, p7, p8) => new ValueTask <Dictionary <int, int> >(p8.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithOuterCacheKey((p1, p2, p3, p4, p5, p6, p7) => p1 + p2 + p3 + p4 + p5 + p6 + p7)
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, 6, 7, input).Result.Should().BeEquivalentTo(expectedOutput);
                break;
            }
            }

            cache.GetMany(28, input).Should().BeEquivalentTo(expectedOutput);
        }
Example #19
0
        public void SetMany_EventsAreTriggeredSuccessfully(bool flag1, bool flag2)
        {
            var config = new LocalCacheEventsWrapperConfig <int, int, int>();

            var successfulResults = new List <(int, IReadOnlyCollection <KeyValuePair <int, int> >, TimeSpan, TimeSpan)>();
            var failedResults     = new List <(int, IReadOnlyCollection <KeyValuePair <int, int> >, TimeSpan, TimeSpan, Exception)>();

            if (flag1)
            {
                config.OnSetManyCompletedSuccessfully = (outerKey, values, timeToLive, duration) =>
                {
                    successfulResults.Add((outerKey, values, timeToLive, duration));
                };
            }

            if (flag2)
            {
                config.OnSetManyException = (outerKey, values, timeToLive, duration, exception) =>
                {
                    failedResults.Add((outerKey, values, timeToLive, duration, exception));
                    return(values.Any(kv => kv.Key == 6));
                };
            }

            var innerCache = new MockLocalCache <int, int, int>();
            var cache      = new LocalCacheEventsWrapper <int, int, int>(config, innerCache);

            var values = new[] { new KeyValuePair <int, int>(2, 3), new KeyValuePair <int, int>(4, 5) };

            cache.SetMany(1, values, TimeSpan.FromSeconds(1));
            if (flag1)
            {
                successfulResults.Should().ContainSingle();
                successfulResults.Last().Item1.Should().Be(1);
                successfulResults.Last().Item2.Should().BeEquivalentTo(values);
                successfulResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                successfulResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                successfulResults.Should().BeEmpty();
            }

            innerCache.ThrowExceptionOnNextAction();
            Action action = () => cache.SetMany(1, values, TimeSpan.FromSeconds(1));

            action.Should().Throw <Exception>();
            if (flag2)
            {
                failedResults.Should().ContainSingle();
                failedResults.Last().Item1.Should().Be(1);
                failedResults.Last().Item2.Should().BeEquivalentTo(values);
                failedResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                failedResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                failedResults.Should().BeEmpty();
            }

            values[1] = new KeyValuePair <int, int>(6, 7);

            innerCache.ThrowExceptionOnNextAction();
            action = () => cache.SetMany(1, values, TimeSpan.FromSeconds(1));
            if (flag2)
            {
                action();
                failedResults.Should().HaveCount(2);
                failedResults.Last().Item1.Should().Be(1);
                failedResults.Last().Item2.Should().BeEquivalentTo(values);
                failedResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                failedResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                action.Should().Throw <Exception>();
                failedResults.Should().BeEmpty();
            }
        }
Example #20
0
        public void TryRemove_EventsAreTriggeredSuccessfully(bool flag1, bool flag2)
        {
            var config = new LocalCacheEventsWrapperConfig <int, int, int>();

            var successfulResults = new List <(int, int, bool, int, TimeSpan)>();
            var failedResults     = new List <(int, int, TimeSpan, Exception)>();

            if (flag1)
            {
                config.OnTryRemoveCompletedSuccessfully = (outerKey, innerKey, found, value, duration) =>
                {
                    successfulResults.Add((outerKey, innerKey, found, value, duration));
                };
            }

            if (flag2)
            {
                config.OnTryRemoveException = (outerKey, innerKey, duration, exception) =>
                {
                    failedResults.Add((outerKey, innerKey, duration, exception));
                    return(innerKey == 5);
                };
            }

            var innerCache = new MockLocalCache <int, int, int>();
            var cache      = new LocalCacheEventsWrapper <int, int, int>(config, innerCache);

            cache.TryRemove(1, 2, out _).Should().BeFalse();
            if (flag1)
            {
                successfulResults.Should().ContainSingle();
                successfulResults.Last().Item1.Should().Be(1);
                successfulResults.Last().Item2.Should().Be(2);
                successfulResults.Last().Item3.Should().BeFalse();
                successfulResults.Last().Item4.Should().Be(0);
                successfulResults.Last().Item5.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                successfulResults.Should().BeEmpty();
            }

            cache.SetMany(1, new[] { new KeyValuePair <int, int>(3, 4) }, TimeSpan.FromSeconds(1));
            cache.TryRemove(1, 3, out _).Should().BeTrue();
            if (flag1)
            {
                successfulResults.Should().HaveCount(2);
                successfulResults.Last().Item1.Should().Be(1);
                successfulResults.Last().Item2.Should().Be(3);
                successfulResults.Last().Item3.Should().BeTrue();
                successfulResults.Last().Item4.Should().Be(4);
                successfulResults.Last().Item5.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                successfulResults.Should().BeEmpty();
            }

            innerCache.ThrowExceptionOnNextAction();
            Action action = () => cache.TryRemove(1, 5, out _);

            if (flag2)
            {
                action();
                failedResults.Should().ContainSingle();
                failedResults.Last().Item1.Should().Be(1);
                failedResults.Last().Item2.Should().Be(5);
                failedResults.Last().Item3.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                action.Should().Throw <Exception>();
                failedResults.Should().BeEmpty();
            }
        }
Example #21
0
        public void GetMany_EventsAreTriggeredSuccessfully(bool flag1, bool flag2)
        {
            var config = new LocalCacheEventsWrapperConfig <int, int>();

            var successfulResults = new List <(IReadOnlyCollection <int>, IReadOnlyCollection <KeyValuePair <int, int> >, TimeSpan)>();
            var failedResults     = new List <(IReadOnlyCollection <int>, TimeSpan, Exception)>();

            if (flag1)
            {
                config.OnGetManyCompletedSuccessfully = (keys, values, duration) =>
                {
                    successfulResults.Add((keys, values, duration));
                };
            }

            if (flag2)
            {
                config.OnGetManyException = (keys, duration, exception) =>
                {
                    failedResults.Add((keys, duration, exception));
                    return(keys.Contains(3));
                };
            }

            var innerCache = new MockLocalCache <int, int>();
            var cache      = new LocalCacheEventsWrapper <int, int>(config, innerCache);

            var keys = new[] { 1, 2 };

            cache.Set(1, 2, TimeSpan.FromSeconds(1));
            cache.GetMany(keys);
            if (flag1)
            {
                successfulResults.Should().ContainSingle();
                successfulResults.Last().Item1.Should().BeEquivalentTo(keys);
                successfulResults.Last().Item2.ToArray().Should().BeEquivalentTo(new KeyValuePair <int, int>(1, 2));
                successfulResults.Last().Item3.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                successfulResults.Should().BeEmpty();
            }

            innerCache.ThrowExceptionOnNextAction();
            Action action = () => cache.GetMany(keys);

            action.Should().Throw <Exception>();
            if (flag2)
            {
                failedResults.Should().ContainSingle();
                failedResults.Last().Item1.Should().BeEquivalentTo(keys);
                failedResults.Last().Item2.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                failedResults.Should().BeEmpty();
            }

            keys[1] = 3;

            innerCache.ThrowExceptionOnNextAction();
            action = () => cache.GetMany(keys);
            if (flag2)
            {
                action();
                failedResults.Should().HaveCount(2);
                failedResults.Last().Item1.Should().BeEquivalentTo(keys);
                failedResults.Last().Item2.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                action.Should().Throw <Exception>();
                failedResults.Should().BeEmpty();
            }
        }
Example #22
0
        public void Set_EventsAreTriggeredSuccessfully(bool flag1, bool flag2)
        {
            var config = new LocalCacheEventsWrapperConfig <int, int>();

            var successfulResults = new List <(int, int, TimeSpan, TimeSpan)>();
            var failedResults     = new List <(int, int, TimeSpan, TimeSpan, Exception)>();

            if (flag1)
            {
                config.OnSetCompletedSuccessfully = (key, value, timeToLive, duration) =>
                {
                    successfulResults.Add((key, value, timeToLive, duration));
                };
            }

            if (flag2)
            {
                config.OnSetException = (key, value, timeToLive, duration, exception) =>
                {
                    failedResults.Add((key, value, timeToLive, duration, exception));
                    return(key == 5);
                };
            }

            var innerCache = new MockLocalCache <int, int>();
            var cache      = new LocalCacheEventsWrapper <int, int>(config, innerCache);

            cache.Set(1, 2, TimeSpan.FromSeconds(1));
            if (flag1)
            {
                successfulResults.Should().ContainSingle();
                successfulResults.Last().Item1.Should().Be(1);
                successfulResults.Last().Item2.Should().Be(2);
                successfulResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                successfulResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                successfulResults.Should().BeEmpty();
            }

            innerCache.ThrowExceptionOnNextAction();
            Action action = () => cache.Set(3, 4, TimeSpan.FromSeconds(1));

            action.Should().Throw <Exception>();
            if (flag2)
            {
                failedResults.Should().ContainSingle();
                failedResults.Last().Item1.Should().Be(3);
                failedResults.Last().Item2.Should().Be(4);
                failedResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                failedResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                failedResults.Should().BeEmpty();
            }

            innerCache.ThrowExceptionOnNextAction();
            action = () => cache.Set(5, 6, TimeSpan.FromSeconds(1));
            if (flag2)
            {
                action();
                failedResults.Should().HaveCount(2);
                failedResults.Last().Item1.Should().Be(5);
                failedResults.Last().Item2.Should().Be(6);
                failedResults.Last().Item3.Should().Be(TimeSpan.FromSeconds(1));
                failedResults.Last().Item4.Should().BePositive().And.BeCloseTo(TimeSpan.Zero, TimeSpan.FromMilliseconds(100));
            }
            else
            {
                action.Should().Throw <Exception>();
                failedResults.Should().BeEmpty();
            }
        }
Example #23
0
        public async Task With6Params_WorksAsExpected(string functionType, bool hasCancellationToken)
        {
            var cache = new MockLocalCache <int, int>();

            var input          = Enumerable.Range(1, 10).ToArray();
            var expectedOutput = input.ToDictionary(x => x);

            switch (functionType)
            {
            case "async" when hasCancellationToken:
            {
                Func <int, int, int, int, int, IEnumerable <int>, CancellationToken, Task <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, cancellationToken) => Task.FromResult(p6.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, input, CancellationToken.None).ConfigureAwait(false)).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "async":
            {
                Func <int, int, int, int, int, IEnumerable <int>, Task <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6) => Task.FromResult(p6.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                (await cachedFunction(1, 2, 3, 4, 5, input).ConfigureAwait(false)).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "sync" when hasCancellationToken:
            {
                Func <int, int, int, int, int, IEnumerable <int>, CancellationToken, Dictionary <int, int> > originalFunction = (p1, p2, p3, p4, p5, p6, cancellationToken) => p6.ToDictionary(x => x);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, input, CancellationToken.None).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "sync":
            {
                Func <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int> > originalFunction = (p1, p2, p3, p4, p5, p6) => p6.ToDictionary(x => x);

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, input).Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "valuetask" when hasCancellationToken:
            {
                Func <int, int, int, int, int, IEnumerable <int>, CancellationToken, ValueTask <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6, cancellationToken) => new ValueTask <Dictionary <int, int> >(p6.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, input, CancellationToken.None).Result.Should().BeEquivalentTo(expectedOutput);
                break;
            }

            case "valuetask":
            {
                Func <int, int, int, int, int, IEnumerable <int>, ValueTask <Dictionary <int, int> > > originalFunction = (p1, p2, p3, p4, p5, p6) => new ValueTask <Dictionary <int, int> >(p6.ToDictionary(x => x));

                var cachedFunction = CachedFunctionFactory
                                     .ConfigureFor(originalFunction)
                                     .WithEnumerableKeys <int, int, int, int, int, IEnumerable <int>, Dictionary <int, int>, int, int>()
                                     .WithLocalCache(cache)
                                     .WithTimeToLive(TimeSpan.FromSeconds(1))
                                     .Build();

                cachedFunction(1, 2, 3, 4, 5, input).Result.Should().BeEquivalentTo(expectedOutput);
                break;
            }
            }

            for (var i = 1; i <= 10; i++)
            {
                cache.TryGet(i, out var value).Should().BeTrue();
                value.Should().Be(i);
            }
        }