diff --git a/src/NuGet.Services.AzureSearch/NuGet.Services.AzureSearch.csproj b/src/NuGet.Services.AzureSearch/NuGet.Services.AzureSearch.csproj index 811747f86..805525192 100644 --- a/src/NuGet.Services.AzureSearch/NuGet.Services.AzureSearch.csproj +++ b/src/NuGet.Services.AzureSearch/NuGet.Services.AzureSearch.csproj @@ -192,9 +192,11 @@ + + diff --git a/src/NuGet.Services.AzureSearch/SearchService/AuxiliaryFileReloader.cs b/src/NuGet.Services.AzureSearch/SearchService/AuxiliaryFileReloader.cs index 1aeda9382..a3948fe8f 100644 --- a/src/NuGet.Services.AzureSearch/SearchService/AuxiliaryFileReloader.cs +++ b/src/NuGet.Services.AzureSearch/SearchService/AuxiliaryFileReloader.cs @@ -6,21 +6,25 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using NuGet.Services.AzureSearch.Wrappers; namespace NuGet.Services.AzureSearch.SearchService { public class AuxiliaryFileReloader : IAuxiliaryFileReloader { private readonly IAuxiliaryDataCache _cache; + private readonly ISystemTime _systemTime; private readonly IOptionsSnapshot _options; private readonly ILogger _logger; public AuxiliaryFileReloader( IAuxiliaryDataCache cache, + ISystemTime systemTime, IOptionsSnapshot options, ILogger logger) { _cache = cache ?? throw new ArgumentNullException(nameof(cache)); + _systemTime = systemTime ?? throw new ArgumentNullException(nameof(systemTime)); _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } @@ -39,7 +43,7 @@ public async Task ReloadContinuouslyAsync(CancellationToken token) } catch (Exception ex) { - _logger.LogError((EventId)0, ex, "An exception was thrown while reloading the auxiliary data."); + _logger.LogError(0, ex, "An exception was thrown while reloading the auxiliary data."); delay = _options.Value.AuxiliaryDataReloadFailureRetryFrequency; } @@ -52,7 +56,7 @@ public async Task ReloadContinuouslyAsync(CancellationToken token) _logger.LogInformation( "Waiting {Duration} before attempting to reload the auxiliary data again.", delay); - await Task.Delay(delay, token); + await _systemTime.Delay(delay, token); } } } diff --git a/src/NuGet.Services.AzureSearch/Wrappers/ISystemTime.cs b/src/NuGet.Services.AzureSearch/Wrappers/ISystemTime.cs new file mode 100644 index 000000000..c30cb5f7f --- /dev/null +++ b/src/NuGet.Services.AzureSearch/Wrappers/ISystemTime.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace NuGet.Services.AzureSearch.Wrappers +{ + /// + /// A wrapper that allows for unit tests related to system time. + /// + public interface ISystemTime + { + Task Delay(TimeSpan delay); + Task Delay(TimeSpan delay, CancellationToken token); + } +} \ No newline at end of file diff --git a/src/NuGet.Services.AzureSearch/Wrappers/SystemTime.cs b/src/NuGet.Services.AzureSearch/Wrappers/SystemTime.cs new file mode 100644 index 000000000..f3e8e054e --- /dev/null +++ b/src/NuGet.Services.AzureSearch/Wrappers/SystemTime.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace NuGet.Services.AzureSearch.Wrappers +{ + public class SystemTime : ISystemTime + { + public async Task Delay(TimeSpan delay) + { + await Task.Delay(delay); + } + + public async Task Delay(TimeSpan delay, CancellationToken token) + { + await Task.Delay(delay, token); + } + } +} diff --git a/tests/NuGet.Services.AzureSearch.Tests/SearchService/AuxiliaryFileReloaderFacts.cs b/tests/NuGet.Services.AzureSearch.Tests/SearchService/AuxiliaryFileReloaderFacts.cs index 6423be8bf..0ae732af7 100644 --- a/tests/NuGet.Services.AzureSearch.Tests/SearchService/AuxiliaryFileReloaderFacts.cs +++ b/tests/NuGet.Services.AzureSearch.Tests/SearchService/AuxiliaryFileReloaderFacts.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Options; using Moq; using NuGet.Services.AzureSearch.Support; +using NuGet.Services.AzureSearch.Wrappers; using Xunit; using Xunit.Abstractions; @@ -50,12 +51,11 @@ public async Task UsesReloadFrequencyOnSuccess() _config.AuxiliaryDataReloadFrequency = TimeSpan.FromMilliseconds(100); _config.AuxiliaryDataReloadFailureRetryFrequency = TimeSpan.Zero; - var stopwatch = Stopwatch.StartNew(); await _target.ReloadContinuouslyAsync(_cts.Token); - stopwatch.Stop(); _cache.Verify(x => x.TryLoadAsync(It.IsAny()), Times.Exactly(2)); - Assert.InRange(stopwatch.Elapsed, _config.AuxiliaryDataReloadFrequency, TimeSpan.FromHours(1)); + _systemTime.Verify(x => x.Delay(_config.AuxiliaryDataReloadFrequency, _cts.Token), Times.Once); + _systemTime.Verify(x => x.Delay(It.IsAny(), It.IsAny()), Times.Once); } [Fact] @@ -77,18 +77,18 @@ public async Task UsesReloadFailureRetryFrequencyOnSuccess() _config.AuxiliaryDataReloadFrequency = TimeSpan.Zero; _config.AuxiliaryDataReloadFailureRetryFrequency = TimeSpan.FromMilliseconds(100); - var stopwatch = Stopwatch.StartNew(); await _target.ReloadContinuouslyAsync(_cts.Token); - stopwatch.Stop(); _cache.Verify(x => x.TryLoadAsync(It.IsAny()), Times.Exactly(2)); - Assert.InRange(stopwatch.Elapsed, _config.AuxiliaryDataReloadFailureRetryFrequency, TimeSpan.FromHours(1)); + _systemTime.Verify(x => x.Delay(_config.AuxiliaryDataReloadFailureRetryFrequency, _cts.Token), Times.Once); + _systemTime.Verify(x => x.Delay(It.IsAny(), It.IsAny()), Times.Once); } } public abstract class BaseFacts { protected readonly Mock _cache; + protected readonly Mock _systemTime; protected readonly SearchServiceConfiguration _config; protected readonly Mock> _options; protected readonly RecordingLogger _logger; @@ -98,6 +98,7 @@ public abstract class BaseFacts public BaseFacts(ITestOutputHelper output) { _cache = new Mock(); + _systemTime = new Mock(); _config = new SearchServiceConfiguration(); _options = new Mock>(); _logger = output.GetLogger(); @@ -114,6 +115,7 @@ public BaseFacts(ITestOutputHelper output) _target = new AuxiliaryFileReloader( _cache.Object, + _systemTime.Object, _options.Object, _logger); }