Browse Source

Prevent saving cycles in services using services

Jocelyn Delande 9 years ago
parent
commit
e175ca0c26
2 changed files with 49 additions and 3 deletions
  1. 15 0
      costs/models.py
  2. 34 3
      costs/tests/test_models.py

+ 15 - 0
costs/models.py

@@ -287,3 +287,18 @@ class ServiceUse(AbstractUse):
         Service, related_name='dependent_services',
         limit_choices_to={'reusable': True},
         validators=[validate_reusable_service])
+
+    def clean(self):
+        """ Checks for cycles in service using services
+        """
+        start_resource = self.resource
+
+        def crawl(service):
+            for use in service.dependent_services.all():
+                if use.service == start_resource:
+                    raise ValidationError(
+                        'Cycle detected in services using services')
+                else:
+                    crawl(use.service)
+
+        crawl(self.service)

+ 34 - 3
costs/tests/test_models.py

@@ -283,15 +283,13 @@ class AbstractUseTests(TestCase):
         # VPN this is the only service using electricity
         self.assertEqual(wifi_vpn_use.unit_real_share(), 10)
 
-    def test_service_using_service_edgecases(self):
+    def test_service_using_non_usable_service(self):
         serva = Service.objects.create(
             name='A', document=self.doc,
             subscriptions_count=4,
             reusable=False,
         )
 
-        # A default service is not reusable
-
         with self.assertRaises(ValidationError):
             su = ServiceUse(
                 service=self.mailbox_service,
@@ -300,3 +298,36 @@ class AbstractUseTests(TestCase):
             )
             su.full_clean()
             su.save()
+
+    def test_service_using_cyclic_service(self):
+        """ We should not save any service dependency building a cycle
+        """
+        a = Service.objects.create(
+            name='a', document=self.doc,
+            reusable=True,
+        )
+
+        b = Service.objects.create(
+            name='b', document=self.doc,
+            reusable=True,
+        )
+
+        c = Service.objects.create(
+            name='c', document=self.doc,
+            reusable=True,
+        )
+
+        def create_clean(user, used):
+            new_use = ServiceUse(service=user, resource=used, share=1)
+            new_use.full_clean()
+            new_use.save()
+
+        create_clean(a, b)
+
+        with self.assertRaises(ValidationError):
+            create_clean(b, a)
+
+        create_clean(b, c)
+
+        with self.assertRaises(ValidationError):
+            create_clean(c, a)