public async Task ProcessAsync_ReturnsCachedValue_IfEnabled() { // Arrange var id = "unique-id"; var childContent = "original-child-content"; var cache = new MemoryCache(new MemoryCacheOptions()); var tagHelperContext = GetTagHelperContext(id); var tagHelperOutput = GetTagHelperOutput( attributes: new TagHelperAttributeList(), childContent: childContent); var cacheTagHelper = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true }; // Act await cacheTagHelper.ProcessAsync(tagHelperContext, tagHelperOutput); // Assert Assert.Empty(tagHelperOutput.PreContent.GetContent()); Assert.Empty(tagHelperOutput.PostContent.GetContent()); Assert.True(tagHelperOutput.IsContentModified); Assert.Equal(childContent, tagHelperOutput.Content.GetContent()); }
public async Task ProcessAsync_UsesExpiresAfter_ToExpireCacheEntry() { // Arrange - 1 var currentTime = new DateTimeOffset(2010, 1, 1, 0, 0, 0, TimeSpan.Zero); var id = "unique-id"; var childContent1 = "original-child-content"; var clock = new Mock <ISystemClock>(); clock.SetupGet(p => p.UtcNow) .Returns(() => currentTime); var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object }); var tagHelperContext1 = GetTagHelperContext(id); var tagHelperOutput1 = GetTagHelperOutput(childContent: childContent1); tagHelperOutput1.PreContent.SetContent("<cache>"); tagHelperOutput1.PostContent.SetContent("</cache>"); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), ExpiresAfter = TimeSpan.FromMinutes(10) }; // Act - 1 await cacheTagHelper1.ProcessAsync(tagHelperContext1, tagHelperOutput1); // Assert - 1 Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.True(tagHelperOutput1.IsContentModified); Assert.Equal(childContent1, tagHelperOutput1.Content.GetContent()); // Arrange - 2 var childContent2 = "different-content"; var tagHelperContext2 = GetTagHelperContext(id); var tagHelperOutput2 = GetTagHelperOutput(childContent: childContent2); tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PostContent.SetContent("</cache>"); var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), ExpiresAfter = TimeSpan.FromMinutes(10) }; currentTime = currentTime.AddMinutes(11); // Act - 2 await cacheTagHelper2.ProcessAsync(tagHelperContext2, tagHelperOutput2); // Assert - 2 Assert.Empty(tagHelperOutput2.PreContent.GetContent()); Assert.Empty(tagHelperOutput2.PostContent.GetContent()); Assert.True(tagHelperOutput2.IsContentModified); Assert.Equal(childContent2, tagHelperOutput2.Content.GetContent()); }
public async Task ProcessAsync_RecalculatesValueIfCacheKeyChanges() { // Arrange - 1 var id = "unique-id"; var childContent1 = "original-child-content"; var cache = new MemoryCache(new MemoryCacheOptions()); var tagHelperContext1 = GetTagHelperContext(id); var tagHelperOutput1 = GetTagHelperOutput(childContent: childContent1); tagHelperOutput1.PreContent.Append("<cache>"); tagHelperOutput1.PostContent.SetContent("</cache>"); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { VaryByCookie = "cookie1,cookie2", ViewContext = GetViewContext(), }; cacheTagHelper1.ViewContext.HttpContext.Request.Headers["Cookie"] = "cookie1=value1;cookie2=value2"; // Act - 1 await cacheTagHelper1.ProcessAsync(tagHelperContext1, tagHelperOutput1); // Assert - 1 Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.True(tagHelperOutput1.IsContentModified); Assert.Equal(childContent1, tagHelperOutput1.Content.GetContent()); // Arrange - 2 var childContent2 = "different-content"; var tagHelperContext2 = GetTagHelperContext(id); var tagHelperOutput2 = GetTagHelperOutput(childContent: childContent2); tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PostContent.SetContent("</cache>"); var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { VaryByCookie = "cookie1,cookie2", ViewContext = GetViewContext(), }; cacheTagHelper2.ViewContext.HttpContext.Request.Headers["Cookie"] = "cookie1=value1;cookie2=not-value2"; // Act - 2 await cacheTagHelper2.ProcessAsync(tagHelperContext2, tagHelperOutput2); // Assert - 2 Assert.Empty(tagHelperOutput2.PreContent.GetContent()); Assert.Empty(tagHelperOutput2.PostContent.GetContent()); Assert.True(tagHelperOutput2.IsContentModified); Assert.Equal(childContent2, tagHelperOutput2.Content.GetContent()); }
public async Task ProcessAsync_ReturnsCachedValue_IfVaryByParamIsUnchanged() { // Arrange - 1 var id = "unique-id"; var childContent = "original-child-content"; var cache = new MemoryCache(new MemoryCacheOptions()); var tagHelperContext1 = GetTagHelperContext(id); var tagHelperOutput1 = GetTagHelperOutput( attributes: new TagHelperAttributeList(), childContent: childContent); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { VaryByQuery = "key1,key2", ViewContext = GetViewContext(), }; cacheTagHelper1.ViewContext.HttpContext.Request.QueryString = new QueryString( "?key1=value1&key2=value2"); // Act - 1 await cacheTagHelper1.ProcessAsync(tagHelperContext1, tagHelperOutput1); // Assert - 1 Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.True(tagHelperOutput1.IsContentModified); Assert.Equal(childContent, tagHelperOutput1.Content.GetContent()); // Arrange - 2 var tagHelperContext2 = GetTagHelperContext(id); var tagHelperOutput2 = GetTagHelperOutput( attributes: new TagHelperAttributeList(), childContent: "different-content"); var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { VaryByQuery = "key1,key2", ViewContext = GetViewContext(), }; cacheTagHelper2.ViewContext.HttpContext.Request.QueryString = new QueryString( "?key1=value1&key2=value2"); // Act - 2 await cacheTagHelper2.ProcessAsync(tagHelperContext2, tagHelperOutput2); // Assert - 2 Assert.Empty(tagHelperOutput2.PreContent.GetContent()); Assert.Empty(tagHelperOutput2.PostContent.GetContent()); Assert.True(tagHelperOutput2.IsContentModified); Assert.Equal(childContent, tagHelperOutput2.Content.GetContent()); }
public async Task ProcessAsync_SetsEntrySize_ForPlaceholderAndFinalCacheEntries() { // Arrange var mockCache = new Mock <IMemoryCache>(); var tempEntry = new Mock <ICacheEntry>(); tempEntry.SetupAllProperties(); tempEntry.Setup(e => e.ExpirationTokens).Returns(new List <IChangeToken>()); tempEntry.Setup(e => e.PostEvictionCallbacks).Returns(new List <PostEvictionCallbackRegistration>()); var finalEntry = new Mock <ICacheEntry>(); finalEntry.SetupAllProperties(); finalEntry.Setup(e => e.ExpirationTokens).Returns(new List <IChangeToken>()); finalEntry.Setup(e => e.PostEvictionCallbacks).Returns(new List <PostEvictionCallbackRegistration>()); object value; mockCache .Setup(s => s.TryGetValue(It.IsAny <object>(), out value)) .Returns(false); mockCache.SetupSequence(mc => mc.CreateEntry(It.IsAny <object>())) .Returns(tempEntry.Object) .Returns(finalEntry.Object); var id = "unique-id"; var childContent1 = "original-child-content"; var tagHelperContext1 = GetTagHelperContext(id); var tagHelperOutput1 = GetTagHelperOutput(childContent: childContent1); tagHelperOutput1.PreContent.Append("<cache>"); tagHelperOutput1.PostContent.SetContent("</cache>"); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(mockCache.Object), new HtmlTestEncoder()) { ViewContext = GetViewContext(), }; // Act await cacheTagHelper1.ProcessAsync(tagHelperContext1, tagHelperOutput1); // Assert Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.True(tagHelperOutput1.IsContentModified); Assert.Equal(childContent1, tagHelperOutput1.Content.GetContent()); tempEntry.VerifySet(e => e.Size = 64); finalEntry.VerifySet(e => e.Size = childContent1.Length * 2); }
public async Task ProcessAsync_WorksForNestedCacheTagHelpers() { // Arrange var expected = "Hello world"; var cache = new MemoryCache(new MemoryCacheOptions()); var encoder = new HtmlTestEncoder(); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), encoder) { ViewContext = GetViewContext(), Enabled = true }; var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), encoder) { ViewContext = GetViewContext(), Enabled = true }; var tagHelperOutput2 = new TagHelperOutput( "cache", new TagHelperAttributeList(), getChildContentAsync: (useCachedResult, _) => { var content = new DefaultTagHelperContent(); content.SetContent(expected); return(Task.FromResult <TagHelperContent>(content)); }); var tagHelperOutput1 = new TagHelperOutput( "cache", new TagHelperAttributeList(), getChildContentAsync: async(useCachedResult, _) => { var context = GetTagHelperContext("test2"); var output = tagHelperOutput2; await cacheTagHelper2.ProcessAsync(context, output); return(await output.GetChildContentAsync()); }); // Act await cacheTagHelper1.ProcessAsync(GetTagHelperContext(), tagHelperOutput1); // Assert Assert.Equal(encoder.Encode(expected), tagHelperOutput1.Content.GetContent()); }
public async Task ProcessAsync_AwaitersUseTheResultOfExecutor() { // Arrange var expected = "Hello world"; var cache = new TestMemoryCache(); var encoder = new HtmlTestEncoder(); // The two instances represent two instances of the same cache tag helper appearance in the page. var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), encoder) { ViewContext = GetViewContext(), Enabled = true }; var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), encoder) { ViewContext = GetViewContext(), Enabled = true }; var invokeCount = 0; Func <bool, HtmlEncoder, Task <TagHelperContent> > getChildContentAsync = (useCachedResult, _) => { invokeCount++; var content = new DefaultTagHelperContent(); content.SetContent(expected); return(Task.FromResult <TagHelperContent>(content)); }; var tagHelperOutput1 = new TagHelperOutput("cache", new TagHelperAttributeList(), getChildContentAsync); var tagHelperOutput2 = new TagHelperOutput("cache", new TagHelperAttributeList(), getChildContentAsync); // Act var task1 = Task.Run(() => cacheTagHelper1.ProcessAsync(GetTagHelperContext(cache.Key1), tagHelperOutput1)); var task2 = Task.Run(() => cacheTagHelper2.ProcessAsync(GetTagHelperContext(cache.Key2), tagHelperOutput2)); // Assert await Task.WhenAll(task1, task2); Assert.Equal(1, invokeCount); // We expect getChildContentAsync to be invoked exactly once. Assert.Equal(encoder.Encode(expected), tagHelperOutput1.Content.GetContent()); Assert.Equal(encoder.Encode(expected), tagHelperOutput2.Content.GetContent()); }
public async Task ProcessAsync_ThrowsExceptionForAwaiters_IfExecutorEncountersAnException() { // Arrange var expected = new DivideByZeroException(); var cache = new TestMemoryCache(); // The two instances represent two instances of the same cache tag helper appearance in the page. var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true }; var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true }; var invokeCount = 0; var tagHelperOutput = new TagHelperOutput( "cache", new TagHelperAttributeList(), getChildContentAsync: (useCachedResult, _) => { invokeCount++; throw expected; }); // Act var task1 = Task.Run(() => cacheTagHelper1.ProcessAsync(GetTagHelperContext(cache.Key1), tagHelperOutput)); var task2 = Task.Run(() => cacheTagHelper2.ProcessAsync(GetTagHelperContext(cache.Key2), tagHelperOutput)); // Assert await Assert.ThrowsAsync <DivideByZeroException>(() => task1); await Assert.ThrowsAsync <DivideByZeroException>(() => task2); Assert.Equal(1, invokeCount); }
public async Task ProcessAsync_DoesNotCache_IfDisabled() { // Arrange var id = "unique-id"; var childContent = "original-child-content"; var cache = new Mock <IMemoryCache>(); var value = new Mock <ICacheEntry>(); value.Setup(c => c.Value).Returns(new DefaultTagHelperContent().SetContent("ok")); cache.Setup(c => c.CreateEntry( /*key*/ It.IsAny <string>())) .Returns((object key) => value.Object) .Verifiable(); object cacheResult; cache.Setup(c => c.TryGetValue(It.IsAny <string>(), out cacheResult)) .Returns(false); var tagHelperContext = GetTagHelperContext(id); var tagHelperOutput = GetTagHelperOutput( attributes: new TagHelperAttributeList(), childContent: childContent); var cacheTagHelper = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache.Object), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = false }; // Act await cacheTagHelper.ProcessAsync(tagHelperContext, tagHelperOutput); // Assert Assert.Equal(childContent, tagHelperOutput.Content.GetContent()); cache.Verify(c => c.CreateEntry( /*key*/ It.IsAny <string>()), Times.Never); }
public async Task ProcessAsync_ExceptionInProcessing_DoNotThrowInSubsequentRequests() { // Arrange var id = "unique-id"; var childContent = "some-content"; var cache = new MemoryCache(new MemoryCacheOptions()); var counter = 0; Task <TagHelperContent> GetChildContentAsync(bool useCachedResult, HtmlEncoder encoder) { counter++; if (counter < 3) { // throw on first two calls throw new Exception(); } else { // produce content on third call var tagHelperContent = new DefaultTagHelperContent(); tagHelperContent.SetHtmlContent(childContent); return(Task.FromResult <TagHelperContent>(tagHelperContent)); } } var tagHelperContext1 = GetTagHelperContext(id); var tagHelperContext2 = GetTagHelperContext(id); var tagHelperContext3 = GetTagHelperContext(id); var tagHelperContext4 = GetTagHelperContext(id); var tagHelperOutput1 = new TagHelperOutput("cache", new TagHelperAttributeList(), GetChildContentAsync); var tagHelperOutput2 = new TagHelperOutput("cache", new TagHelperAttributeList(), GetChildContentAsync); var tagHelperOutput3 = new TagHelperOutput("cache", new TagHelperAttributeList(), GetChildContentAsync); var tagHelperOutput4 = new TagHelperOutput("cache", new TagHelperAttributeList(), GetChildContentAsync); var cacheTagHelper = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true, ExpiresAfter = TimeSpan.FromHours(1.0) }; // Act - 1 await Assert.ThrowsAsync <Exception>(() => cacheTagHelper.ProcessAsync(tagHelperContext1, tagHelperOutput1)); // Assert - 1 Assert.Equal(1, counter); Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.False(tagHelperOutput1.IsContentModified); Assert.Empty(tagHelperOutput1.Content.GetContent()); // Act - 2 await Assert.ThrowsAsync <Exception>(() => cacheTagHelper.ProcessAsync(tagHelperContext2, tagHelperOutput2)); // Assert - 2 Assert.Equal(2, counter); Assert.Empty(tagHelperOutput2.PreContent.GetContent()); Assert.Empty(tagHelperOutput2.PostContent.GetContent()); Assert.False(tagHelperOutput2.IsContentModified); Assert.Empty(tagHelperOutput2.Content.GetContent()); // Act - 3 await cacheTagHelper.ProcessAsync(tagHelperContext3, tagHelperOutput3); // Assert - 3 Assert.Equal(3, counter); Assert.Empty(tagHelperOutput3.PreContent.GetContent()); Assert.Empty(tagHelperOutput3.PostContent.GetContent()); Assert.True(tagHelperOutput3.IsContentModified); Assert.Equal(childContent, tagHelperOutput3.Content.GetContent()); // Act - 4 await cacheTagHelper.ProcessAsync(tagHelperContext4, tagHelperOutput4); // Assert - 4 Assert.Equal(3, counter); Assert.Empty(tagHelperOutput4.PreContent.GetContent()); Assert.Empty(tagHelperOutput4.PostContent.GetContent()); Assert.True(tagHelperOutput4.IsContentModified); Assert.Equal(childContent, tagHelperOutput4.Content.GetContent()); }
public async Task ProcessAsync_ExceptionInProcessing_DoesntBlockConcurrentRequests() { // Arrange var id = "unique-id"; var childContent = "some-content"; var event1 = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); var event2 = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); var event3 = new TaskCompletionSource <int>(TaskCreationOptions.RunContinuationsAsynchronously); var calls = 0; var cache = new MemoryCache(new MemoryCacheOptions()); var tagHelperContext1 = GetTagHelperContext(id + 1); var tagHelperContext2 = GetTagHelperContext(id + 2); var tagHelperOutput1 = new TagHelperOutput( "cache", new TagHelperAttributeList(), getChildContentAsync: (useCachedResult, encoder) => { calls++; event2.SetResult(0); throw new Exception(); }); var tagHelperOutput2 = new TagHelperOutput( "cache", new TagHelperAttributeList(), getChildContentAsync: async(useCachedResult, encoder) => { calls++; await event3.Task.TimeoutAfter(TimeSpan.FromSeconds(5)); var tagHelperContent = new DefaultTagHelperContent(); tagHelperContent.SetHtmlContent(childContent); return(tagHelperContent); }); var cacheTagHelper1 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true }; var cacheTagHelper2 = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), Enabled = true }; // Act var task1 = Task.Run(async() => { await event1.Task.TimeoutAfter(TimeSpan.FromSeconds(5)); await Assert.ThrowsAsync <Exception>(() => cacheTagHelper1.ProcessAsync(tagHelperContext1, tagHelperOutput1)); event3.SetResult(0); }); var task2 = Task.Run(async() => { await event2.Task.TimeoutAfter(TimeSpan.FromSeconds(5)); await cacheTagHelper2.ProcessAsync(tagHelperContext2, tagHelperOutput2); }); event1.SetResult(0); await Task.WhenAll(task1, task2); // Assert Assert.Empty(tagHelperOutput1.PreContent.GetContent()); Assert.Empty(tagHelperOutput1.PostContent.GetContent()); Assert.False(tagHelperOutput1.IsContentModified); Assert.Empty(tagHelperOutput1.Content.GetContent()); Assert.Empty(tagHelperOutput2.PreContent.GetContent()); Assert.Empty(tagHelperOutput2.PostContent.GetContent()); Assert.True(tagHelperOutput2.IsContentModified); Assert.Equal(childContent, tagHelperOutput2.Content.GetContent()); Assert.Equal(2, calls); }
public async Task ProcessAsync_FlowsEntryLinkThatAllowsAddingTriggersToAddedEntry() { // Arrange var id = "some-id"; var expectedContent = new DefaultTagHelperContent(); expectedContent.SetContent("some-content"); var tokenSource = new CancellationTokenSource(); var cache = new MemoryCache(new MemoryCacheOptions() { TrackLinkedCacheEntries = true }); var cacheEntryOptions = new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(tokenSource.Token)); var tagHelperContext = new TagHelperContext( tagName: "cache", allAttributes: new TagHelperAttributeList(), items: new Dictionary <object, object>(), uniqueId: id); var tagHelperOutput = new TagHelperOutput( "cache", new TagHelperAttributeList { { "attr", "value" } }, getChildContentAsync: (useCachedResult, encoder) => { if (!cache.TryGetValue("key1", out TagHelperContent tagHelperContent)) { tagHelperContent = expectedContent; cache.Set("key1", tagHelperContent, cacheEntryOptions); } return(Task.FromResult(tagHelperContent)); }); tagHelperOutput.PreContent.SetContent("<cache>"); tagHelperOutput.PostContent.SetContent("</cache>"); var cacheTagHelper = new CacheTagHelper(new CacheTagHelperMemoryCacheFactory(cache), new HtmlTestEncoder()) { ViewContext = GetViewContext(), }; var cacheTagKey = new CacheTagKey(cacheTagHelper, tagHelperContext); var key = cacheTagKey.GenerateKey(); // Act - 1 await cacheTagHelper.ProcessAsync(tagHelperContext, tagHelperOutput); var result = cache.TryGetValue(cacheTagKey, out Task <IHtmlContent> cachedValue); // Assert - 1 Assert.Equal("HtmlEncode[[some-content]]", tagHelperOutput.Content.GetContent()); Assert.True(result); // Act - 2 tokenSource.Cancel(); result = cache.TryGetValue(cacheTagKey, out cachedValue); // Assert - 2 Assert.False(result); Assert.Null(cachedValue); }