public async Task Null_key_from_identifier_does_not_throw_and_value_does_not_get_cached_async() { var cache = new MKCache <Item>( x => x.Name /* The Name is the key */); var item = new Item(name: null !); var factoryMock = new Mock <Func <Task <Item> > >(); factoryMock.Setup(factory => factory()).Returns(Task.FromResult(item)); var foundItem = await cache.GetOrCreateAsync(item.Id, factoryMock.Object, Expiration); factoryMock.Verify(factory => factory(), Times.Once); Assert.Same(item, foundItem); foundItem = await cache.GetOrCreateAsync("any", factoryMock.Object, Expiration); factoryMock.Verify(factory => factory(), Times.Exactly(2)); Assert.Same(item, foundItem); foundItem = await cache.GetOrCreateAsync("any", factoryMock.Object, Expiration); factoryMock.Verify(factory => factory(), Times.Exactly(3)); Assert.Same(item, foundItem); }
public async Task Cache_item_expires_async() { var cache = new MKCache <Item>(); var item = new Item(); var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(Task.FromResult(item)); var expirySpan = TimeSpan.FromSeconds(1); var foundItem = await cache.GetOrCreateAsync(item.Id, asyncFactoryMock.Object, expirySpan); asyncFactoryMock.Verify(factory => factory(), Times.Once); Assert.Same(item, foundItem); var cachedItem = await cache.GetOrCreateAsync(item.Id, () => Task.FromResult(new Item()), expirySpan); Assert.Same(item, cachedItem); await Task.Delay(expirySpan * 3); var newItem = await cache.GetOrCreateAsync(item.Id, asyncFactoryMock.Object, expirySpan); asyncFactoryMock.Verify(factory => factory(), Times.Exactly(2)); }
public async Task Default_key_cache_can_be_cleared() { var item = new Item(); var key = item.Id; var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(Task.FromResult(item)); var cache = new MKCache <Item>(); var foundItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, foundItem); var cachedItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, cachedItem); cache.Clear(); var _ = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Exactly(2)); }
public async Task An_item_is_cached_with_the_default_key_async() { var item = new Item(); var key = item.Id; var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(Task.FromResult(item)); var cache = new MKCache <Item>(); var foundItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, foundItem); var cachedItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, cachedItem); var _ = await cache.GetOrCreateAsync(item.Name, asyncFactoryMock.Object, Expiration); // Delegate invoked a second time, because the item was not found asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Exactly(2)); }
public async Task An_item_is_cached_with_a_single_key_async() { var item = new Item(); var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(Task.FromResult(item)); var cache = new MKCache <Item>( x => x.Name /* Name property is the key */); _ = await cache.GetOrCreateAsync(item.Id, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); var cachedItem = await cache.GetOrCreateAsync(item.Name, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, cachedItem); _ = await cache.GetOrCreateAsync(item.Id, asyncFactoryMock.Object, Expiration); // Delegate invoked a second time, because the item was not found using its Id asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Exactly(2)); }
public async Task An_item_is_cached_with_multiple_keys_async() { var item = new Item(); var key = item.Id; var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(Task.FromResult(item)); var cache = new MKCache <Item>( x => x.Id, x => x.Name); var foundItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, foundItem); var cachedItem = await cache.GetOrCreateAsync(key, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, cachedItem); var cachedItemWithDifferentKey = await cache.GetOrCreateAsync(item.Name, asyncFactoryMock.Object, Expiration); asyncFactoryMock.Verify(asyncFactory => asyncFactory(), Times.Once); Assert.Same(item, cachedItemWithDifferentKey); }
public async Task Running_async_fetchers_are_reused() { var item = new Item(); var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(() => Task.Run(async() => { // A time long enough for the test to request n concurrent async factories invocations await Task.Delay(TimeSpan.FromSeconds(2)); return(item); })); var cache = new MKCache <Item> { ReuseRunningAsyncFetchers = true }; var foundItemTask = cache.GetOrCreateAsync("any", asyncFactoryMock.Object, Expiration); // Simulate concurrent request for the same object (same key), while the item is not yet been cached. var concurrentFinders = new List <Task <Item> >(); var n = 100; for (int i = 0; i < n; i++) { concurrentFinders.Add(cache.GetOrCreateAsync("any", asyncFactoryMock.Object, Expiration)); } var foundItem = await foundItemTask; // The delegate is invoked only once asyncFactoryMock.Verify(asyncFinder => asyncFinder(), Times.Once); // The reused and completed task gets mapped into another task for each call (by the cache awaiting it), // therefore we might still be needing to await them to complete. await Task.WhenAll(concurrentFinders); // Even if the Id gets created new each time, the same result has been returned. concurrentFinders.ForEach(t => Assert.Equal(t.Result.Id, foundItem.Id)); // After the async fetcher has completed, the item is stored in cache // so the delegate still won't be invoked (but for a different reason). var cachedItem = await cache.GetOrCreateAsync("any", asyncFactoryMock.Object, Expiration); Assert.Same(item, cachedItem); asyncFactoryMock.Verify(asyncFinder => asyncFinder(), Times.Once); }
public async Task Null_value_does_not_throw_and_does_not_get_cached_async() { var cache = new MKCache <Item>(); var factoryMock = new Mock <Func <Task <Item> > >(); factoryMock.Setup(factory => factory()).Returns(Task.FromResult(null as Item) !); var nullItem = await cache.GetOrCreateAsync("any", factoryMock.Object, Expiration); factoryMock.Verify(factory => factory(), Times.Once); Assert.Null(nullItem); nullItem = await cache.GetOrCreateAsync("any", factoryMock.Object, Expiration); factoryMock.Verify(factory => factory(), Times.Exactly(2)); Assert.Null(nullItem); }
public async Task Running_async_fetchers_are_not_reused_by_configuration() { var asyncFactoryMock = new Mock <Func <Task <Item> > >(); asyncFactoryMock.Setup(asyncFactory => asyncFactory()).Returns(() => Task.Run(async() => { // A time long enough for the test to request n concurrent async factories invocations await Task.Delay(TimeSpan.FromSeconds(2)); return(new Item()); })); var cache = new MKCache <Item> { ReuseRunningAsyncFetchers = false }; var foundItemTask = cache.GetOrCreateAsync("any", asyncFactoryMock.Object, Expiration); // Simulate concurrent request for the same object (same key), while the item is not yet been cached. var concurrentFinders = new List <Task <Item> >(); var n = 100; for (int i = 0; i < n; i++) { concurrentFinders.Add(cache.GetOrCreateAsync("any", asyncFactoryMock.Object, Expiration)); } var foundItem = await foundItemTask; await Task.WhenAll(concurrentFinders); // Tasks have not been reused, therefore each item is different. concurrentFinders.ForEach(t => Assert.NotEqual(t.Result.Id, foundItem.Id)); // Ensure that all the delegates have been invoked. asyncFactoryMock.Verify(asyncFinder => asyncFinder(), Times.Exactly(n + 1)); }