def test_predict_few_values(self): """Test that predict() behaves well with a limited data set.""" dh = DateHelper() num_elements = [AWSForecast.MINIMUM - 1, AWSForecast.MINIMUM, AWSForecast.MINIMUM + 1] for number in num_elements: with self.subTest(num_elements=number): expected = [] for n in range(0, number): # the test data needs to include some jitter to avoid # division-by-zero in the underlying dot-product maths. expected.append( { "usage_start": dh.n_days_ago(dh.today, 10 - n).date(), "total_cost": 5 + (0.01 * n), "infrastructure_cost": 3 + (0.01 * n), "supplementary_cost": 2 + (0.01 * n), } ) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset ) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table if number < AWSForecast.MINIMUM: # forecasting isn't useful with less than the minimum number of data points. with self.assertLogs(logger="forecast.forecast", level=logging.WARNING): results = instance.predict() self.assertEqual(results, []) else: results = instance.predict() self.assertNotEqual(results, []) for result in results: for val in result.get("values", []): self.assertIsInstance(val.get("date"), date) item = val.get("cost") self.assertGreaterEqual(float(item.get("total").get("value")), 0) self.assertGreaterEqual(float(item.get("confidence_max").get("value")), 0) self.assertGreaterEqual(float(item.get("confidence_min").get("value")), 0) self.assertGreaterEqual(float(item.get("rsquared").get("value")), 0) for pval in item.get("pvalues").get("value"): self.assertGreaterEqual(float(pval), 0) # test that the results always stop at the end of the month. self.assertEqual(results[-1].get("date"), dh.this_month_end.date())
def test_predict_response_date(self): """Test that predict() returns expected date range.""" dh = DateHelper() expected = [] for n in range(0, 10): expected.append({ "usage_start": dh.n_days_ago(dh.today, 10 - n).date(), "total_cost": 5, "infrastructure_cost": 3, "supplementary_cost": 2, }) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table results = instance.predict() for item in results: self.assertIsInstance(item.get("date"), date) self.assertLessEqual(item.get("date"), dh.this_month_end.date())
def test_negative_values(self, mock_enumerate_dates, mock_run_forecast, mock_format_result): """COST-1110: ensure that the forecast response does not include negative numbers.""" mock_run_forecast.return_value = Mock( prediction=[1, 0, -1, -2, -3], confidence_lower=[2, 1, 0, -1, -2], confidence_upper=[3, 2, 1, 0, -1] ) params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.predict() self.assertIsInstance(mock_format_result.call_args[0][0], dict) for key, val_dict in mock_format_result.call_args[0][0].items(): for inner_key, inner_val in val_dict.items(): if "cost" in inner_key: self.assertGreaterEqual(inner_val[0]["total_cost"], 0) self.assertGreaterEqual(inner_val[0]["confidence_min"], 0) self.assertGreaterEqual(inner_val[0]["confidence_max"], 0)
def test_predict_flat(self): """Test that predict() returns expected values for flat costs.""" dh = DateHelper() expected = [] for n in range(0, 10): expected.append({ "usage_start": (dh.this_month_start + timedelta(days=n)).date(), "total_cost": 5 + (0.01 * n), "infrastructure_cost": 3 + (0.01 * n), "supplementary_cost": 2 + (0.01 * n), }) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table results = instance.predict() for result in results: for val in result.get("values", []): with self.subTest(values=val): self.assertIsInstance(val.get("date"), date) for item, cost, delta in [ (val.get("cost"), 5, 1), (val.get("infrastructure"), 3, 1), (val.get("supplementary"), 2, 1), ]: with self.subTest(cost=cost, delta=delta, item=item): self.assertAlmostEqual(float( item.get("total").get("value")), cost, delta=delta) self.assertAlmostEqual(float( item.get("confidence_max").get("value")), cost, delta=delta) self.assertAlmostEqual(float( item.get("confidence_min").get("value")), cost, delta=delta) self.assertGreater( float(item.get("rsquared").get("value")), 0) for pval in item.get("pvalues").get("value"): self.assertGreaterEqual(float(pval), 0)
def test_predict_flat(self): """Test that predict() returns expected values for flat costs.""" dh = DateHelper() expected = [] for n in range(0, 10): expected.append({ "usage_start": dh.n_days_ago(dh.today, 10 - n).date(), "total_cost": 5, "infrastructure_cost": 3, "supplementary_cost": 2, }) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table results = instance.predict() for result in results: for val in result.get("values", []): self.assertIsInstance(val.get("date"), date) for item, cost in [ (val.get("cost"), 5), (val.get("infrastructure"), 3), (val.get("supplementary"), 2), ]: self.assertAlmostEqual(float( item.get("total").get("value")), cost, delta=0.0001) self.assertAlmostEqual(float( item.get("confidence_max").get("value")), cost, delta=0.0001) self.assertAlmostEqual(float( item.get("confidence_min").get("value")), cost, delta=0.0001) self.assertAlmostEqual(float( item.get("rsquared").get("value")), 1, delta=0.0001) self.assertGreaterEqual( float(item.get("pvalues").get("value")), 0)
def test_results_never_outside_curren_month(self): """Test that our results stop at the end of the current month.""" dh = DateHelper() params = self.mocked_query_params("?", AWSCostForecastView) forecast = AWSForecast(params) forecast.forecast_days_required = 100 results = forecast.predict() dates = [result.get("date") for result in results] self.assertNotIn(dh.next_month_start, dates) self.assertEqual(dh.this_month_end.date(), max(dates))
def test_predict_increasing(self): """Test that predict() returns expected values for increasing costs.""" dh = DateHelper() expected = [] for n in range(0, 10): # the test data needs to include some jitter to avoid # division-by-zero in the underlying dot-product maths. expected.append({ "usage_start": dh.n_days_ago(dh.today, 10 - n).date(), "total_cost": 5 + random.random(), "infrastructure_cost": 3 + random.random(), "supplementary_cost": 2 + random.random(), }) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table results = instance.predict() for result in results: for val in result.get("values", []): self.assertIsInstance(val.get("date"), date) item = val.get("cost") self.assertGreaterEqual(float(item.get("total").get("value")), 0) self.assertGreaterEqual( float(item.get("confidence_max").get("value")), 0) self.assertGreaterEqual( float(item.get("confidence_min").get("value")), 0) self.assertGreaterEqual( float(item.get("rsquared").get("value")), 0) for pval in item.get("pvalues").get("value"): self.assertGreaterEqual(float(pval), 0)
def test_add_additional_data_points(self): """Test that we fill in data to the end of the month.""" dh = DateHelper() params = self.mocked_query_params("?", AWSCostForecastView) last_day_of_data = dh.last_month_start + timedelta(days=10) with patch("forecast.forecast.Forecast.dh") as mock_dh: mock_dh.today = dh.this_month_start mock_dh.this_month_end = dh.this_month_end mock_dh.last_month_start = dh.last_month_start mock_dh.last_month_end = last_day_of_data forecast = AWSForecast(params) results = forecast.predict() self.assertEqual(len(results), dh.this_month_end.day) for i, result in enumerate(results): self.assertEqual( result.get("date"), dh.this_month_start.date() + timedelta(days=i)) for val in result.get("values", []): cost = val.get("cost", {}).get("total", {}).get("value") self.assertNotEqual(cost, 0)
def test_predict_few_values(self): """Test that predict() behaves well with a limited data set.""" dh = DateHelper() num_elements = [1, 2, 3, 4, 5] for number in num_elements: with self.subTest(num_elements=number): expected = [] for n in range(0, number): expected.append({ "usage_start": dh.n_days_ago(dh.today, 10 - n).date(), "total_cost": 5, "infrastructure_cost": 3, "supplementary_cost": 2, }) mock_qset = MockQuerySet(expected) mocked_table = Mock() mocked_table.objects.filter.return_value.order_by.return_value.values.return_value.annotate.return_value = ( # noqa: E501 mock_qset) mocked_table.len = mock_qset.len params = self.mocked_query_params("?", AWSCostForecastView) instance = AWSForecast(params) instance.cost_summary_table = mocked_table if number == 1: # forecasting isn't possible with only 1 data point. with self.assertLogs(logger="forecast.forecast", level=logging.WARNING): results = instance.predict() self.assertEqual(results, []) else: with self.assertLogs(logger="forecast.forecast", level=logging.WARNING): results = instance.predict() for result in results: for val in result.get("values", []): self.assertIsInstance(val.get("date"), date) item = val.get("cost") self.assertGreaterEqual( float(item.get("total").get("value")), 0) self.assertGreaterEqual( float( item.get("confidence_max").get( "value")), 0) self.assertGreaterEqual( float( item.get("confidence_min").get( "value")), 0) self.assertGreaterEqual( float(item.get("rsquared").get("value")), 0) self.assertGreaterEqual( float(item.get("pvalues").get("value")), 0) # test that the results always stop at the end of the month. self.assertEqual(results[-1].get("date"), dh.this_month_end.date())