diff --git a/client/ts/admin.ts b/client/ts/admin.ts index 827a6f74c006662473c8fcc7c101ecb74c51ec71..52bba78c13e0b251e97743e9d6f14c5c14883322 100644 --- a/client/ts/admin.ts +++ b/client/ts/admin.ts @@ -17,7 +17,12 @@ $(async () => { document.dispatchEvent(new Event("page-ready")); }); -function setupDiscrepancyViewer() { +/** + * Controls "load more" and "load less" buttons for + * resolved discrepancies list + * + */ +function setupDiscrepancyViewer() : void { var resolvedLoaded = 10; $("#load-more-resolved-btn").click(() => { diff --git a/src/shared/Models/Entities/Discrepancies.cs b/src/shared/Models/Entities/Discrepancies.cs index 184b75de62ae841303b4ee9d1506a6f81b1c08c6..c7e079d7fa01972c55cb702c0336f1f9806f0631 100644 --- a/src/shared/Models/Entities/Discrepancies.cs +++ b/src/shared/Models/Entities/Discrepancies.cs @@ -44,6 +44,10 @@ namespace StatusMonitor.Shared.Models.Entities public override string ToString() => ToStringWithTimeZone(); + /// <summary> + /// Human readable description of the discrepancy. + /// </summary> + /// <returns>Human readable description of the discrepancy</returns> public string Description() { switch (Type) { diff --git a/src/web/TagHelpers/UtcTimeTagHelper.cs b/src/web/TagHelpers/UtcTimeTagHelper.cs index 2b7bfc380eebdeb261456e2cd752cfce3f94ed2a..c350d1047aa1118095734590e9183968ac93c352 100644 --- a/src/web/TagHelpers/UtcTimeTagHelper.cs +++ b/src/web/TagHelpers/UtcTimeTagHelper.cs @@ -12,6 +12,10 @@ namespace StatusMonitor.Web.TagHelpers /// </summary> public class UtcTimeTagHelper : TagHelper { + /// <summary> + /// If true, the full date should be rendered + /// Otherwise, only time part should be rendered + /// </summary> public bool ShowDate { get; set; } = false; public DateTime Time { get; set; } diff --git a/src/web/ViewModels/DiscrepancyResolutionViewModel.cs b/src/web/ViewModels/DiscrepancyResolutionViewModel.cs index 57707d88bfc0eaa128a08fb5430480625baaa64f..189ef559c94427a7abe6b120d337ce49b3f47ffa 100644 --- a/src/web/ViewModels/DiscrepancyResolutionViewModel.cs +++ b/src/web/ViewModels/DiscrepancyResolutionViewModel.cs @@ -7,10 +7,14 @@ namespace StatusMonitor.Web.ViewModels { public class DiscrepancyResolutionViewModel { + /// <summary> + /// Timestamp when the discrepancy starts. + /// </summary> public DateTime DateFirstOffense { get; set; } /// <summary> /// Alias for DateFirstOffense. + /// Should represent the number of ticks (eq. DateTime.UtcNow.Ticks) /// </summary> [Required] public string Date diff --git a/test/ControllerTests/AdminController/AdminControllerTest.cs b/test/ControllerTests/AdminController/AdminControllerTest.cs index 030f5c9b4259abefd23389789c2a0f3eced1a188..3721b2bdebaf7cd6b161de68e7a23589d5ccd573 100644 --- a/test/ControllerTests/AdminController/AdminControllerTest.cs +++ b/test/ControllerTests/AdminController/AdminControllerTest.cs @@ -27,6 +27,10 @@ namespace StatusMonitor.Tests.ControllerTests private readonly Mock<IMetricService> _mockMetricService = new Mock<IMetricService>(); private readonly Mock<IApiController> _mockApiController = new Mock<IApiController>(); private readonly Mock<ICleanService> _mockCleanService = new Mock<ICleanService>(); + private readonly Mock<INotificationService> _mockNotificationService = new Mock<INotificationService>(); + private readonly Mock<IConfiguration> _mockConfig = new Mock<IConfiguration>(); + + private readonly IDataContext _context; private readonly AdminController _controller; @@ -43,7 +47,7 @@ namespace StatusMonitor.Tests.ControllerTests services.RegisterSharedServices(env, new Mock<IConfiguration>().Object); - var context = services + _context = services .BuildServiceProvider() .GetRequiredService<IDataContext>(); @@ -58,10 +62,11 @@ namespace StatusMonitor.Tests.ControllerTests _mockMetricService.Object, mockServiceProvider.Object, _mockCleanService.Object, - context, - new Mock<INotificationService>().Object, - new Mock<IConfiguration>().Object + _context, + _mockNotificationService.Object, + _mockConfig.Object ); + _controller.ControllerContext.HttpContext = new DefaultHttpContext(); _controller.TempData = new Mock<ITempDataDictionary>().Object; } diff --git a/test/ControllerTests/AdminController/ResolveDiscrepancy.cs b/test/ControllerTests/AdminController/ResolveDiscrepancy.cs new file mode 100644 index 0000000000000000000000000000000000000000..97786d9e1cb5d75df33d99cc38f41df36a7c5544 --- /dev/null +++ b/test/ControllerTests/AdminController/ResolveDiscrepancy.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using StatusMonitor.Web.Controllers.View; +using StatusMonitor.Shared.Extensions; +using StatusMonitor.Shared.Models.Entities; +using StatusMonitor.Shared.Services; +using Xunit; +using Microsoft.AspNetCore.Http; +using Moq; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using StatusMonitor.Shared.Models; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using StatusMonitor.Web.Services; +using System.Linq; +using StatusMonitor.Web.ViewModels; +using Microsoft.EntityFrameworkCore; + +namespace StatusMonitor.Tests.ControllerTests +{ + public partial class AdminControllerTest + { + [Fact] + public async Task ResolveDiscrepancySuccess() + { + // Arrange + var now = DateTime.UtcNow; + + await _context.Discrepancies.AddAsync( + new Discrepancy + { + Type = DiscrepancyType.GapInData, + MetricType = Metrics.CpuLoad, + MetricSource = "the-source", + DateFirstOffense = now, + Resolved = false + } + ); + await _context.SaveChangesAsync(); + + var input = new DiscrepancyResolutionViewModel + { + DateFirstOffense = now, + EnumDiscrepancyType = DiscrepancyType.GapInData, + EnumMetricType = Metrics.CpuLoad, + Source = "the-source" + }; + + // Act + var result = await _controller.ResolveDiscrepancy(input); + + // Assert + var redirectResult = Assert.IsType<RedirectToActionResult>(result); + + Assert.Equal("Index", redirectResult.ActionName); + Assert.Equal("Admin", redirectResult.ControllerName); + + Assert.True( + (await _context + .Discrepancies + .SingleAsync(d => + d.DateFirstOffense == now && + d.MetricSource == "the-source" && + d.MetricType == Metrics.CpuLoad && + d.Type == DiscrepancyType.GapInData + ) + ).Resolved + ); + Assert.InRange( + (await _context + .Discrepancies + .SingleAsync(d => + d.DateFirstOffense == now && + d.MetricSource == "the-source" && + d.MetricType == Metrics.CpuLoad && + d.Type == DiscrepancyType.GapInData + ) + ).DateResolved, + now, + now.AddMinutes(1) + ); + + _mockNotificationService + .Verify( + n => n.ScheduleNotificationAsync( + It.Is<string>(s => s.Contains("the-source")), + NotificationSeverity.Medium + ) + ); + + _mockConfig.Verify(conf => conf["ServiceManager:NotificationService:TimeZone"]); + } + + [Fact] + public async Task ResolveDiscrepancyWarning() + { + // Arrange + var now = DateTime.UtcNow; + + await _context.Discrepancies.AddAsync( + new Discrepancy + { + Type = DiscrepancyType.GapInData, + MetricType = Metrics.CpuLoad, + MetricSource = "the-source", + DateFirstOffense = now, + Resolved = true + } + ); + await _context.SaveChangesAsync(); + + var input = new DiscrepancyResolutionViewModel + { + DateFirstOffense = now, + EnumDiscrepancyType = DiscrepancyType.GapInData, + EnumMetricType = Metrics.CpuLoad, + Source = "the-source" + }; + + // Act + var result = await _controller.ResolveDiscrepancy(input); + + // Assert + var redirectResult = Assert.IsType<RedirectToActionResult>(result); + + Assert.Equal("Index", redirectResult.ActionName); + Assert.Equal("Admin", redirectResult.ControllerName); + + _mockNotificationService + .Verify( + n => n.ScheduleNotificationAsync( + It.Is<string>(s => s.Contains("the-source")), + NotificationSeverity.Medium + ), + Times.Never() + ); + } + + [Fact] + public async Task ResolveDiscrepancyNotFound() + { + // Arrange + var input = new DiscrepancyResolutionViewModel + { + DateFirstOffense = DateTime.UtcNow, + EnumDiscrepancyType = DiscrepancyType.GapInData, + EnumMetricType = Metrics.CpuLoad, + Source = "the-source" + }; + + // Act + var result = await _controller.ResolveDiscrepancy(input); + + // Assert + var notFoundResult = Assert.IsType<NotFoundObjectResult>(result); + + _mockNotificationService + .Verify( + n => n.ScheduleNotificationAsync( + It.Is<string>(s => s.Contains("the-source")), + NotificationSeverity.Medium + ), + Times.Never() + ); + } + } +}