From d98dcec52600a4eab781adfb901ee63c2c43b8ba Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Tue, 24 Sep 2024 14:34:20 -0500 Subject: [PATCH] Add usage enforcement tests --- .../enforcement/usage_enforcement.py | 31 +- balance_service/tests.py | 580 +++++++++++++++++- chameleon/settings.py | 6 +- 3 files changed, 580 insertions(+), 37 deletions(-) diff --git a/balance_service/enforcement/usage_enforcement.py b/balance_service/enforcement/usage_enforcement.py index 73424d86..ee8ceeea 100755 --- a/balance_service/enforcement/usage_enforcement.py +++ b/balance_service/enforcement/usage_enforcement.py @@ -106,7 +106,7 @@ def get_lease_duration_hrs(self, lease_values): return dt_hours(end_date - start_date) - def evaluate_lease(self, context, lease_values): + def _evaluate_lease(self, context, lease_values): project_charge_code = self._get_project_charge_code(context["project_id"]) duration = self.get_lease_duration_hrs(lease_values) total_su_factor = self._total_su_factor(lease_values) @@ -150,7 +150,9 @@ def _check_usage_against_user_budget(self, user, project, new_charge): user=user, project=project, su_budget=project.default_su_budget ) user_budget.save() - left = user_budget.su_budget - su_calculators.calculate_user_total_su_usage(user, project) + left = user_budget.su_budget - su_calculators.calculate_user_total_su_usage( + user, project + ) if left < new_charge: raise exceptions.BillingError( message=( @@ -167,7 +169,7 @@ def check_usage_against_allocation(self, data): Raises a BillingError if we don't have enough available SUs. """ lease = data["lease"] - lease_eval = self.evaluate_lease(data["context"], lease) + lease_eval = self._evaluate_lease(data["context"], lease) LOG.debug(f"Evaluating new lease request: {lease} ({lease_eval})") debug_lease_reservations("New reservation", lease) @@ -189,7 +191,7 @@ def check_usage_against_allocation(self, data): role, scopes = keycloak_client.get_user_project_role_scopes( lease_eval.user.username, lease_eval.project.charge_code ) - if role == 'member': + if role == "member": self._check_usage_against_user_budget( lease_eval.user, lease_eval.project, lease_eval.amount ) @@ -200,17 +202,22 @@ def check_usage_against_allocation(self, data): # create new charges for reservation in lease["reservations"]: - new_charge = Charge( - allocation=alloc, - user=lease_eval.user, - region_name=lease_eval.region, - resource_id=TMP_RESOURCE_ID.format( + # Use tmp resource id if not given + resource_id = reservation.get( + "id", + TMP_RESOURCE_ID.format( prefix=TMP_RESOURCE_ID_PREFIX, project_id=lease["project_id"], user_id=lease["user_id"], start_date=lease["start_date"], name=lease["name"], ), + ) + new_charge = Charge( + allocation=alloc, + user=lease_eval.user, + region_name=lease_eval.region, + resource_id=resource_id, resource_type=reservation["resource_type"], start_time=self._convert_to_localtime( self._date_from_string(lease["start_date"]) @@ -230,8 +237,8 @@ def check_usage_against_allocation_update(self, data): old_lease = data["current_lease"] new_lease = data["lease"] - old_lease_eval = self.evaluate_lease(context, old_lease) - new_lease_eval = self.evaluate_lease(context, new_lease) + old_lease_eval = self._evaluate_lease(context, old_lease) + new_lease_eval = self._evaluate_lease(context, new_lease) LOG.debug( ( @@ -319,7 +326,7 @@ def stop_charging(self, data): context = data["context"] lease = data["lease"] - lease_eval = self.evaluate_lease(context, lease) + lease_eval = self._evaluate_lease(context, lease) LOG.debug(f"Stop charging for lease: {lease} ({lease_eval})") debug_lease_reservations("Ending reservation", lease) diff --git a/balance_service/tests.py b/balance_service/tests.py index 47732fc8..b99d4c6c 100644 --- a/balance_service/tests.py +++ b/balance_service/tests.py @@ -1,10 +1,16 @@ from django.test import TestCase from django.utils import timezone -from allocations.models import Charge +from allocations.models import Charge, ChargeBudget +from balance_service.enforcement import exceptions +from balance_service.enforcement.usage_enforcement import UsageEnforcer from projects.models import Project from django.contrib.auth import get_user_model from unittest.mock import patch +import logging + +LOG = logging.getLogger(__name__) + from .utils.su_calculators import ( get_used_sus, get_total_sus, @@ -14,7 +20,7 @@ ) -class SUCalculatorsTest(TestCase): +class BalanceServiceTest(TestCase): def setUp(self): # Set up data for the tests User = get_user_model() @@ -54,6 +60,7 @@ def setUp(self): balance_service_version=2, ) # Charge is 1/3 through + self.existing_charges = [] self.charge = Charge.objects.create( allocation=self.allocation, user=self.test_requestor, @@ -64,25 +71,30 @@ def setUp(self): end_time=self.now + timezone.timedelta(hours=6), hourly_cost=2.0, ) - Charge.objects.create( - allocation=self.allocation, - user=self.test_requestor, - region_name="DEV@UC", - resource_id="123", - resource_type="baremetal", - start_time=self.now + timezone.timedelta(hours=1), - end_time=self.now + timezone.timedelta(hours=2), - hourly_cost=3.0, + self.existing_charges.append(self.charge) + self.existing_charges.append( + Charge.objects.create( + allocation=self.allocation, + user=self.test_requestor, + region_name="DEV@UC", + resource_id="123", + resource_type="baremetal", + start_time=self.now + timezone.timedelta(hours=1), + end_time=self.now + timezone.timedelta(hours=2), + hourly_cost=3.0, + ) ) - Charge.objects.create( - allocation=self.allocation, - user=self.test_user_2, - region_name="DEV@UC", - resource_id="123", - resource_type="baremetal", - start_time=self.now - timezone.timedelta(hours=2), - end_time=self.now + timezone.timedelta(hours=2), - hourly_cost=5.0, + self.existing_charges.append( + Charge.objects.create( + allocation=self.allocation, + user=self.test_user_2, + region_name="DEV@UC", + resource_id="123", + resource_type="baremetal", + start_time=self.now - timezone.timedelta(hours=2), + end_time=self.now + timezone.timedelta(hours=2), + hourly_cost=5.0, + ) ) @patch("django.utils.timezone.now") # Mocking timezone.now() @@ -103,7 +115,7 @@ def test_get_active_allocation(self): self.assertIsNotNone(active_allocation) self.assertEqual(active_allocation.status, "active") - @patch("django.utils.timezone.now") # Mocking timezone.now() + @patch("django.utils.timezone.now") def test_project_balances(self, mock_now): mock_now.return_value = self.now # Test the project_balances function @@ -116,10 +128,534 @@ def test_project_balances(self, mock_now): self.assertAlmostEqual(balance["allocated"], 50) self.assertAlmostEqual(balance["encumbered"], 25.0, places=2) - @patch("django.utils.timezone.now") # Mocking timezone.now() + @patch("django.utils.timezone.now") def test_calculate_user_total_su_usage(self, mock_now): mock_now.return_value = self.now # Test calculate_user_total_su_usage function user = self.charge.user # Assuming a user is associated with the charge total_su_usage = calculate_user_total_su_usage(user, self.project) self.assertAlmostEqual(total_su_usage, 21.0, places=2) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + def test_usage_enforcer_get_remaining_balance(self, mock_ks, mock_now): + mock_now.return_value = self.now + ks_instance = mock_ks.return_value + ue = UsageEnforcer(ks_instance) + self.assertAlmostEqual(ue.get_remaining_balance(self.project.id), 9.0, places=2) + + def _lease_data( + self, + duration_td, + reservations=1, + allocations_per_res=2, + su_factor=3, + include_update=False, + update_reservations=1, + update_allocations_per_res=2, + update_su_factor=3, + update_extend=0, + ): + lease_start = self.now.strftime("%Y-%m-%d %H:%M:%S") + lease_end = (self.now + duration_td).strftime("%Y-%m-%d %H:%M:%S") + + update_start = self.now.strftime("%Y-%m-%d %H:%M:%S") + update_end = ( + self.now + duration_td + timezone.timedelta(days=update_extend) + ).strftime("%Y-%m-%d %H:%M:%S") + + if not include_update: + return { + "context": { + "user_id": "c631173e-dec0-4bb7-a0c3-f7711153c06c", + "project_id": "a0b86a98-b0d3-43cb-948e-00689182efd4", + "auth_url": "https://api.example.com:5000/v3", + "region_name": "RegionOne", + }, + "lease": { + "start_date": lease_start, + "end_date": lease_end, + "project_id": "a0b86a98-b0d3-43cb-948e-00689182efd4", + "user_id": "c631173e-dec0-4bb7-a0c3-f7711153c06c", + "name": "my_lease", + "reservations": [ + { + "resource_type": "physical:host", + "min": allocations_per_res, + "max": allocations_per_res, + "hypervisor_properties": "[]", + "resource_properties": '["==", "$availability_zone", "az1"]', + "id": str(j), + "allocations": [ + { + "id": str(i), + "hypervisor_hostname": "32af5a7a-e7a3-4883-a643-828e3f63bf54", + "extra": {"availability_zone": "az1"}, + "su_factor": su_factor, + } + for i in range(allocations_per_res) + ], + } + for j in range(reservations) + ], + }, + } + else: + return { + "context": { + "user_id": "c631173e-dec0-4bb7-a0c3-f7711153c06c", + "project_id": "a0b86a98-b0d3-43cb-948e-00689182efd4", + "auth_url": "https://api.example.com:5000/v3", + "region_name": "RegionOne", + }, + "current_lease": { + "start_date": lease_start, + "end_date": lease_end, + "project_id": "a0b86a98-b0d3-43cb-948e-00689182efd4", + "user_id": "c631173e-dec0-4bb7-a0c3-f7711153c06c", + "name": "my_lease", + "reservations": [ + { + "resource_type": "physical:host", + "min": allocations_per_res, + "max": allocations_per_res, + "hypervisor_properties": "[]", + "resource_properties": '["==", "$availability_zone", "az1"]', + "id": str(j), + "allocations": [ + { + "id": str(i), + "hypervisor_hostname": "32af5a7a-e7a3-4883-a643-828e3f63bf54", + "extra": {"availability_zone": "az1"}, + "su_factor": su_factor, + } + for i in range(allocations_per_res) + ], + } + for j in range(reservations) + ], + }, + "lease": { + "start_date": update_start, + "end_date": update_end, + "project_id": "a0b86a98-b0d3-43cb-948e-00689182efd4", + "user_id": "c631173e-dec0-4bb7-a0c3-f7711153c06c", + "name": "my_lease", + "reservations": [ + { + "resource_type": "physical:host", + "min": update_allocations_per_res, + "max": update_allocations_per_res, + "hypervisor_properties": "[]", + "resource_properties": '["==", "$availability_zone", "az1"]', + "id": str(j), + "allocations": [ + { + "id": str(i), + "hypervisor_hostname": "32af5a7a-e7a3-4883-a643-828e3f63bf54", + "extra": {"availability_zone": "az1"}, + "su_factor": update_su_factor, + } + for i in range(update_allocations_per_res) + ], + } + for j in range(update_reservations) + ], + }, + } + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + def test_usage_enforcer_get_lease_duration_hrs(self, mock_ks, mock_now): + mock_now.return_value = self.now + ks_instance = mock_ks.return_value + ue = UsageEnforcer(ks_instance) + + self.assertAlmostEqual( + ue.get_lease_duration_hrs( + self._lease_data(timezone.timedelta(hours=23, minutes=30))["lease"] + ), + 23.5, + places=2, + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_insufficient_sus( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + with self.assertRaisesRegex( + exceptions.BillingError, r"would spend 30.00 SUs.*only 9.00 left" + ): + ue.check_usage_against_allocation( + self._lease_data(timezone.timedelta(hours=5)) + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_past_expiration( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + # Add a lot of SUs for really big lease upcoming + self.allocation.su_allocated = 10000 + self.allocation.save() + with self.assertRaises(exceptions.LeasePastExpirationError): + ue.check_usage_against_allocation( + self._lease_data(timezone.timedelta(days=35)) + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_charges( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + ue.check_usage_against_allocation(self._lease_data(timezone.timedelta(hours=5))) + new_charges = [] + for c in Charge.objects.all(): + if c not in self.existing_charges: + new_charges.append(c) + self.assertEqual(len(new_charges), 1) + self.assertEqual(get_total_sus(new_charges[0]), 30) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_multiple_reservations( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + ue.check_usage_against_allocation( + self._lease_data( + timezone.timedelta(hours=5), + reservations=3, + ) + ) + new_charges = [] + for c in Charge.objects.all(): + if c not in self.existing_charges: + new_charges.append(c) + self.assertEqual(len(new_charges), 3) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_project_budget( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + kc_instance.get_user_project_role_scopes.return_value = ("member", None) + + self.project.default_su_budget = 50 + self.project.save() + # User has used 21 SUs already, 50-21 = 29 left + with self.assertRaisesRegex( + exceptions.BillingError, "60.00 SUs.*29.00 left.*budget" + ): + ue.check_usage_against_allocation( + self._lease_data( + timezone.timedelta(hours=5), + reservations=2, + ) + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_user_budget( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + kc_instance.get_user_project_role_scopes.return_value = ("member", None) + + ChargeBudget.objects.create( + user=self.test_requestor, project=self.project, su_budget=25 + ) + with self.assertRaisesRegex( + exceptions.BillingError, "60.00 SUs.*4.00 left.*budget" + ): + ue.check_usage_against_allocation( + self._lease_data( + timezone.timedelta(hours=5), + reservations=2, + ) + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_update_no_old_charges( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + with self.assertRaisesRegex(exceptions.BillingError, "Wrong number of ongoing"): + ue.check_usage_against_allocation_update( + self._lease_data( + timezone.timedelta(hours=5), include_update=True, update_extend=1 + ) + ) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_update_charges( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + lease_data = self._lease_data( + timezone.timedelta(hours=5), + reservations=3, + include_update=True, + update_extend=1, + update_reservations=3, + ) + updated_data = lease_data.pop("lease") + lease_data["lease"] = lease_data.pop("current_lease") + + # Create charges + ue.check_usage_against_allocation(lease_data) + new_charges = [] + for c in Charge.objects.all(): + if c not in self.existing_charges: + new_charges.append(c) + self.assertEqual(len(new_charges), 3) + + lease_data["current_lease"] = lease_data["lease"] + lease_data["lease"] = updated_data + ue.check_usage_against_allocation_update(lease_data) + + update_new_charges = [] + for c in Charge.objects.all(): + # Get new active charges + if c not in self.existing_charges and c.end_time > self.now: + update_new_charges.append(c) + self.assertEqual(len(new_charges), len(update_new_charges)) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_update_insufficient_sus_extend( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + # New lease uses 432 = 24*3*2*3 (time * res * allocs * su_factor) + # existing charges use 41 + self.allocation.su_allocated = 473 + self.allocation.save() + + lease_data = self._lease_data( + timezone.timedelta(days=1), + reservations=3, + include_update=True, + update_extend=1, + update_reservations=3, + ) + updated_data = lease_data.pop("lease") + lease_data["lease"] = lease_data.pop("current_lease") + + # Create charges + ue.check_usage_against_allocation(lease_data) + new_charges = [] + for c in Charge.objects.all(): + if c not in self.existing_charges: + new_charges.append(c) + self.assertEqual(len(new_charges), 3) + + lease_data["current_lease"] = lease_data["lease"] + lease_data["lease"] = updated_data + + with self.assertRaisesRegex( + exceptions.BillingError, "432.00 more SUs, only 0.00 left" + ): + ue.check_usage_against_allocation_update(lease_data) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_check_usage_against_allocation_update_insufficient_sus_add_hosts( + self, mock_kc, mock_ks, mock_now + ): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + # New lease uses 432 = 24*3*2*3 (time * res * allocs * su_factor) + # existing charges use 41 + self.allocation.su_allocated = 473 + self.allocation.save() + + lease_data = self._lease_data( + timezone.timedelta(days=1), + reservations=3, + include_update=True, + update_reservations=6, + ) + updated_data = lease_data.pop("lease") + lease_data["lease"] = lease_data.pop("current_lease") + + # Create charges + ue.check_usage_against_allocation(lease_data) + new_charges = [] + for c in Charge.objects.all(): + if c not in self.existing_charges: + new_charges.append(c) + self.assertEqual(len(new_charges), 3) + + lease_data["current_lease"] = lease_data["lease"] + lease_data["lease"] = updated_data + + with self.assertRaisesRegex( + exceptions.BillingError, "432.00 more SUs, only 0.00 left" + ): + ue.check_usage_against_allocation_update(lease_data) + + @patch("django.utils.timezone.now") + @patch("balance_service.utils.openstack.keystone.KeystoneAPI") + @patch("balance_service.enforcement.usage_enforcement.KeycloakClient") + def test_usage_enforcer_stop_charging(self, mock_kc, mock_ks, mock_now): + mock_now.return_value = self.now + + ks_instance = mock_ks.return_value + ks_instance.get_project.return_value = {"name": "TEST123"} + ks_instance.get_user.return_value = {"name": "test_requestor"} + + kc_instance = mock_kc.return_value + kc_instance.get_user_project_role_scopes.return_value = ("admin", None) + + ue = UsageEnforcer(ks_instance) + + self.allocation.su_allocated = 10000 + self.allocation.save() + + lease_data = self._lease_data( + timezone.timedelta(hours=5), + reservations=3, + ) + + # Create charges + ue.check_usage_against_allocation(lease_data) + # End charges + ue.stop_charging(lease_data) + # Ensure all charges end now, when stopped + for c in Charge.objects.all(): + if c not in self.existing_charges: + self.assertEqual(c.end_time, self.now) diff --git a/chameleon/settings.py b/chameleon/settings.py index b83c5da7..1194087f 100644 --- a/chameleon/settings.py +++ b/chameleon/settings.py @@ -9,6 +9,7 @@ """ import os import re +import sys from celery.schedules import crontab from django.utils.translation import gettext_lazy as _ @@ -244,12 +245,11 @@ }, } +TESTING = 'test' in sys.argv or 'test_coverage' in sys.argv + # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases -import sys -TESTING = 'test' in sys.argv or 'test_coverage' in sys.argv - if not TESTING and os.environ.get("DB_NAME"): # mysql connection DATABASES = {