From e991f34048c2a8b4cb9f2ebe0d17d82d3c46c6fe Mon Sep 17 00:00:00 2001
From: Dmytro Bogatov <dmytro@dbogatov.org>
Date: Tue, 8 Aug 2017 15:30:06 +0300
Subject: [PATCH] Add tests.

---
 client/ts/modules/metric-page/abstract.ts    |  29 +++++
 docker-compose.yml                           |   2 +-
 src/shared/Extensions/DateTimeExtensions.cs  |   5 +
 test/ControllerTests/HomeConroller/Metric.cs | 126 ++++++++++++++++++-
 4 files changed, 157 insertions(+), 5 deletions(-)

diff --git a/client/ts/modules/metric-page/abstract.ts b/client/ts/modules/metric-page/abstract.ts
index 6ae2115..26cdd6d 100644
--- a/client/ts/modules/metric-page/abstract.ts
+++ b/client/ts/modules/metric-page/abstract.ts
@@ -52,10 +52,38 @@ export abstract class MetricPage<T extends Metric<DataPoint>> {
 	protected detailedPlotOptions: any;
 	protected overviewPlotOptions: any;
 
+	/**
+	 * Date as a number of milliseconds of the first data point
+	 * 
+	 * @protected
+	 * @type {number}
+	 * @memberof MetricPage
+	 */
 	protected minData: number;
+	/**
+	 * Date as a number of milliseconds of the last data point
+	 * 
+	 * @protected
+	 * @type {number}
+	 * @memberof MetricPage
+	 */
 	protected maxData: number;
 
+	/**
+	 * If given, the timestamp of first data point in selected range
+	 * 
+	 * @protected
+	 * @type {Date}
+	 * @memberof MetricPage
+	 */
 	protected start: Date = null;
+	/**
+	 * If given, the timestamp of last data point in selected range
+	 * 
+	 * @protected
+	 * @type {Date}
+	 * @memberof MetricPage
+	 */
 	protected end: Date = null;
 
 	constructor(min: number, max: number) {
@@ -130,6 +158,7 @@ export abstract class MetricPage<T extends Metric<DataPoint>> {
 			// don't fire event on the overview to prevent eternal loop
 			overview.setSelection(ranges, true);
 
+			// Re-render data table to include only selected data points
 			this.renderTable(true, new Date(ranges.xaxis.from), new Date(ranges.xaxis.to));
 		}));
 
diff --git a/docker-compose.yml b/docker-compose.yml
index 4ee371a..fc642b8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -44,7 +44,7 @@ services:
       - ./appsettings.yml:/srv/appsettings.production.yml
     restart: on-failure
   postgres:
-    image: postgres:latest
+    image: postgres:9.6.3-alpine
     environment: # define connection credentials to be used in app
       - POSTGRES_DB=${POSTGRES_DB}
       - POSTGRES_USER=${POSTGRES_USER}
diff --git a/src/shared/Extensions/DateTimeExtensions.cs b/src/shared/Extensions/DateTimeExtensions.cs
index 95d166f..27970d4 100644
--- a/src/shared/Extensions/DateTimeExtensions.cs
+++ b/src/shared/Extensions/DateTimeExtensions.cs
@@ -23,6 +23,11 @@ namespace StatusMonitor.Shared.Extensions
 				: TimeZoneInfo.ConvertTime(value, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId)).ToString();
 		}
 
+		/// <summary>
+		/// Return the number of milliseconds between epoch and given date
+		/// </summary>
+		/// <param name="value">The end date of selected range</param>
+		/// <returns>The number of milliseconds between epoch and given date</returns>
 		public static long TotalMilliseconds(this DateTime value) =>
 			Convert.ToInt64((value - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds);
 		
diff --git a/test/ControllerTests/HomeConroller/Metric.cs b/test/ControllerTests/HomeConroller/Metric.cs
index 8f2a4a8..b6d2ce9 100644
--- a/test/ControllerTests/HomeConroller/Metric.cs
+++ b/test/ControllerTests/HomeConroller/Metric.cs
@@ -28,11 +28,11 @@ namespace StatusMonitor.Tests.ControllerTests
 			_mockMetricService
 				.Setup(mock => mock.GetMetricsAsync(It.IsAny<Metrics>(), It.IsAny<string>()))
 				.ReturnsAsync(
-					new List<Metric> { 
+					new List<Metric> {
 						new Metric {
-							CurrentValue = 50,							
+							CurrentValue = 50,
 							Public = true
-						} 
+						}
 					}
 				);
 
@@ -70,7 +70,7 @@ namespace StatusMonitor.Tests.ControllerTests
 			// Arrange
 			_mockMetricService
 				.Setup(mock => mock.GetMetricsAsync(It.IsAny<Metrics>(), It.IsAny<string>()))
-				.ReturnsAsync(new List<Metric> { new Metric { Public = false }});
+				.ReturnsAsync(new List<Metric> { new Metric { Public = false } });
 
 			_mockAuth
 				.Setup(auth => auth.IsAuthenticated())
@@ -94,5 +94,123 @@ namespace StatusMonitor.Tests.ControllerTests
 
 			Assert.Contains("type", (string)badRequestObjectResult.Value);
 		}
+
+		[Fact]
+		public async Task MetricStartDateRequest()
+		{
+			// Act
+			var result = await _controller.Metric(Metrics.CpuLoad.ToString(), "any-source", "invalid-date");
+
+			// Assert
+			var badRequestObjectResult = Assert.IsType<BadRequestObjectResult>(result);
+
+			Assert.Contains("start", (string)badRequestObjectResult.Value);
+		}
+
+		[Fact]
+		public async Task MetricEndDateRequest()
+		{
+			// Act
+			var result = await _controller.Metric(
+				Metrics.CpuLoad.ToString(),
+				"any-source",
+				DateTime.UtcNow.TotalMilliseconds().ToString(),
+				"invalid-date"
+			);
+
+			// Assert
+			var badRequestObjectResult = Assert.IsType<BadRequestObjectResult>(result);
+
+			Assert.Contains("end", (string)badRequestObjectResult.Value);
+		}
+
+		[Fact]
+		public async Task MetricStartAfterEndDateRequest()
+		{
+			// Act
+			var result = await _controller.Metric(
+				Metrics.CpuLoad.ToString(),
+				"any-source",
+				DateTime.UtcNow.TotalMilliseconds().ToString(),
+				DateTime.UtcNow.AddHours(-1).TotalMilliseconds().ToString()
+			);
+
+			// Assert
+			var badRequestObjectResult = Assert.IsType<BadRequestObjectResult>(result);
+
+			Assert.Contains("greater than", (string)badRequestObjectResult.Value);
+		}
+
+		[Fact]
+		public async Task MetricDatesOK()
+		{
+			// Arrange
+			_mockMetricService
+				.Setup(mock => mock.GetMetricsAsync(It.IsAny<Metrics>(), It.IsAny<string>()))
+				.ReturnsAsync(
+					new List<Metric> {
+						new Metric {
+							CurrentValue = 50,
+							Public = true
+						}
+					}
+				);
+
+			var start = DateTime.UtcNow.AddHours(-1);
+			var end = DateTime.UtcNow;
+
+			// Act
+			var result = await _controller.Metric(
+				Metrics.CpuLoad.ToString(), 
+				"existing-source",
+				start.TotalMilliseconds().ToString(),
+				end.TotalMilliseconds().ToString()
+			);
+
+			// Assert
+			var viewResult = Assert.IsType<ViewResult>(result);
+
+			var model = Assert.IsAssignableFrom<Metric>(
+				viewResult.ViewData.Model
+			);
+
+			Assert.Equal(start.TotalMilliseconds().ToString(), viewResult.ViewData["Start"]);
+			Assert.Equal(end.TotalMilliseconds().ToString(), viewResult.ViewData["End"]);
+		}
+
+		[Fact]
+		public async Task MetricDatesEndNullOK()
+		{
+			// Arrange
+			_mockMetricService
+				.Setup(mock => mock.GetMetricsAsync(It.IsAny<Metrics>(), It.IsAny<string>()))
+				.ReturnsAsync(
+					new List<Metric> {
+						new Metric {
+							CurrentValue = 50,
+							Public = true
+						}
+					}
+				);
+
+			var start = DateTime.UtcNow;
+
+			// Act
+			var result = await _controller.Metric(
+				Metrics.CpuLoad.ToString(), 
+				"existing-source",
+				start.TotalMilliseconds().ToString()
+			);
+
+			// Assert
+			var viewResult = Assert.IsType<ViewResult>(result);
+
+			var model = Assert.IsAssignableFrom<Metric>(
+				viewResult.ViewData.Model
+			);
+
+			Assert.Equal(start.TotalMilliseconds().ToString(), viewResult.ViewData["Start"]);
+			Assert.Equal(0.ToString(), viewResult.ViewData["End"]);
+		}
 	}
 }
-- 
GitLab