Commit 4db74aeb authored by Sebastien Robin's avatar Sebastien Robin

- finished unit test for synchronization :

  - adding workflows
  - update, delete and add local roles
- updated the code in order to make this tests working fine


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@392 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent d80a5e97
...@@ -125,7 +125,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -125,7 +125,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# In the case where this new node is a object to add # In the case where this new node is a object to add
LOG('addNode',0,'object.id: %s' % object.getId()) LOG('addNode',0,'object.id: %s' % object.getId())
LOG('addNode',0,'xml.nodeName: %s' % xml.nodeName) LOG('addNode',0,'xml.nodeName: %s' % xml.nodeName)
LOG('addNode',0,'isSubObjectAdd: %i' % self.getSubObjectDepth(xml)) LOG('addNode',0,'getSubObjectDepth: %i' % self.getSubObjectDepth(xml))
LOG('addNode',0,'isHistoryAdd: %i' % self.isHistoryAdd(xml)) LOG('addNode',0,'isHistoryAdd: %i' % self.isHistoryAdd(xml))
if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0: if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0:
if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed
...@@ -209,21 +209,24 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -209,21 +209,24 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#for action in self.getWorkflowActionFromXml(xml): #for action in self.getWorkflowActionFromXml(xml):
status = self.getStatusFromXml(xml) status = self.getStatusFromXml(xml)
LOG('addNode, status:',0,status) LOG('addNode, status:',0,status)
wf_conflict_list = self.isWorkflowActionAddable(object=object, add_action = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool, status=status,wf_tool=wf_tool,
xml=xml) wf_id=wf_id,xml=xml)
LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list) #LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list)
if wf_conflict_list==[] or force and not simulate: LOG('addNode, workflow_history add_action:',0,add_action)
if add_action and not simulate:
LOG('addNode, setting status:',0,'ok') LOG('addNode, setting status:',0,'ok')
wf_tool.setStatusOf(wf_id,object,status) wf_tool.setStatusOf(wf_id,object,status)
else: #else:
conflict_list += wf_conflict_list # conflict_list += wf_conflict_list
elif xml.nodeName in self.local_role_list and not simulate: elif xml.nodeName in self.local_role_list and not simulate:
# We want to add a local role # We want to add a local role
roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens')
user = self.getAttribute(xml,'id')
roles = list(roles) # Needed for CPS, or we have a CPS error roles = list(roles) # Needed for CPS, or we have a CPS error
user = roles[0] LOG('local_role: ',0,'user: %s roles: %s' % (repr(user),repr(roles)))
roles = roles[1:] #user = roles[0]
#roles = roles[1:]
object.manage_setLocalRoles(user,roles) object.manage_setLocalRoles(user,roles)
else: else:
conflict_list += self.updateNode(xml=xml,object=object, force=force, conflict_list += self.updateNode(xml=xml,object=object, force=force,
...@@ -236,9 +239,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -236,9 +239,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
A node is deleted A node is deleted
""" """
# In the case where this new node is a object to delete # In the case where we have to delete an object
LOG('ERP5Conduit',0,'deleteNode') LOG('ERP5Conduit',0,'deleteNode')
LOG('ERP5Conduit',0,'deleteNode, object.id: %s' % object.getId()) LOG('ERP5Conduit',0,'deleteNode, object.id: %s' % object.getId())
LOG('ERP5Conduit',0,'deleteNode, object path: %s' % repr(object.getPhysicalPath()))
conflict_list = [] conflict_list = []
if xml is not None: if xml is not None:
xml = self.convertToXml(xml) xml = self.convertToXml(xml)
...@@ -248,6 +252,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -248,6 +252,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
object_id = self.getAttribute(xml,'id') object_id = self.getAttribute(xml,'id')
elif self.getSubObjectDepth(xml)==1: elif self.getSubObjectDepth(xml)==1:
object_id = self.getSubObjectId(xml) object_id = self.getSubObjectId(xml)
#LOG('ERP5Conduit',0,'deleteNode, SubObjectDepth: %i' % self.getSubObjectDepth(xml))
elif self.getSubObjectDepth(xml)==2: elif self.getSubObjectDepth(xml)==2:
# we have to call delete node on a subsubobject # we have to call delete node on a subsubobject
sub_object_id = self.getSubObjectId(xml) sub_object_id = self.getSubObjectId(xml)
...@@ -256,14 +261,24 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -256,14 +261,24 @@ class ERP5Conduit(XMLSyncUtilsMixin):
sub_xml = self.getSubObjectXupdate(xml) sub_xml = self.getSubObjectXupdate(xml)
conflict_list += self.deleteNode(xml=sub_xml,object=sub_object, conflict_list += self.deleteNode(xml=sub_xml,object=sub_object,
force=force, simulate=simulate, **kw) force=force, simulate=simulate, **kw)
except KeyError: except (KeyError, AttributeError):
LOG('ERP5Conduit',0,'deleteNode, Unable to delete SubObject: %s' % str(sub_object_id))
pass pass
else: # We do have an object_id if object_id is not None: # We do have an object_id
try: try:
object._delObject(object_id) object._delObject(object_id)
except (AttributeError, KeyError): except (AttributeError, KeyError):
LOG('ERP5Conduit',0,'deleteNode, Unable to delete: %s' % str(object_id)) LOG('ERP5Conduit',0,'deleteNode, Unable to delete: %s' % str(object_id))
pass pass
# In the case where we have to delete an user role
# If we are still there, this means the delete is for this node
elif xml.nodeName in self.XUPDATE_DEL:
xml = self.getElementFromXupdate(xml)
if xml.nodeName in self.local_role_list and not simulate:
# We want to del a local role
user = self.getAttribute(xml,'id')
LOG('local_role: ',0,'user: %s' % repr(user))
object.manage_delLocalRoles([user])
return conflict_list return conflict_list
security.declareProtected(Permissions.ModifyPortalContent, 'updateNode') security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
...@@ -382,12 +397,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -382,12 +397,14 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list += self.addNode(xml=subnode,object=object,force=force, conflict_list += self.addNode(xml=subnode,object=object,force=force,
simulate=simulate,**kw) simulate=simulate,**kw)
elif keyword == self.local_role_tag and not simulate: elif keyword == self.local_role_tag and not simulate:
# This is the case where we have to call addNode # This is the case where we have to update Roles
LOG('updateNode',0,'we will add a local role') LOG('updateNode',0,'we will add a local role')
roles = self.convertXmlValue(data,data_type='tokens') #user = self.getSubObjectId(xml)
user = roles[0] #roles = self.convertXmlValue(data,data_type='tokens')
roles = roles[1:] #object.manage_setLocalRoles(user,roles)
object.manage_setLocalRoles(user,roles) xml = self.getElementFromXupdate(xml)
conflict_list += self.addNode(xml=xml,object=object,force=force,
simulate=simulate,**kw)
elif self.isSubObjectModification(xml): elif self.isSubObjectModification(xml):
# We should find the object corresponding to # We should find the object corresponding to
# this update, so we have to look in the previous_xml # this update, so we have to look in the previous_xml
...@@ -537,7 +554,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -537,7 +554,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
Return a string wich is the selection for the subobject Return a string wich is the selection for the subobject
ex: for "/object[@id='161']/object[@id='default_address']/street_address" ex: for "/object[@id='161']/object[@id='default_address']/street_address"
if returns "/object[@id='default_address']/street_address" it returns "/object[@id='default_address']/street_address"
""" """
if re.search(self.object_exp,select) is not None: if re.search(self.object_exp,select) is not None:
s = '/' s = '/'
...@@ -594,17 +611,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -594,17 +611,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return subnode return subnode
return None return None
# def getObjectId(self, xml):
# """
# Retrieve the id
# # XXX Deprecated, we should use instead, getParameter(xml,'id') XXX
# """
# for attribute in self.getAttributeNodeList(xml):
# if attribute.nodeName == 'id':
# data = attribute.childNodes[0].data
# return self.convertXmlValue(data)
# return None
security.declareProtected(Permissions.AccessContentsInformation,'getAttribute') security.declareProtected(Permissions.AccessContentsInformation,'getAttribute')
def getAttribute(self, xml, param): def getAttribute(self, xml, param):
""" """
...@@ -801,6 +807,36 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -801,6 +807,36 @@ class ERP5Conduit(XMLSyncUtilsMixin):
result += xml_string[maxi:xml_string.find('</xupdate:element>')] result += xml_string[maxi:xml_string.find('</xupdate:element>')]
result += '</' + xml.attributes[0].nodeValue + '>' result += '</' + xml.attributes[0].nodeValue + '>'
return self.convertToXml(result) return self.convertToXml(result)
if xml.nodeName in (self.XUPDATE_UPDATE+self.XUPDATE_DEL):
result = u'<'
for subnode in self.getAttributeNodeList(xml):
if subnode.nodeName == 'select':
attribute = subnode.nodeValue
s = '[@id='
s_place = attribute.find(s)
select_id = None
if (s_place > 0):
select_id = attribute[s_place+len(s):]
select_id = select_id[:select_id.find("'",1)+1]
else:
s_place = len(attribute)
property = attribute[:s_place]
if property.find('/')==0:
property = property[1:]
result += property
if select_id is not None:
result += ' id=%s' % select_id
result += '>'
# Then dumps the xml and remove what we does'nt want
xml_string = StringIO()
PrettyPrint(xml,xml_string)
xml_string = xml_string.getvalue()
xml_string = unicode(xml_string,encoding='utf-8')
maxi = xml_string.find('>')+1
result += xml_string[maxi:xml_string.find('</%s>' % xml.nodeName)]
result += '</' + property + '>'
LOG('getElementFromXupdate, result:',0,repr(result))
return self.convertToXml(result)
return xml return xml
security.declareProtected(Permissions.AccessContentsInformation,'getWorkflowActionFromXml') security.declareProtected(Permissions.AccessContentsInformation,'getWorkflowActionFromXml')
...@@ -833,6 +869,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -833,6 +869,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
data = data.replace('\n','') data = data.replace('\n','')
if type(data) is type(u"a"): if type(data) is type(u"a"):
data = data.encode(self.getEncoding()) data = data.encode(self.getEncoding())
if data=='None':
return None
# We can now convert string in tuple, dict, binary... # We can now convert string in tuple, dict, binary...
if data_type in self.list_type_list: if data_type in self.list_type_list:
if type(data) is type('a'): if type(data) is type('a'):
...@@ -891,26 +929,47 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -891,26 +929,47 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return conflict_list return conflict_list
def isWorkflowActionAddable(self, object=None,status=None,wf_tool=None,xml=None): def isWorkflowActionAddable(self, object=None,status=None,wf_tool=None,
wf_id=None,xml=None):
""" """
Some checking in order to check if we should add the workfow or not Some checking in order to check if we should add the workfow or not
""" We should not returns a conflict list as we wanted before, we should
conflict_list = [] instead go to a conflict state.
if status.has_key('action'): """
action_name = status['action'] # We first test if the status in not already inside the workflow_history
authorized = 0 wf_history = object.workflow_history
authorized_actions = wf_tool.getActionsFor(object) if wf_history.has_key(wf_id):
LOG('isWorkflowActionAddable, status:',0,status) action_list = wf_history[wf_id]
LOG('isWorkflowActionAddable, authorized_actions:',0,authorized_actions) else: action_list = []
for action in authorized_actions: addable = 1
if action['id']==action_name: for action in action_list:
authorized = 1 this_one = 1
if not authorized: for key in action.keys():
string_io = StringIO() if status[key] != action[key]:
PrettyPrint(xml,stream=string_io) this_one = 0
conflict = Conflict(object_path=object.getPhysicalPath(), break
keyword=self.history_tag) if this_one:
conflict.setXupdate(string_io.getvalue()) addable = 0
conflict.setRemoteValue(status) break
conflict_list += [conflict] return addable
return conflict_list
# This doesn't works fine because all workflows variables
# are not set the same way
# if status.has_key('action'):
# action_name = status['action']
# authorized = 0
# authorized_actions = wf_tool.getActionsFor(object)
# LOG('isWorkflowActionAddable, status:',0,status)
# LOG('isWorkflowActionAddable, authorized_actions:',0,authorized_actions)
# for action in authorized_actions:
# if action['id']==action_name:
# authorized = 1
# if not authorized:
# string_io = StringIO()
# PrettyPrint(xml,stream=string_io)
# conflict = Conflict(object_path=object.getPhysicalPath(),
# keyword=self.history_tag)
# conflict.setXupdate(string_io.getvalue())
# conflict.setRemoteValue(status)
# conflict_list += [conflict]
# return conflict_list
...@@ -187,9 +187,9 @@ class Signature(SyncCode): ...@@ -187,9 +187,9 @@ class Signature(SyncCode):
""" """
# Constructor # Constructor
def __init__(self,gid=None, status=None, xml_string=None): def __init__(self,gid=None, id=None, status=None, xml_string=None):
self.setGid(gid) self.setGid(gid)
self.setId(None) self.setId(id)
self.status = status self.status = status
self.setXML(xml_string) self.setXML(xml_string)
self.partial_xml = None self.partial_xml = None
...@@ -595,14 +595,14 @@ class Subscription(SyncCode, Implicit): ...@@ -595,14 +595,14 @@ class Subscription(SyncCode, Implicit):
""" """
self.xml_mapping = xml_mapping self.xml_mapping = xml_mapping
def setGidGenerator(self, method_id): def setGidGenerator(self, method):
""" """
This set the method name wich allows to find a gid This set the method name wich allows to find a gid
from any object from any object
""" """
if method_id in (None,''): if method in (None,''):
method_id = 'getId' method = 'getId'
self.gid_generator = method_id self.gid_generator = method
def getGidGenerator(self): def getGidGenerator(self):
""" """
...@@ -611,6 +611,22 @@ class Subscription(SyncCode, Implicit): ...@@ -611,6 +611,22 @@ class Subscription(SyncCode, Implicit):
""" """
return self.gid_generator return self.gid_generator
def getGidFromObject(self, object):
"""
"""
o_base = aq_base(object)
o_gid = None
LOG('getGidFromObject',0,'gidgenerator : %s' % repr(self.getGidGenerator()))
gid_gen = self.getGidGenerator()
if callable(gid_gen):
o_gid=gid_gen(object)
elif hasattr(o_base, gid_gen):
LOG('getGidFromObject',0,'there is the gid generator')
generator = getattr(object, self.getGidGenerator())
o_gid = generator()
LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid))
return o_gid
def getObjectFromGid(self, gid): def getObjectFromGid(self, gid):
""" """
This tries to get the object with the given gid This tries to get the object with the given gid
...@@ -619,15 +635,9 @@ class Subscription(SyncCode, Implicit): ...@@ -619,15 +635,9 @@ class Subscription(SyncCode, Implicit):
signature = self.getSignature(gid) signature = self.getSignature(gid)
# First look if we do already have the mapping between # First look if we do already have the mapping between
# the id and the gid # the id and the gid
# query_list = []
# query = self.getQuery()
# if query is type('a'):
# query_method = getattr(object,self.getQuery(),None)
# query_list = query()
# if callable(query):
# query_list = query(self)
object_list = self.getObjectList() object_list = self.getObjectList()
destination = self.getDestination() destination = self.getDestination()
LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
if signature is not None: if signature is not None:
o_id = signature.getId() o_id = signature.getId()
o = None o = None
...@@ -639,16 +649,9 @@ class Subscription(SyncCode, Implicit): ...@@ -639,16 +649,9 @@ class Subscription(SyncCode, Implicit):
return o return o
for o in object_list: for o in object_list:
LOG('getObjectFromGid',0,'working on : %s' % repr(o)) LOG('getObjectFromGid',0,'working on : %s' % repr(o))
o_base = aq_base(o) o_gid = self.getGidFromObject(o)
LOG('getObjectFromGid',0,'gidgenerator : %s' % repr(self.getGidGenerator())) if o_gid == gid:
if hasattr(o_base, self.getGidGenerator()): return o
LOG('getObjectFromGid',0,'there is the gid generator')
generator = getattr(o, self.getGidGenerator())
o_gid = generator()
LOG('getObjectFromGid',0,'o_gid: %s' % repr(o_gid))
LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
if o_gid == gid:
return o
LOG('getObjectFromGid',0,'returning None') LOG('getObjectFromGid',0,'returning None')
return None return None
...@@ -675,20 +678,27 @@ class Subscription(SyncCode, Implicit): ...@@ -675,20 +678,27 @@ class Subscription(SyncCode, Implicit):
""" """
This tries to generate a new Id This tries to generate a new Id
""" """
if self.getIdGenerator() is not None: LOG('generateNewId, object: ',0,object.getPhysicalPath())
id_generator = self.getIdGenerator()
LOG('generateNewId, id_generator: ',0,id_generator)
if id_generator is not None:
o_base = aq_base(object) o_base = aq_base(object)
if hasattr(aq_base, self.getIdGenerator()): new_id = None
generator = getattr(o, self.getIdGenerator()) if callable(id_generator):
new_id = id_generator(object)
elif hasattr(o_base, id_generator):
generator = getattr(object, id_generator)
new_id = generator() new_id = generator()
return new_id LOG('generateNewId, new_id: ',0,new_id)
return new_id
return None return None
def setIdGenerator(self, method_id): def setIdGenerator(self, method):
""" """
This set the method name wich allows to generate This set the method name wich allows to generate
a new id a new id
""" """
self.id_generator = method_id self.id_generator = method
def getIdGenerator(self): def getIdGenerator(self):
""" """
......
...@@ -198,14 +198,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -198,14 +198,8 @@ class XMLSyncUtilsMixin(SyncCode):
file2 = open('/tmp/sync_old_object','w') file2 = open('/tmp/sync_old_object','w')
file2.write(old_xml) file2.write(old_xml)
file2.close() file2.close()
#xupdate = commands.getoutput('xmldiff -xg /tmp/sync_old_object /tmp/sync_new_object')
xupdate = commands.getoutput('erp5diff /tmp/sync_old_object /tmp/sync_new_object') xupdate = commands.getoutput('erp5diff /tmp/sync_old_object /tmp/sync_new_object')
xupdate = xupdate[xupdate.find('<xupdate:modifications'):] xupdate = xupdate[xupdate.find('<xupdate:modifications'):]
# XXX To be removed, this is only needed for xmldiff with does bad things
#while xupdate.find('xupdate:move')>0:
# LOG('getXupdateObject',0,'Removing the move section')
# xupdate = xupdate[:xupdate.find('<xupdate:move')] + \
# xupdate[xupdate.find('</xupdate:move>\n')+16:]
return xupdate return xupdate
def getXMLObject(self, object=None, xml_mapping=None): def getXMLObject(self, object=None, xml_mapping=None):
...@@ -318,6 +312,17 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -318,6 +312,17 @@ class XMLSyncUtilsMixin(SyncCode):
return int(subnode.childNodes[0].data) return int(subnode.childNodes[0].data)
return None return None
def getStatusCommand(self, xml):
"""
Return the value of the command inside the xml_stream
"""
# Get informations from the body
if xml.nodeName=='Status':
for subnode in xml.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Cmd':
return subnode.childNodes[0].data
return None
def getAlertCode(self, xml_stream): def getAlertCode(self, xml_stream):
""" """
Return the value of the alert code inside the full syncml message Return the value of the alert code inside the full syncml message
...@@ -428,20 +433,20 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -428,20 +433,20 @@ class XMLSyncUtilsMixin(SyncCode):
return subnode return subnode
return next_status return next_status
def getActionObjectId(self, action): # def getActionObjectId(self, action):
""" # """
XXX Deprecated # XXX Deprecated
Return the id of the object described by the action # Return the id of the object described by the action
""" # """
for subnode in action.childNodes: # for subnode in action.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item': # if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item':
for subnode2 in subnode.childNodes: # for subnode2 in subnode.childNodes:
if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Data': # if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Data':
for subnode3 in subnode2.childNodes: # for subnode3 in subnode2.childNodes:
if subnode3.nodeType == subnode3.ELEMENT_NODE and subnode3.nodeName == 'object': # if subnode3.nodeType == subnode3.ELEMENT_NODE and subnode3.nodeName == 'object':
for subnode4 in subnode3.childNodes: # for subnode4 in subnode3.childNodes:
if subnode4.nodeType == subnode4.ELEMENT_NODE and subnode4.nodeName == 'id': # if subnode4.nodeType == subnode4.ELEMENT_NODE and subnode4.nodeName == 'id':
return str(subnode4.childNodes[0].data) # return str(subnode4.childNodes[0].data)
#return subnode4.childNodes[0].data #return subnode4.childNodes[0].data
def getDataSubNode(self, action): def getDataSubNode(self, action):
...@@ -562,20 +567,14 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -562,20 +567,14 @@ class XMLSyncUtilsMixin(SyncCode):
""" """
local_gid_list = [] local_gid_list = []
syncml_data = '' syncml_data = ''
# store_xupdate = 0
# if object is None:
# object_list = domain.getObjectList()
# else:
# store_xupdate = 1
# object_list = [object]
for object in domain.getObjectList(): for object in domain.getObjectList():
status = self.SENT status = self.SENT
gid_generator = getattr(object,domain.getGidGenerator(),None) #gid_generator = getattr(object,domain.getGidGenerator(),None)
object_gid = None object_gid = domain.getGidFromObject(object)
if gid_generator is not None: local_gid_list += [object_gid]
object_gid = gid_generator() #if gid_generator is not None:
local_gid_list += [object_gid] # object_gid = gid_generator()
force = 0 force = 0
if syncml_data.count('\n') < self.MAX_LINES and (object.id.find('.')!=0): # If not we have to cut if syncml_data.count('\n') < self.MAX_LINES and (object.id.find('.')!=0): # If not we have to cut
xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping) xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping)
...@@ -597,7 +596,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -597,7 +596,7 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('PubSyncModif',0,'Current object.getPath: %s' % object.getPath()) #LOG('PubSyncModif',0,'Current object.getPath: %s' % object.getPath())
LOG('getSyncMLData',0,'no signature for gid: %s' % object_gid) LOG('getSyncMLData',0,'no signature for gid: %s' % object_gid)
xml_string = xml_object xml_string = xml_object
signature = Signature(gid=object_gid) signature = Signature(gid=object_gid,id=object.getId())
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
if xml_string.count('\n') > self.MAX_LINES: if xml_string.count('\n') > self.MAX_LINES:
more_data=1 more_data=1
...@@ -710,7 +709,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -710,7 +709,6 @@ class XMLSyncUtilsMixin(SyncCode):
# were not able to create # were not able to create
syncml_data += self.deleteXMLObject(xml_object=signature.getXML() or '', syncml_data += self.deleteXMLObject(xml_object=signature.getXML() or '',
object_gid=object_gid,cmd_id=cmd_id) object_gid=object_gid,cmd_id=cmd_id)
subscriber.delSignature(object_gid)
return (syncml_data,xml_confirmation,cmd_id) return (syncml_data,xml_confirmation,cmd_id)
...@@ -750,7 +748,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -750,7 +748,6 @@ class XMLSyncUtilsMixin(SyncCode):
data_subnode = self.getDataSubNode(next_action) data_subnode = self.getDataSubNode(next_action)
if next_action.nodeName == 'Add': if next_action.nodeName == 'Add':
# Then store the xml of this new subobject # Then store the xml of this new subobject
#object = domain.getObjectFromGid(object=destination_path,gid=object_gid)
if object is None: if object is None:
object_id = domain.generateNewId(object=destination_path) object_id = domain.generateNewId(object=destination_path)
conflict_list += conduit.addNode(xml=data_subnode, object=destination_path, conflict_list += conduit.addNode(xml=data_subnode, object=destination_path,
...@@ -763,7 +760,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -763,7 +760,6 @@ class XMLSyncUtilsMixin(SyncCode):
xml_object = '' xml_object = ''
if mapping is not None: if mapping is not None:
xml_object = mapping() xml_object = mapping()
#xml_object = object.asXML()
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
signature.setId(object.getId()) signature.setId(object.getId())
signature.setXML(xml_object) signature.setXML(xml_object)
...@@ -785,7 +781,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -785,7 +781,6 @@ class XMLSyncUtilsMixin(SyncCode):
xml_object = '' xml_object = ''
if mapping is not None: if mapping is not None:
xml_object = mapping() xml_object = mapping()
#xml_object = object.asXML()
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
if conflict_list != []: if conflict_list != []:
status_code = self.CONFLICT status_code = self.CONFLICT
...@@ -809,16 +804,14 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -809,16 +804,14 @@ class XMLSyncUtilsMixin(SyncCode):
data_subnode_string = string_io.getvalue() data_subnode_string = string_io.getvalue()
LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string) LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string)
signature.setSubscriberXupdate(data_subnode_string) signature.setSubscriberXupdate(data_subnode_string)
# xml_string = self.getXupdateObject(object=object,
# xml_mapping=domain.xml_mapping,
# old_xml=signature.getXML())
# signature.setPublisherXupdate(xml_string) XXX is it needed ??
elif next_action.nodeName == 'Delete': elif next_action.nodeName == 'Delete':
object_id = object.id object_id = signature.getId()
conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path, conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path,
object_id=self.getActionId(next_action)) object_id=object_id)
subscriber.delSignature(object_gid) subscriber.delSignature(object_gid)
xml_confirmation += self.SyncMLConfirmation(cmd_id,
object_gid,status_code,'Delete')
else: # We want to retrieve more data else: # We want to retrieve more data
signature.setStatus(self.PARTIAL) signature.setStatus(self.PARTIAL)
#LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial)) #LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
...@@ -851,23 +844,28 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -851,23 +844,28 @@ class XMLSyncUtilsMixin(SyncCode):
while next_status != None: while next_status != None:
object_gid = self.getStatusTarget(next_status) object_gid = self.getStatusTarget(next_status)
status_code = self.getStatusCode(next_status) status_code = self.getStatusCode(next_status)
status_cmd = self.getStatusCommand(next_status)
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignature(object_gid)
LOG('SyncModif',0,'next_status: %s' % str(status_code)) LOG('SyncModif',0,'next_status: %s' % str(status_code))
if status_code == self.CHUNK_OK: if status_cmd in ('Add','Replace'):
destination_waiting_more_data = 1 if status_code == self.CHUNK_OK:
signature.setStatus(self.PARTIAL) destination_waiting_more_data = 1
elif status_code == self.CONFLICT: signature.setStatus(self.PARTIAL)
signature.setStatus(self.CONFLICT) elif status_code == self.CONFLICT:
elif status_code == self.CONFLICT_MERGE: signature.setStatus(self.CONFLICT)
# We will have to apply the update, and we should not care about conflicts elif status_code == self.CONFLICT_MERGE:
# so we have to force the update # We will have to apply the update, and we should not care about conflicts
signature.setStatus(self.NOT_SYNCHRONIZED) # so we have to force the update
signature.setForce(1) signature.setStatus(self.NOT_SYNCHRONIZED)
elif status_code == self.CONFLICT_CLIENT_WIN: signature.setForce(1)
# The server was agree to apply our updates, nothing to do elif status_code == self.CONFLICT_CLIENT_WIN:
signature.setStatus(self.SYNCHRONIZED) # The server was agree to apply our updates, nothing to do
elif status_code == self.SUCCESS: signature.setStatus(self.SYNCHRONIZED)
signature.setStatus(self.SYNCHRONIZED) elif status_code == self.SUCCESS:
signature.setStatus(self.SYNCHRONIZED)
elif status_cmd == 'Delete':
if status_code == self.SUCCESS:
subscriber.delSignature(object_gid)
next_status = self.getNextSyncBodyStatus(remote_xml, next_status) next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
return (destination_waiting_more_data, has_status_list) return (destination_waiting_more_data, has_status_list)
......
...@@ -53,15 +53,24 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -53,15 +53,24 @@ class TestERP5SyncML(ERP5TypeTestCase):
# Different variables used for this test # Different variables used for this test
run_all_test = 1 run_all_test = 1
workflow_id = 'edit_workflow'
first_name1 = 'Sebastien' first_name1 = 'Sebastien'
last_name1 = 'Robin' last_name1 = 'Robin'
description1 = 'description1 $sdfr_sdfsdf_oisfsopf' description1 = 'description1 $sdfr_sdfsdf_oisfsopf'
lang1 = 'fr'
format2 = 'html'
format3 = 'xml'
format4 = 'txt'
first_name2 = 'Jean-Paul' first_name2 = 'Jean-Paul'
last_name2 = 'Smets' last_name2 = 'Smets'
description2 = 'description2@ $*< <<< >>>></title>&oekd' description2 = 'description2@ $*< <<< >>>></title>&oekd'
lang2 = 'en'
first_name3 = 'Yoshinori' first_name3 = 'Yoshinori'
last_name3 = 'Okuji' last_name3 = 'Okuji'
description3 = 'description3 sdf__sdf_df___&&]]]' description3 = 'description3 sdf__sdf_df___&&]]]'
description4 = 'description4 sdflkmooo^^^^]]]]]{{{{{{{'
lang3 = 'jp'
lang4 = 'ca'
xml_mapping = 'asXML' xml_mapping = 'asXML'
id1 = '170' id1 = '170'
id2 = '171' id2 = '171'
...@@ -153,8 +162,8 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -153,8 +162,8 @@ class TestERP5SyncML(ERP5TypeTestCase):
def login(self, quiet=0, run=run_all_test): def login(self, quiet=0, run=run_all_test):
uf = self.getPortal().acl_users uf = self.getPortal().acl_users
uf._doAddUser('ERP5TypeTestCase', '', ['Manager'], []) uf._doAddUser('seb', '', ['Manager'], [])
user = uf.getUserById('ERP5TypeTestCase').__of__(uf) user = uf.getUserById('seb').__of__(uf)
newSecurityManager(None, user) newSecurityManager(None, user)
def populatePersonServer(self, quiet=0, run=run_all_test): def populatePersonServer(self, quiet=0, run=run_all_test):
...@@ -178,9 +187,24 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -178,9 +187,24 @@ class TestERP5SyncML(ERP5TypeTestCase):
return nb_person return nb_person
def setupPublicationAndSubscription(self, quiet=0, run=run_all_test): def setupPublicationAndSubscription(self, quiet=0, run=run_all_test):
self.testAddPublication(quiet=quiet,run=run) self.testAddPublication(quiet=1,run=1)
self.testAddSubscription1(quiet=quiet,run=run) self.testAddSubscription1(quiet=1,run=1)
self.testAddSubscription2(quiet=quiet,run=run) self.testAddSubscription2(quiet=1,run=1)
def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test):
self.setupPublicationAndSubscription(quiet=1,run=1)
def getGid(object):
return object.getTitle()
portal_sync = self.getSynchronizationTool()
sub1 = portal_sync.getSubscription(self.sub_id1)
sub2 = portal_sync.getSubscription(self.sub_id2)
pub = portal_sync.getPublication(self.pub_id)
pub.setGidGenerator(getGid)
sub1.setGidGenerator(getGid)
sub2.setGidGenerator(getGid)
pub.setIdGenerator('generateNewId')
sub1.setIdGenerator('generateNewId')
sub2.setIdGenerator('generateNewId')
def testGetSynchronizationList(self, quiet=0, run=run_all_test): def testGetSynchronizationList(self, quiet=0, run=run_all_test):
# This test the getSynchronizationList, ie, # This test the getSynchronizationList, ie,
...@@ -196,9 +220,11 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -196,9 +220,11 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.failUnless(len(synchronization_list)==self.nb_synchronization) self.failUnless(len(synchronization_list)==self.nb_synchronization)
def testGetObjectList(self, quiet=0, run=run_all_test): def testGetObjectList(self, quiet=0, run=run_all_test):
# This test the default getObjectList, ie, when the """
# query is 'objectValues', and this also test if we enter This test the default getObjectList, ie, when the
# a new method for the query query is 'objectValues', and this also test if we enter
a new method for the query
"""
if not run: return if not run: return
if not quiet: if not quiet:
ZopeTestCase._print('\nTest getObjectList ') ZopeTestCase._print('\nTest getObjectList ')
...@@ -224,8 +250,10 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -224,8 +250,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.failUnless(len(object_list)==1) self.failUnless(len(object_list)==1)
def testExportImport(self, quiet=0, run=run_all_test): def testExportImport(self, quiet=0, run=run_all_test):
# We will try to export a person with asXML """
# And then try to add it to another folder with a conduit We will try to export a person with asXML
And then try to add it to another folder with a conduit
"""
if not run: return if not run: return
if not quiet: if not quiet:
ZopeTestCase._print('\nTest Export and Import ') ZopeTestCase._print('\nTest Export and Import ')
...@@ -242,10 +270,17 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -242,10 +270,17 @@ class TestERP5SyncML(ERP5TypeTestCase):
new_object = person_client1._getOb(self.id1) new_object = person_client1._getOb(self.id1)
self.failUnless(new_object.getLastName()==self.last_name1) self.failUnless(new_object.getLastName()==self.last_name1)
self.failUnless(new_object.getFirstName()==self.first_name1) self.failUnless(new_object.getFirstName()==self.first_name1)
# XXX We should also looks at the workflow history
self.failUnless(len(new_object.workflow_history[self.workflow_id])==2)
s_local_role = person_server.get_local_roles()
c_local_role = person_client1.get_local_roles()
self.assertEqual(s_local_role,c_local_role)
def synchronize(self, id, run=run_all_test): def synchronize(self, id, run=run_all_test):
# This just define how we synchronize, we have """
# to define it here because it is specific to the unit testing This just define how we synchronize, we have
to define it here because it is specific to the unit testing
"""
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
portal_sync.email = None portal_sync.email = None
subscription = portal_sync.getSubscription(id) subscription = portal_sync.getSubscription(id)
...@@ -489,6 +524,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -489,6 +524,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
- id1 - id1
- id1 - id1
- id2 - id2
- id1
- id2 - id2
""" """
if not run: return if not run: return
...@@ -501,12 +537,18 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -501,12 +537,18 @@ class TestERP5SyncML(ERP5TypeTestCase):
kw = {'first_name':self.first_name1,'last_name':self.last_name1, kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1} 'description':self.description1}
sub_person1.edit(**kw) sub_person1.edit(**kw)
sub_sub_person = sub_person1.newContent(id=self.id2,portal_type='Person') sub_sub_person1 = sub_person1.newContent(id=self.id1,portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1}
sub_sub_person1.edit(**kw)
sub_sub_person2 = sub_person1.newContent(id=self.id2,portal_type='Person')
kw = {'first_name':self.first_name2,'last_name':self.last_name2, kw = {'first_name':self.first_name2,'last_name':self.last_name2,
'description':self.description2} 'description':self.description2}
sub_sub_person.edit(**kw) sub_sub_person2.edit(**kw)
# remove ('','portal...','person_server') # remove ('','portal...','person_server')
len_path = len(sub_sub_person.getPhysicalPath()) - 3 len_path = len(sub_sub_person1.getPhysicalPath()) - 3
self.failUnless(len_path==3)
len_path = len(sub_sub_person2.getPhysicalPath()) - 3
self.failUnless(len_path==3) self.failUnless(len_path==3)
def testAddSubObject(self, quiet=0, run=run_all_test): def testAddSubObject(self, quiet=0, run=run_all_test):
...@@ -522,23 +564,32 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -522,23 +564,32 @@ class TestERP5SyncML(ERP5TypeTestCase):
LOG('Testing... ',0,'testAddSubObject') LOG('Testing... ',0,'testAddSubObject')
self.populatePersonServerWithSubObject(quiet=1,run=1) self.populatePersonServerWithSubObject(quiet=1,run=1)
self.synchronize(self.sub_id1) self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized() self.checkSynchronizationStateIsSynchronized()
person_client1 = self.getPersonClient1() person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1) person1_c = person_client1._getOb(self.id1)
sub_person1_c = person1_c._getOb(self.id1) sub_person1_c = person1_c._getOb(self.id1)
sub_sub_person = sub_person1_c._getOb(self.id2) sub_sub_person1 = sub_person1_c._getOb(self.id1)
sub_sub_person2 = sub_person1_c._getOb(self.id2)
# remove ('','portal...','person_server') # remove ('','portal...','person_server')
len_path = len(sub_sub_person.getPhysicalPath()) - 3 len_path = len(sub_sub_person1.getPhysicalPath()) - 3
self.failUnless(len_path==3) self.failUnless(len_path==3)
self.failUnless(sub_sub_person.getDescription()==self.description2) len_path = len(sub_sub_person2.getPhysicalPath()) - 3
self.failUnless(sub_sub_person.getFirstName()==self.first_name2) self.failUnless(len_path==3)
self.failUnless(sub_sub_person.getLastName()==self.last_name2) self.failUnless(sub_sub_person1.getDescription()==self.description1)
self.failUnless(sub_sub_person1.getFirstName()==self.first_name1)
def testUpdateSubObject(self, quiet=0, run=1): self.failUnless(sub_sub_person1.getLastName()==self.last_name1)
self.failUnless(sub_sub_person2.getDescription()==self.description2)
self.failUnless(sub_sub_person2.getFirstName()==self.first_name2)
self.failUnless(sub_sub_person2.getLastName()==self.last_name2)
def testUpdateSubObject(self, quiet=0, run=run_all_test):
""" """
In this test, we start with a tree of object already In this test, we start with a tree of object already
synchronized, then we update a subobject, and we will see synchronized, then we update a subobject, and we will see
if it is updated correctly if it is updated correctly.
To make this test a bit more harder, we will update on both
the client and the server by the same time
""" """
if not run: return if not run: return
self.testAddSubObject(quiet=1,run=1) self.testAddSubObject(quiet=1,run=1)
...@@ -562,6 +613,326 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -562,6 +613,326 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.failUnless(sub_sub_person_s.getDescription()==self.description3) self.failUnless(sub_sub_person_s.getDescription()==self.description3)
self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3) self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3)
def testDeleteObject(self, quiet=0, run=run_all_test):
"""
We will do a first synchronization, then delete an object on both
sides, and we will see if nothing is left on the server and also
on the two clients
"""
if not run: return
self.testFirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Delete Object ')
LOG('Testing... ',0,'testDeleteObject')
person_server = self.getPersonServer()
person_server.manage_delObjects(self.id1)
person_client1 = self.getPersonClient1()
person_client1.manage_delObjects(self.id2)
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id)
subscription1 = portal_sync.getSubscription(self.sub_id1)
subscription2 = portal_sync.getSubscription(self.sub_id2)
self.failUnless(len(publication.getObjectList())==0)
self.failUnless(len(subscription1.getObjectList())==0)
self.failUnless(len(subscription2.getObjectList())==0)
def testDeleteSubObject(self, quiet=0, run=run_all_test):
"""
We will do a first synchronization, then delete a sub-object on both
sides, and we will see if nothing is left on the server and also
on the two clients
- before : after :
- id1 - id1
- id1 - id1
- id2 - id2
- id1
- id2
"""
if not run: return
self.testAddSubObject(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Delete Sub Object ')
LOG('Testing... ',0,'testDeleteSubObject')
person_server = self.getPersonServer()
sub_object_s = person_server._getOb(self.id1)._getOb(self.id1)
sub_object_s.manage_delObjects(self.id1)
person_client1 = self.getPersonClient1()
sub_object_c1 = person_client1._getOb(self.id1)._getOb(self.id1)
sub_object_c1.manage_delObjects(self.id2)
person_client2 = self.getPersonClient2()
sub_object_c2 = person_client2._getOb(self.id1)._getOb(self.id1)
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
len_s = len(sub_object_s.objectValues())
len_c1 = len(sub_object_c1.objectValues())
len_c2 = len(sub_object_c2.objectValues())
self.failUnless(len_s==len_c1==len_c2==0)
def testGetConflictListOnSubObject(self, quiet=0, run=run_all_test):
"""
We will change several attributes on a sub object on both the server
and a client, then we will see if we have correctly the conflict list
"""
if not run: return
self.testAddSubObject(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Get Conflict List On Sub Object ')
LOG('Testing... ',0,'testGetConflictListOnSubObject')
person_server = self.getPersonServer()
object_s = person_server._getOb(self.id1)
sub_object_s = object_s._getOb(self.id1)
person_client1 = self.getPersonClient1()
sub_object_c1 = person_client1._getOb(self.id1)._getOb(self.id1)
person_client2 = self.getPersonClient2()
sub_object_c2 = person_client2._getOb(self.id1)._getOb(self.id1)
# Change values so that we will get conflicts
kw = {'language':self.lang2,'description':self.description2}
sub_object_s.edit(**kw)
kw = {'language':self.lang3,'description':self.description3}
sub_object_c1.edit(**kw)
self.synchronize(self.sub_id1)
portal_sync = self.getSynchronizationTool()
conflict_list = portal_sync.getConflictList()
self.failUnless(len(conflict_list)==2)
conflict_list = portal_sync.getConflictList(sub_object_c1)
self.failUnless(len(conflict_list)==0)
conflict_list = portal_sync.getConflictList(object_s)
self.failUnless(len(conflict_list)==0)
conflict_list = portal_sync.getConflictList(sub_object_s)
self.failUnless(len(conflict_list)==2)
def testApplyPublisherDocumentOnSubObject(self, quiet=0, run=run_all_test):
"""
there's several conflict on a sub object, we will see if we can
correctly have the publisher version of this document
"""
if not run: return
self.testGetConflictListOnSubObject(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Apply Publisher Document On Sub Object ')
LOG('Testing... ',0,'testApplyPublisherDocumentOnSubObject')
portal_sync = self.getSynchronizationTool()
conflict_list = portal_sync.getConflictList()
conflict = conflict_list[0]
conflict.applyPublisherDocument()
person_server = self.getPersonServer()
sub_object_s = person_server._getOb(self.id1)._getOb(self.id1)
person_client1 = self.getPersonClient1()
sub_object_c1 = person_client1._getOb(self.id1)._getOb(self.id1)
person_client2 = self.getPersonClient2()
sub_object_c2 = person_client2._getOb(self.id1)._getOb(self.id1)
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(sub_object_s.getDescription()==self.description2)
self.failUnless(sub_object_s.getLanguage()==self.lang2)
self.failUnless(sub_object_c1.getDescription()==self.description2)
self.failUnless(sub_object_c1.getLanguage()==self.lang2)
self.failUnless(sub_object_c2.getDescription()==self.description2)
self.failUnless(sub_object_c2.getLanguage()==self.lang2)
def testApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test):
"""
there's several conflict on a sub object, we will see if we can
correctly have the subscriber version of this document
"""
if not run: return
self.testGetConflictListOnSubObject(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Apply Subscriber Document On Sub Object ')
LOG('Testing... ',0,'testApplySubscriberDocumentOnSubObject')
portal_sync = self.getSynchronizationTool()
conflict_list = portal_sync.getConflictList()
conflict = conflict_list[0]
conflict.applySubscriberDocument()
person_server = self.getPersonServer()
sub_object_s = person_server._getOb(self.id1)._getOb(self.id1)
person_client1 = self.getPersonClient1()
sub_object_c1 = person_client1._getOb(self.id1)._getOb(self.id1)
person_client2 = self.getPersonClient2()
sub_object_c2 = person_client2._getOb(self.id1)._getOb(self.id1)
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(sub_object_s.getDescription()==self.description3)
self.failUnless(sub_object_s.getLanguage()==self.lang3)
self.failUnless(sub_object_c1.getDescription()==self.description3)
self.failUnless(sub_object_c1.getLanguage()==self.lang3)
self.failUnless(sub_object_c2.getDescription()==self.description3)
self.failUnless(sub_object_c2.getLanguage()==self.lang3)
def testSynchronizeWithStrangeGid(self, quiet=0, run=run_all_test):
"""
By default, the synchronization process use the id in order to
recognize objects (because by default, getGid==getId. Here, we will see
if it also works with a somewhat strange getGid
"""
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Synchronize With Strange Gid ')
LOG('Testing... ',0,'testSynchronizeWithStrangeGid')
self.login()
self.setupPublicationAndSubscriptionAndGid(quiet=1,run=1)
nb_person = self.populatePersonServer(quiet=1,run=1)
# This will test adding object
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
portal_sync = self.getSynchronizationTool()
subscription1 = portal_sync.getSubscription(self.sub_id1)
self.failUnless(len(subscription1.getObjectList())==nb_person)
publication = portal_sync.getPublication(self.pub_id)
self.failUnless(len(publication.getObjectList())==nb_person)
gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin'
person_c1 = subscription1.getObjectFromGid(gid)
id_c1 = person_c1.getId()
self.failUnless(id_c1 in ('1','2')) # id given by the default generateNewId
person_s = publication.getObjectFromGid(gid)
id_s = person_s.getId()
self.failUnless(id_s==self.id1)
# This will test updating object
person_s.setDescription(self.description3)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(person_s.getDescription()==self.description3)
self.failUnless(person_c1.getDescription()==self.description3)
# This will test deleting object
person_server = self.getPersonServer()
person_client1 = self.getPersonClient1()
person_server.manage_delObjects(self.id2)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(len(subscription1.getObjectList())==(nb_person-1))
self.failUnless(len(publication.getObjectList())==(nb_person-1))
person_s = publication.getObjectFromGid(gid)
person_c1 = subscription1.getObjectFromGid(gid)
self.failUnless(person_s.getDescription()==self.description3)
self.failUnless(person_c1.getDescription()==self.description3)
def testMultiNodeConflict(self, quiet=0, run=run_all_test):
"""
We will create conflicts with 3 differents nodes, and we will
solve it by taking one full version of documents.
"""
if not run: return
self.testFirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Multi Node Conflict ')
LOG('Testing... ',0,'testMultiNodeConflict')
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
kw = {'language':self.lang2,'description':self.description2,
'format':self.format2}
person1_s.edit(**kw)
person_client1 = self.getPersonClient1()
person1_c1 = person_client1._getOb(self.id1)
kw = {'language':self.lang3,'description':self.description3,
'format':self.format3}
person1_c1.edit(**kw)
person_client2 = self.getPersonClient2()
person1_c2 = person_client2._getOb(self.id1)
kw = {'language':self.lang4,'description':self.description4,
'format':self.format4}
person1_c2.edit(**kw)
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
conflict_list = portal_sync.getConflictList()
self.failUnless(len(conflict_list)==6)
# we will take :
# description on person_server
# language on person_client1
# format on person_client2
for conflict in conflict_list:
subscriber = conflict.getSubscriber()
property = conflict.getPropertyId()
resolve = 0
if property == 'language':
if subscriber.getSubscriptionUrl()==self.subscription_url1:
resolve = 1
conflict.applySubscriberValue()
if property == 'format':
if subscriber.getSubscriptionUrl()==self.subscription_url2:
resolve = 1
conflict.applySubscriberValue()
if not resolve:
conflict.applyPublisherValue()
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(person1_s.getDescription()==self.description2)
self.failUnless(person1_c1.getDescription()==self.description2)
self.failUnless(person1_c2.getDescription()==self.description2)
self.failUnless(person1_s.getLanguage()==self.lang3)
self.failUnless(person1_c1.getLanguage()==self.lang3)
self.failUnless(person1_c2.getLanguage()==self.lang3)
self.failUnless(person1_s.getFormat()==self.format4)
self.failUnless(person1_c1.getFormat()==self.format4)
self.failUnless(person1_c2.getFormat()==self.format4)
def testSynchronizeWorkflowHistory(self, quiet=0, run=run_all_test):
"""
We will do a synchronization, then we will edit two times
the object on the server, then two times the object on the
client, and see if the global history as 4 more actions.
"""
if not run: return
self.testFirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Synchronize WorkflowHistory ')
LOG('Testing... ',0,'testSynchronizeWorkflowHistory')
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
kw1 = {'description':self.description1}
kw2 = {'description':self.description2}
len_wf = len(person1_s.workflow_history[self.workflow_id])
person1_s.edit(**kw2)
person1_c.edit(**kw2)
person1_s.edit(**kw1)
person1_c.edit(**kw1)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(len(person1_s.workflow_history[self.workflow_id])==len_wf+4)
self.failUnless(len(person1_c.workflow_history[self.workflow_id])==len_wf+4)
def testUpdateLocalRole(self, quiet=0, run=run_all_test):
"""
We will do a first synchronization, then modify, add and delete
an user role and see if it is correctly synchronized
"""
if not run: return
self.testFirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Update Local Role ')
LOG('Testing... ',0,'testUpdateLocalRole')
# First, Create a new user
uf = self.getPortal().acl_users
uf._doAddUser('jp', '', ['Manager'], [])
user = uf.getUserById('jp').__of__(uf)
# then update create and delete roles
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
person2_s = person_server._getOb(self.id2)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
person2_c = person_client1._getOb(self.id2)
person1_s.manage_setLocalRoles('seb',['Manager','Owner'])
person2_s.manage_setLocalRoles('jp',['Manager','Owner'])
person2_s.manage_delLocalRoles(['seb'])
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
role_1_s = person1_s.get_local_roles()
role_2_s = person2_s.get_local_roles()
role_1_c = person1_c.get_local_roles()
role_2_c = person2_c.get_local_roles()
self.assertEqual(role_1_s,role_1_c)
self.assertEqual(role_2_s,role_2_c)
if __name__ == '__main__': if __name__ == '__main__':
framework() framework()
else: else:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment