public async Task <UrlAppearanceCount> CountAppearances([FromBody] UrlAppearanceQuery query)
        {
            int count;
            var tokenSource = new CancellationTokenSource();

            tokenSource.CancelAfter(CancelAfterMilliSecond);

            if (!_memoryCache.TryGetValue((query.Keywords, query.Url), out count))
            {
                try
                {
                    count = await _scraper.SearchKeywordsAndCountUrlAppearances(query.Keywords, query.Url, tokenSource.Token);

                    _memoryCache.Set((query.Keywords, query.Url), count, DefaultMemoryCacheEntryOptions);
                }
                catch (InvalidOperationException ex)
                {
                    _logger.LogError(ex, "Scraper failed, did search engine change its layout?");

                    return(GetUrlApperanceCount(query, ""));
                }
            }

            return(GetUrlApperanceCount(query, count.ToString()));
        }
 private UrlAppearanceCount GetUrlApperanceCount(UrlAppearanceQuery query, string count)
 {
     return(new UrlAppearanceCount
     {
         Keywords = query.Keywords,
         Url = query.Url,
         Count = count
     });
 }
        public async Task CountAppearances_UsesCache_WhenCacheHasEntryAndNotExpired()
        {
            // Arrange
            var query = new UrlAppearanceQuery
            {
                Keywords = "abc",
                Url      = "def"
            };
            var expected            = 1;
            var _memoryCacheWithHit = MockMemoryCacheService.GetMemoryCache(expected, true);
            var controller          = new UrlAppearanceController(_options.Object, _memoryCacheWithHit, _scraper.Object, _logger.Object);

            // Act
            var result = await controller.CountAppearances(query);

            // Assert
            _scraper.Verify(s => s.SearchKeywordsAndCountUrlAppearances(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>()), Times.Never);
            Assert.Equal("abc", result.Keywords);
            Assert.Equal("def", result.Url);
            Assert.Equal("1", result.Count);
        }
        public async Task CountAppearances_DoesNotUsesCache_WhenNoCacheEntry()
        {
            // Arrange
            var query = new UrlAppearanceQuery
            {
                Keywords = "abc",
                Url      = "def"
            };
            var _memoryCacheWithMiss = MockMemoryCacheService.GetMemoryCache(null, false);

            _scraper.Setup(s => s.SearchKeywordsAndCountUrlAppearances(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>())).ReturnsAsync(2);
            var controller = new UrlAppearanceController(_options.Object, _memoryCacheWithMiss, _scraper.Object, _logger.Object);

            // Act
            var result = await controller.CountAppearances(query);

            // Assert
            _scraper.Verify(s => s.SearchKeywordsAndCountUrlAppearances(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <CancellationToken>()), Times.Once);
            Assert.Equal("abc", result.Keywords);
            Assert.Equal("def", result.Url);
            Assert.Equal("2", result.Count);
        }