make state/transition name unique within a workflow 3.5
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 20 Aug 2009 19:03:24 +0200
branch3.5
changeset 2939 a613cc003ab7
parent 2938 e5cef8ff5857
child 2940 db2fb2907389
make state/transition name unique within a workflow
entities/test/unittest_wfobjs.py
schemas/workflow.py
--- a/entities/test/unittest_wfobjs.py	Thu Aug 20 18:37:33 2009 +0200
+++ b/entities/test/unittest_wfobjs.py	Thu Aug 20 19:03:24 2009 +0200
@@ -1,6 +1,41 @@
 from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb import ValidationError
 
+class WorkflowBuildingTC(EnvBasedTC):
+
+    def test_wf_construction(self):
+        wf = self.execute('INSERT Workflow X: X name "test"').get_entity(0, 0)
+        self.execute('SET WF workflow_of ET WHERE ET name "Company"')
+        foo = wf.add_state(u'foo', initial=True)
+        bar = wf.add_state(u'bar')
+        self.assertEquals(wf.state_by_name('bar').eid, bar.eid)
+        self.assertEquals(wf.state_by_name('barrr'), None)
+        baz = wf.add_transition(u'baz', (foo,), bar, ('managers',))
+        self.assertEquals(wf.transition_by_name('baz').eid, baz.eid)
+        self.assertEquals(len(baz.require_group), 1)
+        self.assertEquals(baz.require_group[0].name, 'managers')
+
+    def test_duplicated_state(self):
+        wf = self.execute('INSERT Workflow X: X name "test"').get_entity(0, 0)
+        self.execute('SET WF workflow_of ET WHERE ET name "Company"')
+        wf.add_state(u'foo', initial=True)
+        wf.add_state(u'foo')
+        ex = self.assertRaises(ValidationError, self.commit)
+        # XXX enhance message
+        self.assertEquals(ex.errors, {'state_of': 'unique constraint S name N, Y state_of O, Y name N failed'})
+
+    def test_duplicated_transition(self):
+        wf = self.execute('INSERT Workflow X: X name "test"').get_entity(0, 0)
+        self.execute('SET WF workflow_of ET WHERE ET name "Company"')
+        foo = wf.add_state(u'foo', initial=True)
+        bar = wf.add_state(u'bar')
+        wf.add_transition(u'baz', (foo,), bar, ('managers',))
+        wf.add_transition(u'baz', (bar,), foo)
+        ex = self.assertRaises(ValidationError, self.commit)
+        # XXX enhance message
+        self.assertEquals(ex.errors, {'transition_of': 'unique constraint S name N, Y transition_of O, Y name N failed'})
+
+
 class WorkflowTC(EnvBasedTC):
 
     def setup_database(self):
@@ -23,17 +58,6 @@
                           ['deactivate 1', 'activate 1', 'deactivate 2'])
         self.assertEquals(e.latest_trinfo().comment, 'deactivate 2')
 
-    # def test_wf_construction(self): # XXX update or kill me
-    #     bar = self.mh.cmd_add_state(u'bar', ('Personne', 'Email'), initial=True)
-    #     baz = self.mh.cmd_add_transition(u'baz', ('Personne', 'Email'),
-    #                                      (foo,), bar, ('managers',))
-    #     for etype in ('Personne', 'Email'):
-    #         t1 = self.mh.rqlexec('Any N WHERE T transition_of ET, ET name "%s", T name N' %
-    #                              etype)[0][0]
-    #         self.assertEquals(t1, "baz")
-    #     gn = self.mh.rqlexec('Any GN WHERE T require_group G, G name GN, T eid %s' % baz)[0][0]
-    #     self.assertEquals(gn, 'managers')
-
     def test_possible_transitions(self):
         user = self.entity('CWUser X')
         trs = list(user.possible_transitions())
--- a/schemas/workflow.py	Thu Aug 20 18:37:33 2009 +0200
+++ b/schemas/workflow.py	Thu Aug 20 19:03:24 2009 +0200
@@ -10,8 +10,9 @@
 
 from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
                             ObjectRelation, RichString, String)
-from cubicweb.schema import RQLConstraint
-from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS, HOOKS_RTYPE_PERMS
+from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
+from cubicweb.schemas import (META_ETYPE_PERMS, META_RTYPE_PERMS,
+                              HOOKS_RTYPE_PERMS)
 
 class Workflow(EntityType):
     permissions = META_ETYPE_PERMS
@@ -34,7 +35,6 @@
                                    constraints=[RQLConstraint('O state_of S')],
                                    description=_('initial state for this workflow'))
 
-# XXX ensure state/transition name is unique in a given workflow
 
 class State(EntityType):
     """used to associate simple states to an entity type and/or to define
@@ -47,13 +47,14 @@
     description = RichString(fulltextindexed=True, default_format='text/rest',
                              description=_('semantic description of this state'))
 
-    state_of = SubjectRelation('Workflow', cardinality='+*',
-                    description=_('workflow to which this state belongs'))
     # XXX should be on BaseTransition w/ AND/OR selectors when we will
     # implements #345274
     allowed_transition = SubjectRelation('BaseTransition', cardinality='**',
                                          constraints=[RQLConstraint('S state_of WF, O transition_of WF')],
                                          description=_('allowed transitions from this state'))
+    state_of = SubjectRelation('Workflow', cardinality='+*',
+                               description=_('workflow to which this state belongs'),
+                               constraints=[RQLUniqueConstraint('S name N, Y state_of O, Y name N')])
 
 
 class BaseTransition(EntityType):
@@ -75,7 +76,8 @@
                                     description=_('group in which a user should be to be '
                                                   'allowed to pass this transition'))
     transition_of = SubjectRelation('Workflow', cardinality='+*',
-                                    description=_('workflow to which this transition belongs'))
+                                    description=_('workflow to which this transition belongs'),
+                                    constraints=[RQLUniqueConstraint('S name N, Y transition_of O, Y name N')])
 
 
 class Transition(BaseTransition):