From f113f221a12e5c7969f5b73cbdaaa206485143da Mon Sep 17 00:00:00 2001
From: Vincent Pelletier <vincent@nexedi.com>
Date: Mon, 24 Dec 2007 19:46:10 +0000
Subject: [PATCH] Fix the 2 previously added tests, they were all wrong: test
 what happen when activity generates the error. Add one more test for a bug
 found while coding above fix: object path is not a tuple when the path
 (string) is given to Message instance. Add 2 more tests which test what was
 intended for the 2 first tests: test what happen when CMFActivity generates
 the error.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@18501 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/CMFActivity/tests/testCMFActivity.py | 218 ++++++++++++++++---
 1 file changed, 189 insertions(+), 29 deletions(-)

diff --git a/product/CMFActivity/tests/testCMFActivity.py b/product/CMFActivity/tests/testCMFActivity.py
index 62db037455..c0728bb1eb 100644
--- a/product/CMFActivity/tests/testCMFActivity.py
+++ b/product/CMFActivity/tests/testCMFActivity.py
@@ -40,6 +40,8 @@ from AccessControl.SecurityManagement import newSecurityManager
 from zLOG import LOG
 from ZODB.POSException import ConflictError
 from DateTime import DateTime
+import cPickle as pickle
+from Products.CMFActivity.ActivityTool import Message
 
 try:
   from transaction import get as get_transaction
@@ -1927,60 +1929,218 @@ class TestCMFActivity(ERP5TypeTestCase):
     message_list = activity_tool.getMessageList()
     self.assertEquals(len(message_list), 2)
 
-  def activityModificationsViaCMFActivityConnectionRolledBackOnError(self, activity):
+  def test_83_ActivityModificationsViaCMFActivityConnectionRolledBackOnErrorSQLDict(self, quiet=0, run=run_all_test):
+    """
+      When an activity modifies tables through CMFActivity SQL connection and
+      raises, check that its changes are correctly rolled back.
+    """
+    if not run: return
+    if not quiet:
+      message = '\nCheck activity modifications via CMFActivity connection are rolled back on error (SQLDict)'
+      ZopeTestCase._print(message)
+      LOG('Testing... ',0,message)
+    get_transaction().commit()
+    self.tic()
     activity_tool = self.getActivityTool()
-    def modifySQLAndFail(self, connection_id):
-      # Add a dumy activity which will not be executed
-      # Modified table does not matter
-      self.SQLDict_writeMessageList(
-        uid_list=[0], # This uid is never automaticaly assigned (starts at 1)
-        date_list=[DateTime().Date()],
-        path_list=['dummy_activity'],
-        method_id_list=['dummy_activity'],
-        message_list=['dummy_message'],
-        priority_list=[1],
-        processing_node_list=[-4],
-        group_method_id_list=[''],
-        tag_list=[''],
-        order_validation_text_list=['']
-        )
-      # Fail
-      raise ValueError, 'This method always fail'
+    def modifySQLAndFail(self, object_list, **kw):
+      # Only create the dummy activity if none is present: we would just
+      # generate missleading errors (duplicate uid).
+      if activity_tool.countMessage(method_id='dummy_activity') == 0:
+        # Add a dumy activity which will not be executed
+        # Modified table does not matter
+        method_id = 'dummy_activity'
+        path = '/'.join(self.getPhysicalPath())
+        message = Message(self, None, {}, method_id, (), {})
+        pickled_message = pickle.dumps(message)
+        self.SQLDict_writeMessageList(
+          uid_list=[0], # This uid is never automaticaly assigned (starts at 1)
+          date_list=[DateTime().Date()],
+          path_list=[path],
+          method_id_list=[method_id],
+          message_list=[pickled_message],
+          priority_list=[1],
+          processing_node_list=[-2],
+          group_method_id_list=[''],
+          tag_list=[''],
+          order_validation_text_list=['']
+          )
+      if len(object_list) == 2:
+        # Remove one entry from object list: this is understood by caller as a
+        # success for this entry.
+        object_list.pop()
     Organisation.modifySQLAndFail = modifySQLAndFail
+    def dummy(self):
+      pass
+    Organisation.dummy = dummy
     obj = self.getPortal().organisation_module.newContent(portal_type='Organisation')
-    obj.activate(activity=activity).modifySQLAndFail()
+    group_method_id = '%s/modifySQLAndFail' % (obj.getPath(), )
+    obj.activate(activity='SQLDict', group_method_id=group_method_id).dummy()
+    obj2 = self.getPortal().organisation_module.newContent(portal_type='Organisation')
+    obj2.activate(activity='SQLDict', group_method_id=group_method_id).dummy()
     get_transaction().commit()
     self.flushAllActivities(silent=1, loop_size=100)
-    self.assertEquals(activity_tool.countMessage(path='dummy_activity'), 0)
-  
+    self.assertEquals(activity_tool.countMessage(method_id='dummy_activity'), 0)
 
-  def test_83_ActivityModificationsViaCMFActivityConnectionRolledBackOnErrorSQLDict(self, quiet=0, run=run_all_test):
+  def test_84_ActivityModificationsViaCMFActivityConnectionRolledBackOnErrorSQLQueue(self, quiet=0, run=run_all_test):
     """
       When an activity modifies tables through CMFActivity SQL connection and
       raises, check that its changes are correctly rolled back.
     """
     if not run: return
     if not quiet:
-      message = '\nCheck activity modifications via CMFActivity connection are rolled back on error (SQLDict)'
+      message = '\nCheck activity modifications via CMFActivity connection are rolled back on error (SQLQueue)'
       ZopeTestCase._print(message)
       LOG('Testing... ',0,message)
     get_transaction().commit()
     self.tic()
-    self.activityModificationsViaCMFActivityConnectionRolledBackOnError('SQLDict')
+    activity_tool = self.getActivityTool()
+    def modifySQLAndFail(self):
+      # Only create the dummy activity if none is present: we would just
+      # generate missleading errors (duplicate uid).
+      if activity_tool.countMessage(method_id='dummy_activity') == 0:
+        # Add a dumy activity which will not be executed
+        # Modified table does not matter
+        method_id = 'dummy_activity'
+        path = '/'.join(self.getPhysicalPath())
+        message = Message(self, None, {}, method_id, (), {})
+        pickled_message = pickle.dumps(message)
+        self.SQLDict_writeMessageList(
+          uid_list=[0], # This uid is never automaticaly assigned (starts at 1)
+          date_list=[DateTime().Date()],
+          path_list=[path],
+          method_id_list=[method_id],
+          message_list=[pickled_message],
+          priority_list=[1],
+          processing_node_list=[-2],
+          group_method_id_list=[''],
+          tag_list=[''],
+          order_validation_text_list=['']
+          )
+      # Fail
+      raise ValueError, 'This method always fail'
+    Organisation.modifySQLAndFail = modifySQLAndFail
+    obj = self.getPortal().organisation_module.newContent(portal_type='Organisation')
+    obj.activate(activity='SQLQueue').modifySQLAndFail()
+    get_transaction().commit()
+    self.flushAllActivities(silent=1, loop_size=100)
+    self.assertEquals(activity_tool.countMessage(method_id='dummy_activity'), 0)
 
-  def test_84_ActivityModificationsViaCMFActivityConnectionRolledBackOnErrorSQLQeue(self, quiet=0, run=run_all_test):
+  def test_85_MessagePathMustBeATuple(self, quiet=0, run=run_all_test):
     """
-      When an activity modifies tables through CMFActivity SQL connection and
-      raises, check that its changes are correctly rolled back.
+      Message property 'object_path' must be a tuple, whatever it is generated from.
+      Possible path sources are:
+       - bare string
+       - object
     """
     if not run: return
     if not quiet:
-      message = '\nCheck activity modifications via CMFActivity connection are rolled back on error (SQLQueue)'
+      message = '\nCheck that message property \'object_path\' is a tuple, whatever it is generated from.'
+      ZopeTestCase._print(message)
+      LOG('Testing... ',0,message)
+    def check(value):
+      message = Message(value, None, {}, 'dummy', (), {})
+      self.assertTrue(isinstance(message.object_path, tuple))
+    # Bare string
+    check('/foo/bar')
+    # Object
+    check(self.getPortalObject().person_module)
+
+  def test_86_ActivityToolInvokeGroupFailureDoesNotCommitCMFActivitySQLConnectionSQLDict(self, quiet=0, run=run_all_test):
+    """
+      Check that CMFActivity SQL connection is rollback if activity_tool.invokeGroup raises.
+    """
+    if not run: return
+    if not quiet:
+      message = '\nCheck that activity modifications via CMFActivity connection are rolled back on ActivityTool error (SQLDict)'
       ZopeTestCase._print(message)
       LOG('Testing... ',0,message)
     get_transaction().commit()
     self.tic()
-    self.activityModificationsViaCMFActivityConnectionRolledBackOnError('SQLQueue') 
+    activity_tool = self.getActivityTool()
+    def modifySQLAndFail(self, *arg, **kw):
+      # Only create the dummy activity if none is present: we would just
+      # generate missleading errors (duplicate uid).
+      if activity_tool.countMessage(method_id='dummy_activity') == 0:
+        # Add a dumy activity which will not be executed
+        # Modified table does not matter
+        method_id = 'dummy_activity'
+        path = '/'.join(self.getPhysicalPath())
+        message = Message(self, None, {}, method_id, (), {})
+        pickled_message = pickle.dumps(message)
+        self.SQLDict_writeMessageList(
+          uid_list=[0], # This uid is never automaticaly assigned (starts at 1)
+          date_list=[DateTime().Date()],
+          path_list=[path],
+          method_id_list=[method_id],
+          message_list=[pickled_message],
+          priority_list=[1],
+          processing_node_list=[-2],
+          group_method_id_list=[''],
+          tag_list=[''],
+          order_validation_text_list=['']
+          )
+      # Fail
+      raise ValueError, 'This method always fail'
+    activity_tool.__class__.invoke = modifySQLAndFail
+    activity_tool.__class__.invokeGroup = modifySQLAndFail
+    def dummy(self):
+      pass
+    Organisation.dummy = dummy
+    obj = self.getPortal().organisation_module.newContent(portal_type='Organisation')
+    group_method_id = '%s/dummy' % (obj.getPath(), )
+    obj.activate(activity='SQLDict', group_method_id=group_method_id).dummy()
+    obj2 = self.getPortal().organisation_module.newContent(portal_type='Organisation')
+    obj2.activate(activity='SQLDict', group_method_id=group_method_id).dummy()
+    get_transaction().commit()
+    self.flushAllActivities(silent=1, loop_size=100)
+    self.assertEquals(activity_tool.countMessage(method_id='dummy_activity'), 0)
+  
+  def test_87_ActivityToolInvokeFailureDoesNotCommitCMFActivitySQLConnectionSQLQueue(self, quiet=0, run=run_all_test):
+    """
+      Check that CMFActivity SQL connection is rollback if activity_tool.invoke raises.
+    """
+    if not run: return
+    if not quiet:
+      message = '\nCheck that activity modifications via CMFActivity connection are rolled back on ActivityTool error (SQLQueue)'
+      ZopeTestCase._print(message)
+      LOG('Testing... ',0,message)
+    get_transaction().commit()
+    self.tic()
+    activity_tool = self.getActivityTool()
+    def modifySQLAndFail(self, *args, **kw):
+      # Only create the dummy activity if none is present: we would just
+      # generate missleading errors (duplicate uid).
+      if activity_tool.countMessage(method_id='dummy_activity') == 0:
+        # Add a dumy activity which will not be executed
+        # Modified table does not matter
+        method_id = 'dummy_activity'
+        path = '/'.join(self.getPhysicalPath())
+        message = Message(self, None, {}, method_id, (), {})
+        pickled_message = pickle.dumps(message)
+        self.SQLDict_writeMessageList(
+          uid_list=[0], # This uid is never automaticaly assigned (starts at 1)
+          date_list=[DateTime().Date()],
+          path_list=[path],
+          method_id_list=[method_id],
+          message_list=[pickled_message],
+          priority_list=[1],
+          processing_node_list=[-2],
+          group_method_id_list=[''],
+          tag_list=[''],
+          order_validation_text_list=['']
+          )
+      # Fail
+      raise ValueError, 'This method always fail'
+    activity_tool.__class__.invoke = modifySQLAndFail
+    activity_tool.__class__.invokeGroup = modifySQLAndFail
+    def dummy(self):
+      pass
+    Organisation.dummy = dummy
+    obj = self.getPortal().organisation_module.newContent(portal_type='Organisation')
+    obj.activate(activity='SQLQueue').dummy()
+    get_transaction().commit()
+    self.flushAllActivities(silent=1, loop_size=100)
+    self.assertEquals(activity_tool.countMessage(method_id='dummy_activity'), 0)
 
 def test_suite():
   suite = unittest.TestSuite()
-- 
2.30.9