Skip to content

Commit 3e83e7f

Browse files
authored
Fix for statistics not refreshing (#8015)
1 parent 3404a49 commit 3e83e7f

4 files changed

Lines changed: 53 additions & 21 deletions

File tree

src/NuGetGallery/Services/JsonStatisticsService.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public class JsonStatisticsService : IStatisticsService
3232
/// </summary>
3333
private readonly IReportService _reportService;
3434

35+
/// <summary>
36+
/// Mockable source of current time.
37+
/// </summary>
38+
private readonly IDateTimeProvider _dateTimeProvider;
39+
3540
/// <summary>
3641
/// The semaphore used to update the statistics service's reports.
3742
/// </summary>
@@ -52,9 +57,10 @@ public class JsonStatisticsService : IStatisticsService
5257
private readonly List<StatisticsNuGetUsageItem> _nuGetClientVersion = new List<StatisticsNuGetUsageItem>();
5358
private readonly List<StatisticsWeeklyUsageItem> _last6Weeks = new List<StatisticsWeeklyUsageItem>();
5459

55-
public JsonStatisticsService(IReportService reportService)
60+
public JsonStatisticsService(IReportService reportService, IDateTimeProvider dateTimeProvider)
5661
{
57-
_reportService = reportService;
62+
_reportService = reportService ?? throw new ArgumentNullException(nameof(reportService));
63+
_dateTimeProvider = dateTimeProvider ?? throw new ArgumentNullException(nameof(dateTimeProvider));
5864
}
5965

6066
public StatisticsReportResult PackageDownloadsResult { get; private set; }
@@ -126,7 +132,7 @@ public async Task Refresh()
126132
.Select(r => r.LastUpdatedUtc)
127133
.FirstOrDefault();
128134

129-
_lastRefresh = DateTime.UtcNow;
135+
_lastRefresh = _dateTimeProvider.UtcNow;
130136
}
131137
finally
132138
{
@@ -143,7 +149,7 @@ private bool ShouldRefresh()
143149
return true;
144150
}
145151

146-
return (_lastRefresh - DateTime.UtcNow) >= _refreshInterval;
152+
return (_dateTimeProvider.UtcNow - _lastRefresh) >= _refreshInterval;
147153
}
148154

149155
private Task<StatisticsReportResult> LoadDownloadPackages()

tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2780,7 +2780,7 @@ public async Task VerifyRecentPopularityStatsDownloads()
27802780

27812781
var controller = new TestableApiController(GetConfigurationService())
27822782
{
2783-
StatisticsService = new JsonStatisticsService(fakeReportService.Object),
2783+
StatisticsService = new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()),
27842784
};
27852785

27862786
TestUtility.SetupUrlHelperForUrlGeneration(controller);
@@ -2831,7 +2831,7 @@ public async Task VerifyRecentPopularityStatsDownloadsCount()
28312831

28322832
var controller = new TestableApiController(GetConfigurationService())
28332833
{
2834-
StatisticsService = new JsonStatisticsService(fakeReportService.Object),
2834+
StatisticsService = new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()),
28352835
};
28362836

28372837
TestUtility.SetupUrlHelperForUrlGeneration(controller);

tests/NuGetGallery.Facts/Controllers/StatisticsControllerFacts.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public async Task StatisticsHomePage_ValidateReportStructureAndAvailability()
108108
fakeReportService.Setup(x => x.Load("nugetclientversion.json")).Returns(Task.FromResult(new StatisticsReport(fakeNuGetClientVersion, DateTime.MinValue)));
109109
fakeReportService.Setup(x => x.Load("last6weeks.json")).Returns(Task.FromResult(new StatisticsReport(fakeLast6Weeks, updatedUtc)));
110110

111-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
111+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
112112

113113
var model = (StatisticsPackagesViewModel)((ViewResult)await controller.Index()).Model;
114114

@@ -218,7 +218,7 @@ public async Task StatisticsHomePage_ValidateFullReportStructureAndAvailability(
218218
fakeReportService.Setup(x => x.Load("nugetclientversion.json")).Returns(Task.FromResult(new StatisticsReport(fakeNuGetClientVersion, DateTime.UtcNow)));
219219
fakeReportService.Setup(x => x.Load("last6weeks.json")).Returns(Task.FromResult(new StatisticsReport(fakeLast6Weeks, DateTime.UtcNow)));
220220

221-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
221+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
222222

223223
var model = (StatisticsPackagesViewModel)((ViewResult)await controller.Index()).Model;
224224

@@ -282,7 +282,7 @@ public async Task StatisticsHomePage_Packages_ValidateReportStructureAndAvailabi
282282

283283
fakeReportService.Setup(x => x.Load("recentpopularity.json")).Returns(Task.FromResult(new StatisticsReport(fakePackageReport, DateTime.UtcNow)));
284284

285-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
285+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
286286

287287
var model = (StatisticsPackagesViewModel)((ViewResult)await controller.Packages()).Model;
288288

@@ -330,7 +330,7 @@ public async Task StatisticsHomePage_PackageVersions_ValidateReportStructureAndA
330330
fakeReportService.Setup(x => x.Load("recentpopularitydetail.json")).Returns(Task.FromResult(new StatisticsReport(fakePackageVersionReport, updatedUtc1)));
331331
fakeReportService.Setup(x => x.Load("recentcommunitypopularitydetail.json")).Returns(Task.FromResult(new StatisticsReport(fakePackageVersionReport, updatedUtc2)));
332332

333-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
333+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
334334

335335
var model = (StatisticsPackagesViewModel)((ViewResult)await controller.PackageVersions()).Model;
336336

@@ -353,7 +353,7 @@ public async void StatisticsHomePage_Per_Package_ValidateModel()
353353

354354
var fakeReportService = new Mock<IReportService>();
355355

356-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
356+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
357357

358358
TestUtility.SetupUrlHelperForUrlGeneration(controller);
359359

@@ -425,7 +425,7 @@ public async Task StatisticsHomePage_Per_Package_ValidateReportStructureAndAvail
425425
var updatedUtc = new DateTime(2001, 01, 01, 10, 20, 30);
426426
fakeReportService.Setup(x => x.Load(reportName)).Returns(Task.FromResult(new StatisticsReport(fakeReport, updatedUtc)));
427427

428-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
428+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
429429

430430
TestUtility.SetupUrlHelperForUrlGeneration(controller);
431431

@@ -507,7 +507,7 @@ public async Task StatisticsHomePage_Per_Package_ValidateReportStructureAndAvail
507507
var updatedUtc = new DateTime(2001, 01, 01, 10, 20, 30);
508508
fakeReportService.Setup(x => x.Load(reportName)).Returns(Task.FromResult(new StatisticsReport(fakeReport, updatedUtc)));
509509

510-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
510+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
511511

512512
TestUtility.SetupUrlHelperForUrlGeneration(controller);
513513

@@ -537,7 +537,7 @@ public async void Statistics_By_Client_Operation_ValidateModel()
537537

538538
var fakeReportService = new Mock<IReportService>();
539539

540-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
540+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
541541

542542
TestUtility.SetupUrlHelperForUrlGeneration(controller);
543543

@@ -611,7 +611,7 @@ public async Task Statistics_By_Client_Operation_ValidateReportStructureAndAvail
611611
var updatedUtc = new DateTime(2001, 01, 01, 10, 20, 30);
612612
fakeReportService.Setup(x => x.Load(reportName)).Returns(Task.FromResult(new StatisticsReport(fakeReport, updatedUtc)));
613613

614-
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object));
614+
var controller = new StatisticsController(new JsonStatisticsService(fakeReportService.Object, new DateTimeProvider()));
615615

616616
TestUtility.SetupUrlHelperForUrlGeneration(controller);
617617

tests/NuGetGallery.Facts/Services/JsonStatisticsServiceFacts.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,25 @@ public async Task LoadReportsFailsIfDownloadsIsMissingOrNotInteger()
107107

108108
VerifyReportsLoadedOnce();
109109
}
110+
111+
[Fact]
112+
public async Task RefreshesAfter1Hour()
113+
{
114+
var time = DateTime.UtcNow - TimeSpan.FromHours(2);
115+
_dateTimeProvider
116+
.SetupGet(st => st.UtcNow)
117+
.Returns(time);
118+
119+
await _target.Refresh();
120+
VerifyReportsLoadedOnce();
121+
122+
_dateTimeProvider
123+
.SetupGet(st => st.UtcNow)
124+
.Returns(time + TimeSpan.FromHours(1.1));
125+
126+
await _target.Refresh();
127+
VerifyReportsLoaded(Times.Exactly(2));
128+
}
110129
}
111130

112131
public class FactsBase
@@ -124,6 +143,7 @@ public class FactsBase
124143
public readonly int Package2Downloads = 789;
125144

126145
protected readonly Mock<IReportService> _reportService;
146+
protected readonly Mock<IDateTimeProvider> _dateTimeProvider;
127147
protected readonly JsonStatisticsService _target;
128148

129149
protected Dictionary<string, object>[] PackageDownloadsReport => new[]
@@ -168,8 +188,12 @@ public class FactsBase
168188
public FactsBase()
169189
{
170190
_reportService = new Mock<IReportService>();
191+
_dateTimeProvider = new Mock<IDateTimeProvider>();
192+
_dateTimeProvider
193+
.SetupGet(st => st.UtcNow)
194+
.Returns(() => DateTime.UtcNow);
171195

172-
_target = new JsonStatisticsService(_reportService.Object);
196+
_target = new JsonStatisticsService(_reportService.Object, _dateTimeProvider.Object);
173197
}
174198

175199
protected void Mock(
@@ -223,20 +247,22 @@ Task<StatisticsReport> CreateReport(IEnumerable<Dictionary<string, object>> repo
223247
.Returns(CreateReport(communityPackageVersionDownloadsReport, communityPackageVersionsLastUpdateTimeUtc));
224248
}
225249

226-
protected void VerifyReportsLoadedOnce()
250+
protected void VerifyReportsLoaded(Times times)
227251
{
228252
_reportService
229-
.Verify(s => s.Load(It.Is<string>(n => n == "recentpopularity.json")), Times.Once);
253+
.Verify(s => s.Load(It.Is<string>(n => n == "recentpopularity.json")), times);
230254

231255
_reportService
232-
.Verify(s => s.Load(It.Is<string>(n => n == "recentcommunitypopularity.json")), Times.Once);
256+
.Verify(s => s.Load(It.Is<string>(n => n == "recentcommunitypopularity.json")), times);
233257

234258
_reportService
235-
.Verify(s => s.Load(It.Is<string>(n => n == "recentpopularitydetail.json")), Times.Once);
259+
.Verify(s => s.Load(It.Is<string>(n => n == "recentpopularitydetail.json")), times);
236260

237261
_reportService
238-
.Verify(s => s.Load(It.Is<string>(n => n == "recentcommunitypopularitydetail.json")), Times.Once);
262+
.Verify(s => s.Load(It.Is<string>(n => n == "recentcommunitypopularitydetail.json")), times);
239263
}
264+
265+
protected void VerifyReportsLoadedOnce() => VerifyReportsLoaded(Times.Once());
240266
}
241267
}
242268
}

0 commit comments

Comments
 (0)