def test_create_storage_no_tiers_rate(self): """Test creating a non tiered storage rate.""" storage_rates = (CostModelMetricsMap.OCP_METRIC_STORAGE_GB_REQUEST_MONTH, CostModelMetricsMap.OCP_METRIC_STORAGE_GB_USAGE_MONTH) for storage_rate in storage_rates: ocp_data = { 'name': 'Test Cost Model', 'description': 'Test', 'source_type': 'OCP', 'provider_uuids': [self.provider.uuid], 'rates': [ { 'metric': {'name': storage_rate}, 'tiered_rates': [{ 'unit': 'USD', 'value': 0.22 }] } ] } with tenant_context(self.tenant): instance = None serializer = CostModelSerializer(data=ocp_data) if serializer.is_valid(raise_exception=True): instance = serializer.save() self.assertIsNotNone(instance) self.assertIsNotNone(instance.uuid)
def test_multiple_tag_values(self): """Test that tag keys can be multiple cost types.""" value_kwargs = [ { "tag_value": "value_one", "value": 0.1, "default": True }, { "tag_value": "value_two", "value": 0.2 }, ] self.basic_model["rates"][0]["tag_rates"] = [ format_tag_rate(tag_values=value_kwargs) ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.basic_model) self.assertTrue(serializer.is_valid(raise_exception=True)) serializer.save() data = serializer.data rates = data["rates"] self.assertEqual(len(rates), 1) for rate in rates: tag_rates = rate.get("tag_rates") self.assertIsNotNone(tag_rates) for tag_rate in tag_rates: tag_values = tag_rate["tag_values"] self.assertEqual(len(tag_values), 2)
def test_tiered_rate_with_overlaps(self): """Test creating a tiered rate with a overlaps between the tiers.""" self.ocp_data['rates'][0]['tiered_rates'] = [ { 'unit': 'USD', 'value': 0.22, 'usage': { 'usage_start': None, 'usage_end': 10.0 } }, { 'unit': 'USD', 'value': 0.26, 'usage': { 'usage_start': 5.0, 'usage_end': 20.0 } }, { 'unit': 'USD', 'value': 0.26, 'usage': { 'usage_start': 20.0, 'usage_end': None } } ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_invalid_currency(self): """Test failure while handling invalid cost_type.""" currency = "invalid" serializer = CostModelSerializer(data=self.ocp_data, context=self.request_context) with self.assertRaises(serializers.ValidationError): serializer.validate_currency(currency)
def test_create_cpu_core_per_hour_tiered_rate(self): """Test creating a cpu_core_per_hour rate.""" self.ocp_data['rates'][0]['tiered_rates'] = [ { 'unit': 'USD', 'value': 0.22, 'usage': { 'usage_start': None, 'usage_end': 10.0 } }, { 'unit': 'USD', 'value': 0.26, 'usage': { 'usage_start': 10.0, 'usage_end': None } } ] with tenant_context(self.tenant): instance = None serializer = CostModelSerializer(data=self.ocp_data) if serializer.is_valid(raise_exception=True): instance = serializer.save() self.assertIsNotNone(instance) self.assertIsNotNone(instance.uuid)
def test_tiered_rate_null_start_end(self): """Test creating a rate with out a start and end.""" self.ocp_data['rates'][0]['tiered_rates'] = [ { 'unit': 'USD', 'value': 0.22, 'usage': { 'usage_start': 0.0, 'usage_end': 7.0 } }, { 'unit': 'USD', 'value': 0.26, 'usage': { 'usage_start': 10.0, 'usage_end': 20.0 } } ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_create_storage_tiered_rate(self): """Test creating a storage tiered rate.""" storage_rates = ( metric_constants.OCP_METRIC_STORAGE_GB_REQUEST_MONTH, metric_constants.OCP_METRIC_STORAGE_GB_USAGE_MONTH, ) for storage_rate in storage_rates: ocp_data = { "name": "Test Cost Model", "description": "Test", "source_type": Provider.PROVIDER_OCP, "providers": [{"uuid": self.provider.uuid, "name": self.provider.name}], "rates": [ { "metric": {"name": storage_rate}, "tiered_rates": [ {"unit": "USD", "value": 0.22, "usage": {"usage_start": None, "usage_end": 10.0}}, {"unit": "USD", "value": 0.26, "usage": {"usage_start": 10.0, "usage_end": None}}, ], } ], } with tenant_context(self.tenant): instance = None serializer = CostModelSerializer(data=ocp_data) if serializer.is_valid(raise_exception=True): instance = serializer.save() self.assertIsNotNone(instance) self.assertIsNotNone(instance.uuid)
def currency_filter(self, qs, name, values): """Filter currency if a valid currency is passed in""" serializer = CostModelSerializer(qs) if serializer.validate_currency(values[0]): lookup = "__".join([name, "iexact"]) queries = [Q(**{lookup: val}) for val in values] return qs.filter(reduce(and_, queries))
def test_tiered_rate_null_start_end(self): """Test creating a rate with out a start and end.""" self.ocp_data["rates"][0]["tiered_rates"] = [ { "unit": "USD", "value": 0.22, "usage": { "usage_start": 0.0, "usage_end": 7.0 } }, { "unit": "USD", "value": 0.26, "usage": { "usage_start": 10.0, "usage_end": 20.0 } }, ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_tag_key_can_be_multiple_cost_types(self): """Test that tag keys can be multiple cost types.""" value_kwargs = [{"value": 0.1, "default": True, "usage_start": 1, "usage_end": 10}] tag_rates_list = [] cost_types = ["Infrastructure", "Supplementary"] for cost_type in cost_types: rate = {"metric": {"name": metric_constants.OCP_METRIC_CPU_CORE_USAGE_HOUR}, "cost_type": cost_type} rate["tag_rates"] = format_tag_rate(tag_values=value_kwargs) tag_rates_list.append(rate) self.basic_model["rates"] = tag_rates_list with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.basic_model) self.assertTrue(serializer.is_valid(raise_exception=True)) serializer.save() data = serializer.data rates = data.get("rates", []) self.assertEqual(len(rates), 2) for rate in rates: tag_rate = rate.get("tag_rates") self.assertIsNotNone(tag_rate) # Check cost types result_cost_type = rate["cost_type"] self.assertIn(result_cost_type, cost_types) cost_types.remove(result_cost_type) # Check that to_representation is working tag_value = tag_rate["tag_values"][0] decimals = [tag_value["value"], tag_value["usage"]["usage_start"], tag_value["usage"]["usage_end"]] for expected_decimal in decimals: self.assertIsInstance(expected_decimal, Decimal)
def test_tiered_rate_with_overlaps(self): """Test creating a tiered rate with a overlaps between the tiers.""" self.ocp_data["rates"][0]["tiered_rates"] = [ { "unit": "USD", "value": 0.22, "usage": { "usage_start": None, "usage_end": 10.0 } }, { "unit": "USD", "value": 0.26, "usage": { "usage_start": 5.0, "usage_end": 20.0 } }, { "unit": "USD", "value": 0.26, "usage": { "usage_start": 20.0, "usage_end": None } }, ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_rate_cost_type_valid(self): """Test that a valid cost type is accepted.""" self.ocp_data["rates"][0]["tiered_rates"] = [ { "unit": "USD", "value": 0.22, "usage": {"usage_start": None, "usage_end": None}, "cost_type": "Infrastructure", } ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) if serializer.is_valid(raise_exception=True): serializer.save() self.ocp_data["rates"][0]["tiered_rates"] = [ { "unit": "USD", "value": 0.22, "usage": {"usage_start": None, "usage_end": None}, "cost_type": "Supplementary", } ] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) if serializer.is_valid(raise_exception=True): serializer.save()
def test_create_cpu_core_per_hour_tiered_rate(self): """Test creating a cpu_core_per_hour rate.""" self.ocp_data["rates"][0]["tiered_rates"] = [ { "unit": "USD", "value": 0.22, "usage": { "usage_start": None, "usage_end": 10.0 } }, { "unit": "USD", "value": 0.26, "usage": { "usage_start": 10.0, "usage_end": None } }, ] with tenant_context(self.tenant): instance = None serializer = CostModelSerializer(data=self.ocp_data) if serializer.is_valid(raise_exception=True): instance = serializer.save() self.assertIsNotNone(instance) self.assertIsNotNone(instance.uuid)
def test_validate_source_uuid_error(self): """Test validate source uuid error.""" tag_value = {"tag_value": "key_one", "value": 0.2} self.basic_model["rates"][0]["tag_rates"] = format_tag_rate(tag_values=[tag_value]) with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.basic_model) with self.assertRaises(serializers.ValidationError): serializer.validate_source_uuids([uuid4()])
def test_error_on_invalid_metric(self): """Test error on an invalid metric rate.""" self.ocp_data.get("rates", [])[0]["metric"]["name"] = "invalid_metric" with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_on_invalid_provider(self): """Test error with an invalid provider id.""" self.ocp_data.update({'provider_uuids': ['1dd7204c-72c4-4ec4-95bc-d5c447688b27']}) with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_get_metric_display_data_openshift(self): """Test the display data helper function for OpenShift metrics.""" serializer = CostModelSerializer(data=None) for metric_choice in metric_constants.METRIC_CHOICES: response = serializer._get_metric_display_data(Provider.PROVIDER_OCP, metric_choice[0]) self.assertIsNotNone(response.get("label_measurement_unit")) self.assertIsNotNone(response.get("label_measurement")) self.assertIsNotNone(response.get("label_metric"))
def test_error_on_nonOCP_source_type_with_markup_and_rates(self): """Test error when non OCP source is added with markup and rates.""" self.ocp_data["source_type"] = Provider.PROVIDER_AWS with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_source_type_with_markup(self): """Test that non-existent source type is invalid.""" self.ocp_data["source_type"] = "invalid-source" with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_on_negative_rate(self): """Test error when trying to create an negative rate input.""" self.ocp_data['rates'][0]['tiered_rates'][0]['value'] = float(round(Decimal(random.random()), 6) * -1) with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_on_rate_type(self): """Test error when trying to create an invalid rate input.""" self.ocp_data["rates"][0].pop("tiered_rates") self.ocp_data["rates"][0]["bad_rates"] = [] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_on_usage_bad_upper_bound(self): """Test error on a usage_end that does not cover lower bound.""" self.ocp_data["rates"][0]["tiered_rates"][0]["usage"] = {"usage_start": None, "usage_end": 5} with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_on_source_type_without_markup(self): """Test error when non OCP source is added without markup.""" self.ocp_data['source_type'] = 'AWS' self.ocp_data['markup'] = {} with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_no_rate(self): """Test error when trying to create an empty rate.""" self.ocp_data["rates"][0]["tiered_rates"] = [] with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_get_metric_display_data_openshift(self): """Test the display data helper function for OpenShift metrics.""" serializer = CostModelSerializer(data=None) for metric_choice in CostModelMetricsMap.METRIC_CHOICES: response = serializer._get_metric_display_data('OCP', metric_choice[0]) self.assertIsNotNone(response.label_measurement_unit) self.assertIsNotNone(response.label_measurement) self.assertIsNotNone(response.label_metric)
def test_error_tier_usage_end_less_than(self): """Test error when trying to create a tiered usage_end less than usage_start.""" self.ocp_data["rates"][0]["tiered_rates"][0]["usage"] = {"usage_start": 10.0, "usage_end": 3.0} with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_rate_cost_type_invalid(self): """Test that an invalid cost type is rejected.""" self.ocp_data["rates"][0]["cost_type"] = "Infrastructurez" with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): if serializer.is_valid(raise_exception=True): serializer.save()
def test_error_bad_distribution_choice(self): """Test that source successfully fails if bad distribution type.""" bad_choice_list = ["bad1", "bad2", "bad3"] for bad_input in bad_choice_list: self.ocp_data["distribution"] = bad_input self.assertEqual(self.ocp_data["distribution"], bad_input) with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.ocp_data) with self.assertRaises(serializers.ValidationError): serializer.validate_distribution(bad_input)
def test_error_on_empty_list_for_tag_values(self): """Test that tag_values can not be an empty list.""" self.basic_model["rates"][0]["tag_rates"] = format_tag_rate(tag_values=[]) with tenant_context(self.tenant): serializer = CostModelSerializer(data=self.basic_model) with self.assertRaises(serializers.ValidationError): self.assertFalse(serializer.is_valid(raise_exception=True)) result_err_msg = serializer.errors["rates"][0]["tag_values"][0] expected_err_msg = "A tag_values can not be an empty list." self.assertEqual(result_err_msg, expected_err_msg)
def test_valid_data(self): """Test rate and markup for valid entries.""" with tenant_context(self.tenant): instance = None serializer = CostModelSerializer(data=self.ocp_data) if serializer.is_valid(raise_exception=True): instance = serializer.save() self.assertIn(instance.source_type, SOURCE_TYPE_MAP.keys()) self.assertIsNotNone(instance.markup) self.assertIsNotNone(instance.rates)