test.erp5.testERP5DocumentSyncML.py 37.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
#          Danièle Vanbaelinghem <daniele@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

import os
Aurel's avatar
Aurel committed
31
from base64 import b16encode
32
from unittest import expectedFailure
33
import unittest
Aurel's avatar
Aurel committed
34

35
from AccessControl.SecurityManagement import newSecurityManager
Aurel's avatar
Aurel committed
36 37

from Products.ERP5Type.tests.runUnitTest import tests_home
38
from Products.ERP5Type.tests.utils import FileUpload
39 40 41
from erp5.component.tool import SynchronizationTool
from erp5.component.test.testERP5SyncML import TestERP5SyncMLMixin
from erp5.component.document import SyncMLSubscription
42

43 44
import Products.ERP5.tests
test_files = os.path.join(os.path.dirname(Products.ERP5.tests.__file__), 'test_data')
Nicolas Delaby's avatar
Nicolas Delaby committed
45
FILENAME_REGULAR_EXPRESSION = "(?P<reference>[A-Z]{3,10})-\
46 47 48
(?P<language>[a-z]{2})-(?P<version>[0-9]{3})"
REFERENCE_REGULAR_EXPRESSION = "(?P<reference>[A-Z]{3,10})(-\
(?P<language>[a-z]{2}))?(-(?P<version>[0-9]{3}))?"
49 50 51 52 53

def makeFileUpload(name):
  path = os.path.join(test_files, name)
  return FileUpload(path, name)

54
class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
55

56
  nb_objects = 10
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  #for objects
  ids = range(1, nb_objects+1)
  #id_max_text : number of document text
  id_max_text = nb_objects/2
  id1 = '2'
  id2 = '3'
  #for documents (encoding in unicode for utf-8)
  #files
  filename_text = 'TEST-en-002.txt'
  size_filename_text = len(makeFileUpload(filename_text).read())
  filename_odt = 'TEST-en-002.odt'
  size_filename_odt = len(makeFileUpload(filename_odt).read())
  filename_ppt = 'TEST-en-002.ppt'
  size_filename_ppt = len(makeFileUpload(filename_ppt).read())
  filename_pdf = 'TEST-en-002.pdf'
  size_filename_pdf = len(makeFileUpload(filename_pdf).read())
  #properties
  reference1 = 'P-SYNCML.Text'
  version1 = '001'
  language1 = 'en'
  #description1 - blaàéc1
  description1 = 'description1 - blac\xc3\xa0\xc3\xa91'
  short_title1 = 'P-SYNCML-Text'
  reference2 = 'P-SYNCML-SyncML.Document.Pdf'
  version2 = '001'
  language2 = 'fr'
  #description2 - file $£µ%c2éè!
  description2 = 'description2 - file $\xc2\xa3\xc2\xb5%c2\xc3\xa9\xc3\xa8!'
  short_title2 = 'P-SYNCML-Pdf'
  reference3 = 'P-SYNCML-SyncML.Document.WebPage'
  version3 = '001'
  language3 = 'ja'
  #description3 - file description3 - file ù@
  description3 = 'description3 - file \xc3\xb9@'
  short_title3 = 'P-SYNCML-WebPage'
  #for synchronization
  pub_id = 'Publication'
  sub_id1 = 'Subscription1'
  sub_id_from_server = 'SubscriptionFromServer'
  pub_query = 'objectValues'
  sub_query1 = 'objectValues'
  xml_mapping = 'asXML'
  pub_conduit = 'ERP5DocumentConduit'
  sub_conduit1 = 'ERP5DocumentConduit'
101 102 103
  publication_url = 'file:/%s/sync_server' % tests_home
  subscription_url = {'two_way': 'file:/%s/sync_client1' % tests_home,
      'from_server': 'file:/%s/sync_client_from_server' % tests_home}
104
  #for this tests
Aurel's avatar
Aurel committed
105 106 107
  nb_message_first_synchronization = 6
  nb_message_multi_first_synchronization = 12
  activity_enable=False
Nicolas Delaby's avatar
Nicolas Delaby committed
108

109 110 111 112 113

  def getBusinessTemplateList(self):
    """
      Return the list of business templates.
    """
Aurel's avatar
Aurel committed
114 115 116
    return list(TestERP5SyncMLMixin.getBusinessTemplateList(self)) + \
        ['erp5_ingestion', 'erp5_ingestion_mysql_innodb_catalog',
        'erp5_web', 'erp5_dms']
117 118 119 120

  def afterSetUp(self):
    """Setup."""
    self.login()
121 122
    self.portal.z_drop_syncml()
    self.portal.z_create_syncml()
123 124 125 126 127
    self.addPublications()
    self.addSubscriptions()
    self.portal = self.getPortal()
    self.setSystemPreferences()
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
128

129 130 131 132 133 134 135 136 137
  def beforeTearDown(self):
    """
      Do some stuff after each test:
      - clear document module of server and client
      - clear the publications and subscriptions
    """
    self.clearDocumentModules()
    self.clearPublicationsAndSubscriptions()

Aurel's avatar
Aurel committed
138 139 140 141

  def clearFiles(self):
    # reset files, because we do sync by files
    for filename in self.subscription_url.values():
142 143 144 145 146 147
      file_ = open(filename[len('file:/'):], 'w')
      file_.write('')
      file_.close()
    file_ = open(self.publication_url[len('file:/'):], 'w')
    file_.write('')
    file_.close()
Aurel's avatar
Aurel committed
148 149


150 151
  def setSystemPreferences(self):
    default_pref = self.portal.portal_preferences.default_site_preference
152
    default_pref.setPreferredDocumentFileNameRegularExpression(FILENAME_REGULAR_EXPRESSION)
153 154 155 156 157 158
    default_pref.setPreferredDocumentReferenceRegularExpression(REFERENCE_REGULAR_EXPRESSION)
    if default_pref.getPreferenceState() == 'disabled':
      default_pref.enable()

  def addSubscriptions(self):
    portal_sync = self.getSynchronizationTool()
159 160 161 162 163 164
    if self.sub_id1 not in portal_sync.objectIds():
      subscription = portal_sync.newContent(portal_type='SyncML Subscription',
                      id=self.sub_id1,
                      url_string=self.publication_url,
                      subscription_url_string=self.subscription_url['two_way'],
                      source='document_client1',
Aurel's avatar
Aurel committed
165 166 167 168 169
                      source_reference='Document:',
                      destination_reference='Document',
                      list_method_id= self.sub_query1,
                      xml_binding_generator_method_id=self.xml_mapping,
                      conduit_module_id=self.sub_conduit1,
170
                      sync_alert_code='two_way',
Aurel's avatar
Aurel committed
171
                      is_activity_enabled=self.activity_enable,
172 173 174 175
                      user_id='daniele',
                      password='myPassword')
      subscription.validate()
      self.tic()
176 177 178

  def addPublications(self):
    portal_sync = self.getSynchronizationTool()
179 180 181 182 183 184 185 186 187
    if self.pub_id not in portal_sync.objectIds():
      publication = portal_sync.newContent(portal_type='SyncML Publication',
                             id=self.pub_id,
                             url_string=self.publication_url,
                             source='document_server',
                             source_reference='Document',
                             list_method_id=self.pub_query,
                             xml_binding_generator_method_id=self.xml_mapping,
                             conduit_module_id=self.pub_conduit,
Aurel's avatar
Aurel committed
188
                             is_activity_enabled=self.activity_enable,)
189 190
      publication.validate()
      self.tic()
191

192
  def createDocumentModules(self, one_way=False):
193 194 195 196 197 198 199 200 201
    if not hasattr(self.portal, 'document_server'):
      self.portal.portal_types.constructContent(type_name = 'Document Module',
                                             container = self.portal,
                                             id = 'document_server')

    if not hasattr(self.portal, 'document_client1'):
      self.portal.portal_types.constructContent(type_name = 'Document Module',
                                             container = self.portal,
                                             id = 'document_client1')
Nicolas Delaby's avatar
Nicolas Delaby committed
202

203
    if one_way:
204 205 206 207 208
      if not hasattr(self.portal, 'document_client_from_server'):
        self.portal.portal_types.constructContent(type_name = 'Document Module',
                                               container = self.portal,
                                               id = 'document_client_from_server')
  def clearDocumentModules(self):
209
    if getattr(self.portal, 'document_server', None) is not None:
210
      self.portal._delObject(id='document_server')
211
    if getattr(self.portal, 'document_client1', None) is not None:
212 213
      self.portal._delObject(id='document_client1')
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
214

215 216
  def clearPublicationsAndSubscriptions(self):
    portal_sync = self.getSynchronizationTool()
217 218
    id_list = [object_id for object_id in portal_sync.objectIds()]
    portal_sync.manage_delObjects(id_list)
219 220 221 222 223 224 225 226
    self.tic()

  ####################
  ### Usefull methods
  ####################


  def getDocumentClient1(self):
227
    return getattr(self.portal, 'document_client1')
228 229

  def getDocumentClientFromServer(self):
230
    return getattr(self.portal, 'document_client_from_server')
Aurel's avatar
Aurel committed
231

232
  def getDocumentServer(self):
233
    return getattr(self.portal, 'document_server')
234

235
  def login(self):
236 237 238 239 240 241
    uf = self.portal.acl_users
    uf._doAddUser('daniele', 'myPassword', ['Manager'], [])
    uf._doAddUser('ERP5TypeTestCase', '', ['Manager'], [])
    uf._doAddUser('syncml', '', ['Manager'], [])
    user = uf.getUserById('daniele').__of__(uf)
    newSecurityManager(None, user)
Nicolas Delaby's avatar
Nicolas Delaby committed
242

243 244
  def resetSignaturePublicationAndSubscription(self):
    portal_sync = self.getSynchronizationTool()
245 246 247 248 249
    publication = portal_sync[self.pub_id]
    subscription1 = portal_sync[self.sub_id1]
    publication.resetSubscriberList()
    subscription1.resetSignatureList()
    subscription1.resetAnchorList()
250 251
    self.tic()

252
  def documentMultiServer(self):
253 254 255 256
    # create different document by category documents
    self.createDocumentModules()
    document_server = self.getDocumentServer()
    #plain text document
257 258 259
    for id_ in self.ids[:self.id_max_text]:
      reference = "Test-Text-%s" % (id_,)
      self.createDocument(id=id_, file_name=self.filename_text, reference=reference)
260
    self.commit()
261 262 263
    nb_document = len(document_server.objectValues())
    self.assertEqual(nb_document, len(self.ids[:self.id_max_text]))
    #binary document
264 265 266
    for id_ in self.ids[self.id_max_text:]:
      reference = "Test-Odt-%s" % (id_, )
      self.createDocument(id=id_, file_name=self.filename_odt, reference=reference)
267 268 269 270
    self.tic()
    nb_document = len(document_server.objectValues())
    self.assertEqual(nb_document, len(self.ids))
    return nb_document
Nicolas Delaby's avatar
Nicolas Delaby committed
271

272
  def createDocumentServerList(self, one_way=False):
273 274 275
    """
    create document in document_server
     """
Aurel's avatar
Aurel committed
276
    self.createDocumentModules(one_way)
277
    document_server = self.getDocumentServer()
278
    document_text = document_server.newContent(id=self.id1,
279
                                               portal_type='Text')
280 281
    kw = {'reference': self.reference1, 'Version': self.version1,
          'Language': self.language1, 'Description': self.description1}
282
    document_text.edit(**kw)
283 284
    file_ = makeFileUpload(self.filename_text)
    document_text.edit(file=file_)
285
    self.tic()
286
    document_pdf = document_server.newContent(id=self.id2,
287
                                              portal_type='PDF')
288 289
    kw = {'reference': self.reference2, 'Version': self.version2,
          'Language': self.language2, 'Description': self.description2}
290
    document_pdf.edit(**kw)
291 292
    file_ = makeFileUpload(self.filename_pdf)
    document_pdf.edit(file=file_)
293
    self.tic()
294
    nb_document = len(document_server)
295 296 297
    self.assertEqual(nb_document, 2)
    return nb_document

298
  def createDocument(self, id, file_name=None, portal_type='Text', # pylint: disable=redefined-builtin
299 300 301 302 303 304 305 306 307
             reference='P-SYNCML.Text', version='001', language='en'):
    """
      Create a text document
    """
    document_server = self.getDocumentServer()
    doc_text = document_server.newContent(id=id, portal_type=portal_type)
    kw = {'reference': reference, 'version': version, 'language': language}
    doc_text.edit(**kw)
    if file_name is not None:
308 309
      file_ = makeFileUpload(file_name)
      doc_text.edit(file=file_)
310 311
    return doc_text

312
  def checkSynchronizationStateIsSynchronized(self):
313 314 315
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    for document in document_server.objectValues():
Aurel's avatar
Aurel committed
316
      state_list = self.getSynchronizationState(document)
317
      for state in state_list:
318
        self.assertEqual(state[1], 'no_conflict')
319 320
    document_client1 = self.getDocumentClient1()
    for document in document_client1.objectValues():
Aurel's avatar
Aurel committed
321
      state_list = self.getSynchronizationState(document)
322
      for state in state_list:
323
        self.assertEqual(state[1], 'no_conflict')
324
    # Check for each signature that the tempXML is None
325 326
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
      for m in sub.contentValues():
327 328
        self.assertEqual(m.getTemporaryData(), None)
        self.assertEqual(m.getPartialData(), None)
329 330 331
    for pub in portal_sync.contentValues(portal_type='SyncML Publication'):
      for sub in pub.contentValues(portal_type='SyncML Subscription'):
        for m in sub.contentValues():
332
          self.assertEqual(m.getPartialData(), None)
333

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
  def checkFirstSynchronization(self, nb_document=0): # pylint: disable=arguments-differ

    portal_sync = self.getSynchronizationTool()
    subscription1 = portal_sync[self.sub_id1]
    self.assertEqual(len(subscription1), nb_document)
    document_server = self.getDocumentServer() # We also check we don't
                                               # modify initial ob
    doc1_s = document_server._getOb(self.id1)
    self.assertEqual(doc1_s.getId(), self.id1)
    self.assertEqual(doc1_s.getReference(), self.reference1)
    self.assertEqual(doc1_s.getVersion(), self.version1)
    self.assertEqual(doc1_s.getLanguage(), self.language1)
    self.assertEqual(doc1_s.getFilename(), self.filename_text)
    self.assertEqual(self.size_filename_text, doc1_s.get_size())
    doc2_s = document_server._getOb(self.id2)
    self.assertEqual(doc2_s.getReference(), self.reference2)
    self.assertEqual(doc2_s.getVersion(), self.version2)
    self.assertEqual(doc2_s.getLanguage(), self.language2)
    self.assertEqual(doc2_s.getFilename(), self.filename_pdf)
    self.assertEqual(self.size_filename_pdf, doc2_s.get_size())
    document_client1 = self.getDocumentClient1()
    document_c = document_client1._getOb(self.id1)
    self.assertEqual(document_c.getId(), self.id1)
    self.assertEqual(document_c.getReference(), self.reference1)
    self.assertEqual(document_c.getVersion(), self.version1)
    self.assertEqual(document_c.getLanguage(), self.language1)
    self.assertEqual(document_c.getFilename(), self.filename_text)
    self.assertEqual(self.size_filename_text, document_c.get_size())
    self.assertXMLViewIsEqual(self.sub_id1, doc1_s, document_c)
    self.assertXMLViewIsEqual(self.sub_id1, doc2_s,
                              document_client1._getOb(self.id2))

  def checkDocument(self, id, document, filename=None, # pylint: disable=redefined-builtin
Aurel's avatar
Aurel committed
367
                    size_filename=None, reference='P-SYNCML.Text',
368 369 370
                    portal_type='Text', version='001', language='en',
                    description=''):
    """
371
      Check synchronization with a document and the information provided
372 373 374 375 376 377 378 379
    """
    if document is not None:
      self.assertEqual(document.getId(), id)
      self.assertEqual(document.getReference(), reference)
      self.assertEqual(document.getVersion(), version)
      self.assertEqual(document.getLanguage(), language)
      self.assertEqual(document.getDescription(), description)
      if filename is not None:
380
        self.assertEqual(document.getFilename(), filename)
381
        self.assertEqual(size_filename, document.get_size())
382
    else:
Aurel's avatar
Aurel committed
383 384
      self.fail("Document is None to check this information")

385 386 387
  def checkXMLsSynchronized(self):
    document_server = self.getDocumentServer()
    document_client1 = self.getDocumentClient1()
388 389 390
    for id_ in self.ids:
      doc_s = document_server._getOb(str(id_))
      doc_c = document_client1._getOb(str(id_))
391 392
      self.assertXMLViewIsEqual(self.sub_id1, doc_s, doc_c)

Nicolas Delaby's avatar
Nicolas Delaby committed
393

394 395
  def checkFirstSynchronizationWithMultiDocument(self, nb_document=0):
    portal_sync = self.getSynchronizationTool()
396
    subscription1 = portal_sync[self.sub_id1]
Aurel's avatar
Aurel committed
397
    self.assertEqual(len(subscription1.getDocumentList()), nb_document)
398
    document_server = self.getDocumentServer()
399 400 401 402
    id_ = str(self.ids[0])
    doc_text_s = document_server._getOb(id_)
    reference = 'Test-Text-%s' % id_
    self.checkDocument(id=id_, document=doc_text_s,
403 404 405
                       reference=reference,
                       filename=self.filename_text,
                       size_filename=self.size_filename_text)
406 407 408 409
    id_ = str(self.ids[self.id_max_text])
    doc_odt_s = document_server._getOb(id_)
    reference = 'Test-Odt-%s' % id_
    self.checkDocument(id=id_, document=doc_odt_s,
410 411 412
                       reference=reference,
                       filename=self.filename_odt,
                       size_filename=self.size_filename_odt)
Aurel's avatar
Aurel committed
413
    self.checkXMLsSynchronized()
Nicolas Delaby's avatar
Nicolas Delaby committed
414 415 416

class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):

417
  def getTitle(self):
Nicolas Delaby's avatar
Nicolas Delaby committed
418
    return "ERP5 Document SyncML"
419

420
  def checkSynchronizationStateIsConflict(self, portal_type='Text'):
421 422 423
    document_server = self.getDocumentServer()
    for document in document_server.objectValues():
      if document.getId()==self.id1:
Aurel's avatar
Aurel committed
424
        state_list = self.getSynchronizationState(document)
425
        for state in state_list:
426
          self.assertEqual(state[1], 'conflict')
427 428 429
    document_client1 = self.getDocumentClient1()
    for document in document_client1.objectValues():
      if document.getId()==self.id1:
Aurel's avatar
Aurel committed
430
        state_list = self.getSynchronizationState(document)
431
        for state in state_list:
432
          self.assertEqual(state[1], 'conflict')
433 434
   # make sure sub object are also in a conflict mode
    document = document_client1._getOb(self.id1)
Aurel's avatar
Aurel committed
435
    state_list = self.getSynchronizationState(document)
436
    for state in state_list:
437
      self.assertEqual(state[1], 'conflict')
438 439


440
  def test_02_FirstSynchronization(self):
441 442
    # We will try to populate the folder document_client1
    # with the data form document_server
443
    nb_document = self.createDocumentServerList()
444
    portal_sync = self.getSynchronizationTool()
445
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
446
      self.assertEqual(sub.getSyncmlAlertCode(), 'two_way')
447

448 449
    # Synchronize the first client
    nb_message1 = self.synchronize(self.sub_id1)
450
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
451
      self.assertEqual(sub.getSyncmlAlertCode(), 'two_way')
452 453 454
    self.assertEqual(nb_message1, self.nb_message_first_synchronization)
    self.checkSynchronizationStateIsSynchronized()
    self.checkFirstSynchronization(nb_document=nb_document)
Nicolas Delaby's avatar
Nicolas Delaby committed
455

Aurel's avatar
Aurel committed
456
  @expectedFailure
457
  def test_03_UpdateSimpleData(self):
458
    # Add two objects
459
    self.test_02_FirstSynchronization()
460 461 462
    # First we do only modification on server
    document_server = self.getDocumentServer()
    document_s = document_server._getOb(self.id1)
Aurel's avatar
Aurel committed
463 464 465
    # We modified GID information so we get
    # - deletion of former document
    # - addition of new document
466 467 468
    kw = {'reference':self.reference3, 'language':self.language3,
    'version':self.version3}
    document_s.edit(**kw)
Aurel's avatar
Aurel committed
469
    self.tic()
470 471 472 473 474 475 476
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    document_client1 = self.getDocumentClient1()
    document_c = document_client1._getOb(self.id1)
    self.assertEqual(document_s.getReference(), self.reference3)
    self.assertEqual(document_s.getLanguage(), self.language3)
    self.assertEqual(document_s.getVersion(), self.version3)
477
    self.assertEqual(document_c.getFilename(), self.filename_text)
478
    self.assertEqual(self.size_filename_text, document_c.get_size())
479 480 481 482
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)
    # Then we do only modification on a client (the gid) of client => add a object
    kw = {'reference':self.reference1,'version':self.version3}
    document_c.edit(**kw)
483 484
    file_ = makeFileUpload(self.filename_odt)
    document_c.edit(file=file_)
485 486 487 488 489 490 491
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    document_s = document_server._getOb(self.id1)
    self.assertEqual(document_s.getReference(), self.reference1)
    self.assertEqual(document_s.getLanguage(), self.language3)
    self.assertEqual(document_s.getVersion(), self.version3)
492
    self.assertEqual(document_c.getFilename(), self.filename_odt)
493
    self.assertEqual(self.size_filename_odt, document_c.get_size())
494 495 496 497 498 499 500 501 502 503 504
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)
    # Then we do only modification the field (useless for the gid)
    # on both the client and the server and of course, on the same object
    kw = {'description':self.description2}
    document_s.edit(**kw)
    kw = {'short_title':self.short_title1}
    document_c.edit(**kw)
    # The gid is modify so the synchronization add a object
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
Nicolas Delaby's avatar
Nicolas Delaby committed
505 506
    document_c = document_client1._getOb(self.id1)
    self.assertEqual(document_c.getDescription(), self.description2)
507
    self.assertEqual(document_s.getShortTitle(), self.short_title1)
Nicolas Delaby's avatar
Nicolas Delaby committed
508
    self.assertEqual(document_s.getBaseData(), document_c.getBaseData())
509 510
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)

511
  def test_04_DeleteObject(self):
512 513 514 515 516
    """
      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
    """
517
    self.test_02_FirstSynchronization()
518 519 520 521 522 523 524 525
    document_server = self.getDocumentServer()
    document_server.manage_delObjects(self.id1)
    document_client1 = self.getDocumentClient1()
    document_client1.manage_delObjects(self.id2)
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    portal_sync = self.getSynchronizationTool()
526 527
    publication = portal_sync[self.pub_id]
    subscription1 = portal_sync[self.sub_id1]
Aurel's avatar
Aurel committed
528 529
    self.assertEqual(len(publication.getDocumentList()), 0)
    self.assertEqual(len(subscription1.getDocumentList()), 0)
530

531
  def test_05_FirstMultiSynchronization(self):
Aurel's avatar
Aurel committed
532
    # Add document on the server and first synchronization for client
533
    nb_document = self.documentMultiServer()
534
    portal_sync = self.getSynchronizationTool()
Aurel's avatar
Aurel committed
535
    self.synchronize(self.sub_id1)
536
    # It has transmitted some object
537
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
538
      self.assertEqual(sub.getSyncmlAlertCode(), 'two_way')
539 540 541
    self.checkSynchronizationStateIsSynchronized()
    self.checkFirstSynchronizationWithMultiDocument(nb_document=nb_document)

Aurel's avatar
Aurel committed
542
  @expectedFailure
543
  def test_06_UpdateMultiData(self):
Aurel's avatar
Aurel committed
544 545 546
    # XXX This tests modify GID of document and so signature
    # get added and removed, due to bad behaviour in conduit, it fails
    # Add various data in server
547
    # modification in client and server for synchronize
548
    self.test_05_FirstMultiSynchronization()
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    # Side server modification gid of a text document
    document_server = self.getDocumentServer()
    document_client1= self.getDocumentClient1()
    id_text = str(self.ids[3])
    doc_s = document_server._getOb(id_text)
    kw = {'reference':self.reference3, 'language':self.language3,
        'version':self.version3, 'description':self.description3}
    doc_s.edit(**kw)
    # Side client modification gid of a odt document
    id_odt = str(self.ids[self.id_max_text+1])
    doc_c = document_client1._getOb(id_odt)
    kw = {'reference':self.reference2, 'language':self.language2,
        'version':self.version2, 'description':self.description2}
    doc_c.edit(**kw)
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    # Check that the datas modified after synchronization
    doc_s = document_server._getOb(id_text)
    self.checkDocument(id=id_text, document=doc_s,\
                       filename=self.filename_text,\
                       size_filename=self.size_filename_text,\
                       reference=self.reference3,\
                       language=self.language3,\
                       version=self.version3,\
                       description = self.description3)
    doc_c = document_client1._getOb(id_odt)
    self.checkDocument(id=id_odt, document=doc_c,\
                       filename=self.filename_odt,\
                       size_filename=self.size_filename_odt,\
                       reference=self.reference2,\
                       language=self.language2,\
                       version=self.version2,\
                       description = self.description2)
    # Others
    doc_c = document_client1._getOb(str(self.ids[2]))
    reference = 'Test-Text-%s' % str(self.ids[2])
    self.checkDocument(id=str(self.ids[2]), document=doc_c,\
                       reference=reference, filename=self.filename_text,\
                       size_filename=self.size_filename_text)
    # Check the XMLs
    self.checkXMLsSynchronized()
    # Replace description and filename
    doc_s = document_server._getOb(id_text)
    kw = {'description':self.description1}
    doc_s.edit(**kw)
594 595
    file_ = makeFileUpload(self.filename_odt)
    doc_s.edit(file=file_)
596 597 598 599 600
    # Side client modification gid of a odt document
    id_odt = str(self.ids[self.id_max_text+1])
    doc_c = document_client1._getOb(id_odt)
    kw = {'description':self.description3}
    doc_c.edit(**kw)
601 602
    file_ = makeFileUpload(self.filename_text)
    doc_c.edit(file=file_)
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    # Check that the datas modified after synchronization
    doc_s = document_server._getOb(id_text)
    self.checkDocument(id=id_text, document=doc_s,\
                       filename=self.filename_odt,\
                       size_filename=self.size_filename_odt,\
                       reference=self.reference3,\
                       language=self.language3,\
                       version=self.version3,\
                       description=self.description1)
    doc_c = document_client1._getOb(id_odt)
    self.checkDocument(id=id_odt, document=doc_c,\
                       filename=self.filename_text,\
                       size_filename=self.size_filename_text,\
                       reference=self.reference2,\
                       language=self.language2,\
                       version=self.version2,\
                       description = self.description3)
    doc_c = document_client1._getOb(str(self.ids[2]))
    reference = 'Test-Text-%s' % str(self.ids[2])
    self.checkDocument(id=str(self.ids[2]), document=doc_c,\
                       reference=reference, filename=self.filename_text,\
                       size_filename=self.size_filename_text)
    # Check the XMLs
    self.checkXMLsSynchronized()

631
  def test_07_SynchronizeWithStrangeIdGenerator(self):
632 633
    """
    By default, the synchronization process use the id in order to
Aurel's avatar
Aurel committed
634
    recognize objects (because by default, getGid==getId. Here, we will see
635 636
    if it also works with a somewhat strange getGid
    """
637
    nb_document = self.createDocumentServerList()
638 639 640 641
    # This will test adding object
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    portal_sync = self.getSynchronizationTool()
642 643 644 645
    subscription1 = portal_sync[self.sub_id1]
    self.assertEqual(len(subscription1), nb_document)
    publication = portal_sync[self.pub_id]
    self.assertEqual(len(publication['1']), nb_document)
646 647
    gid = self.reference1 +  '-' + self.version1 + '-' + self.language1 # ie the title ''
    gid = b16encode(gid)
Aurel's avatar
Aurel committed
648 649
    document_c1 = subscription1.getDocumentFromGid(gid)
    document_s = publication.getSubscriber(self.subscription_url['two_way']).getDocumentFromGid(gid)
650 651 652 653
    id_s = document_s.getId()
    self.assertEqual(id_s, self.id1)
    # This will test updating object
    document_s.setDescription(self.description3)
654
    self.tic()
655 656 657 658 659 660 661 662 663 664 665
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    self.assertEqual(document_s.getDescription(), self.description3)
    self.assertEqual(document_c1.getDescription(), self.description3)
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c1)
    # This will test deleting object
    document_server = self.getDocumentServer()
    document_server.manage_delObjects(self.id2)
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
Aurel's avatar
Aurel committed
666 667 668
    self.assertEqual(len(subscription1.getDocumentList()), (nb_document-1))
    self.assertEqual(len(publication.getDocumentList()), (nb_document-1))
    document_s = publication.getSubscriber(self.subscription_url['two_way']).getDocumentFromGid(gid)
669 670
    id_s = document_s.getId()
    self.assertEqual(id_s, self.id1)
Aurel's avatar
Aurel committed
671
    document_c1 = subscription1.getDocumentFromGid(gid)
672 673 674
    self.assertEqual(document_s.getDescription(), self.description3)
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c1)

Aurel's avatar
Aurel committed
675
  @expectedFailure
676
  def test_08_MultiNodeConflict(self):
677 678 679 680
    """
    We will create conflicts with 3 differents nodes, and we will
    solve it by taking one full version of documents.
    """
Aurel's avatar
Aurel committed
681 682 683 684 685
    # XXX-Aurel Note that this test does no do what it describes !
    # Only conflict between one client and one server is done
    # so this is node multinodes at all !!

    # Do a first synchronization
686
    self.test_02_FirstSynchronization()
Aurel's avatar
Aurel committed
687 688 689 690
    # Then modify data on both side to generate conflicts on different
    # properties of the same document

    # Modify on server side
691 692
    document_server = self.getDocumentServer()
    document_s = document_server._getOb(self.id1)
Aurel's avatar
Aurel committed
693
    kw = {'description': self.description2, 'short_title': self.short_title2 }
694
    document_s.edit(**kw)
695 696
    file_ = makeFileUpload(self.filename_ppt)
    document_s.edit(file=file_)
697
    self.tic()
Aurel's avatar
Aurel committed
698 699

    # Modify on client side
700 701
    document_client1 = self.getDocumentClient1()
    document_c1 = document_client1._getOb(self.id1)
Aurel's avatar
Aurel committed
702
    kw = {'description': self.description3, 'short_title': self.short_title3 }
703
    document_c1.edit(**kw)
704 705
    file_ = makeFileUpload(self.filename_odt)
    document_c1.edit(file=file_)
706
    self.tic()
Aurel's avatar
Aurel committed
707

708
    self.synchronize(self.sub_id1)
Aurel's avatar
Aurel committed
709 710 711
    # Check conflicts generated
    conflict_list = self.getSynchronizationTool().getConflictList()
    self.assertEqual(len(conflict_list), 8)
712
    self.assertEqual(sorted([x.getPropertyId() for x in conflict_list]),
Aurel's avatar
Aurel committed
713 714
                     ['content_md5', 'content_type',
                      'data', 'description', 'filename', 'short_title',
Nicolas Delaby's avatar
Nicolas Delaby committed
715
                      'size', 'title'])
716 717
    # check if we have the state conflict on all clients
    self.checkSynchronizationStateIsConflict()
Aurel's avatar
Aurel committed
718 719
    # Fix conflict :
    # apply description & file property on document_server
720
    # short_title on document_client1
Aurel's avatar
Aurel committed
721
    for conflict in conflict_list :
722
      subscriber = conflict.getSubscriber()
Aurel's avatar
Aurel committed
723 724 725 726 727 728 729 730 731 732 733
      property_id = conflict.getPropertyId()
      if property_id == 'description' and \
          subscriber.getUrlString() == self.publication_url:
        conflict.applySubscriberValue()
        continue
      if property_id == 'short_title' and \
          subscriber.getUrlString() == self.subscription_url['two_way']:
        conflict.applySubscriberValue()
        continue
      conflict.applyPublisherValue()

734 735 736 737
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    self.assertEqual(document_c1.getDescription(), self.description2)
    self.assertEqual(document_c1.getShortTitle(), self.short_title3)
738
    self.assertEqual(document_c1.getFilename(), self.filename_ppt)
739
    #XXX Error in convert XML
740
    #self.assertEqual(self.size_filename_text, document_c1.get_size())
741
    document_s = document_server._getOb(self.id1)
742 743
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c1,
                              ignore_processing_status_workflow=True)
744

Aurel's avatar
Aurel committed
745
  @expectedFailure
746
  def test_10_BrokenMessage(self):
747 748 749 750
    """
    With http synchronization, when a message is not well
    received, then we send message again, we want to
    be sure that is such case we don't do stupid things
Nicolas Delaby's avatar
Nicolas Delaby committed
751

752 753 754
    If we want to make this test more intersting, it is
    better to split messages
    """
Aurel's avatar
Aurel committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
    previous_max_lines = SyncMLSubscription.MAX_LEN
    try:
      SynchronizationTool.MAX_LEN = 1 << 8
      nb_document = self.createDocumentServerList()
      # Synchronize the first client
      self.synchronizeWithBrokenMessage(self.sub_id1)

      portal_sync = self.getSynchronizationTool()
      subscription1 = portal_sync[self.sub_id1]
      self.assertEqual(len(subscription1.getDocumentList()), nb_document)
      document_server = self.getDocumentServer() # We also check we don't
                                             # modify initial ob
      document_s = document_server._getOb(self.id1)
      document_client1 = self.getDocumentClient1()
      document_c = document_client1._getOb(self.id1)
      self.assertEqual(document_s.getId(), self.id1)
      self.assertEqual(document_s.getReference(), self.reference1)
      self.assertEqual(document_s.getLanguage(), self.language1)
      self.assertEqual(document_s.getFilename(), self.filename_text)
774
      self.assertEqual(self.size_filename_text, document_c.get_size())
Aurel's avatar
Aurel committed
775 776 777
      self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)
    finally:
      SyncMLSubscription.MAX_LEN = previous_max_lines
778

779
  def addOneWaySyncFromServerSubscription(self):
780
    portal_sync = self.getSynchronizationTool()
781 782 783 784 785 786 787 788 789 790
    portal_sync.newContent(portal_type='SyncML Subscription',
                  id=self.sub_id_from_server,
                  url_string=self.publication_url,
                  subscription_url_string=self.subscription_url['from_server'],
                  source='document_client_from_server',
                  source_reference='DocumentSubscription',
                  destination_reference='Document',
                  list_method_id='objectValues',
                  xml_binding_generator_method_id=self.xml_mapping,
                  conduit_module_id='ERP5DocumentConduit',
Aurel's avatar
Aurel committed
791
                  is_activity_enabled=self.activity_enable,
792 793 794
                  syncml_alert_code='one_way_from_server',
                  user_id='daniele',
                  password='myPassword')
795

796
  def test_12_OneWaySyncFromServer(self):
797 798 799 800 801
    """
    We will test if we can synchronize only from to server to the client.
    We want to make sure in this case that all modifications on the client
    will not be taken into account.
    """
802 803
    self.addOneWaySyncFromServerSubscription()
    nb_document = self.createDocumentServerList(one_way=True)
804
    portal_sync = self.getSynchronizationTool()
805
    sub_from_server = portal_sync[self.sub_id_from_server]
806
    self.assertEqual(sub_from_server.getSyncmlAlertCode(), 'one_way_from_server')
807 808
    # First do the sync from the server to the client
    nb_message1 = self.synchronize(self.sub_id_from_server)
809
    sub_from_server = portal_sync[self.sub_id_from_server]
810 811 812
    self.assertEqual(sub_from_server.getSyncmlAlertCode(), 'one_way_from_server')
    self.assertEqual(nb_message1, self.nb_message_first_synchronization)
    self.assertEqual(len(sub_from_server.getDocumentList()), nb_document)
813 814 815 816 817 818 819 820 821 822 823 824
    document_server = self.getDocumentServer() # We also check we don't
                                           # modify initial ob
    document_s = document_server._getOb(self.id1)
    document_client1 = self.getDocumentClientFromServer()
    document_c = document_client1._getOb(self.id1)
    self.assertEqual(document_s.getId(), self.id1)
    self.assertEqual(document_s.getReference(), self.reference1)
    self.assertEqual(document_s.getLanguage(), self.language1)
    self.checkSynchronizationStateIsSynchronized()
    self.assertXMLViewIsEqual(self.sub_id_from_server, document_s, document_c, force=1)
    # Then we change things on both sides and we look if there
    # is synchronization from only one way
825 826
    file_ = makeFileUpload(self.filename_odt)
    document_c.edit(file=file_)
827

Aurel's avatar
Aurel committed
828
    kw = {'short_title' : self.short_title2}
829 830
    document_s.edit(**kw)
    self.tic()
831
    self.assertEqual(document_s.getFilename(), self.filename_text)
832
    self.assertEqual(self.size_filename_text, document_s.get_size())
833 834 835
    nb_message1 = self.synchronize(self.sub_id_from_server)
    #In One_From_Server Sync not modify the first_name in client because any
    #datas client sent
836
    self.assertEqual(document_c.getFilename(), self.filename_odt)
837 838
    self.assertEqual(self.size_filename_odt, document_c.get_size())
    self.assertEqual(document_c.getShortTitle(), self.short_title2)
839
    self.assertEqual(document_s.getFilename(), self.filename_text)
840 841
    self.assertEqual(self.size_filename_text, document_s.get_size())
    self.assertEqual(document_s.getShortTitle(), self.short_title2)
Nicolas Delaby's avatar
Nicolas Delaby committed
842

843 844 845 846
    #reset for refresh sync
    #after synchronize, the client object retrieve value of server
    self.resetSignaturePublicationAndSubscription()
    nb_message1 = self.synchronize(self.sub_id_from_server)
847
    self.assertEqual(document_c.getFilename(), self.filename_text)
848 849
    self.assertEqual(self.size_filename_text, document_c.get_size())
    self.assertEqual(document_c.getShortTitle(), self.short_title2)
850 851 852
    self.checkSynchronizationStateIsSynchronized()
    document_s = document_server._getOb(self.id1)
    document_c = document_client1._getOb(self.id1)
Aurel's avatar
Aurel committed
853
    # Ignore processing status workflow as
854 855
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c, force=True,
                              ignore_processing_status_workflow=True)
856 857

def test_suite():
858 859 860
  suite = unittest.TestSuite()
  suite.addTest(unittest.makeSuite(TestERP5DocumentSyncML))
  return suite