testERP5DocumentSyncML.py 41.6 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 31 32
# -*- 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
import unittest
from Testing import ZopeTestCase
33 34 35
from Products.ERP5Type.tests.runUnitTest import tests_home
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase,\
                                                       _getConversionServerDict
36 37 38
from AccessControl.SecurityManagement import newSecurityManager
from Products.ERP5SyncML.Conduit.ERP5DocumentConduit import ERP5DocumentConduit
from zLOG import LOG
39
from base64 import b16encode 
40 41
from lxml import etree
from Products.ERP5Type.tests.utils import FileUpload
42 43 44
from Products.ERP5SyncML import SyncMLConstant
from Products.ERP5SyncML.Tool import SynchronizationTool
from Products.ERP5SyncML.tests.testERP5SyncML import TestERP5SyncMLMixin
45 46

test_files = os.path.join(os.path.dirname(__file__), 'test_document')
Nicolas Delaby's avatar
Nicolas Delaby committed
47
FILENAME_REGULAR_EXPRESSION = "(?P<reference>[A-Z]{3,10})-\
48 49 50
(?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}))?"
51 52 53 54 55

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

56
class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
57

58
  nb_objects = 10
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 101 102 103
  #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'
  activity_enabled = True
104 105 106
  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}
107 108
  #for this tests
  nb_message_first_synchronization = 12
109
  nb_message_multi_first_synchronization = 14
110 111 112 113 114
  nb_synchronization = 2
  nb_subscription = 1
  nb_publication = 1
  #default edit_workflow
  workflow_id = 'processing_status_workflow'
Nicolas Delaby's avatar
Nicolas Delaby committed
115

116 117 118 119 120

  def getBusinessTemplateList(self):
    """
      Return the list of business templates.
    """
Nicolas Delaby's avatar
Nicolas Delaby committed
121
    return ('erp5_base',
122
            'erp5_syncml',
Nicolas Delaby's avatar
Nicolas Delaby committed
123 124 125 126 127
            'erp5_ingestion',
            'erp5_ingestion_mysql_innodb_catalog',
            'erp5_web',
            'erp5_dms',
            )
128 129 130 131 132 133 134 135 136 137


  def afterSetUp(self):
    """Setup."""
    self.login()
    self.addPublications()
    self.addSubscriptions()
    self.portal = self.getPortal()
    self.setSystemPreferences()
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
138

139 140 141 142 143 144 145 146 147 148 149
  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()

  def setSystemPreferences(self):
    default_pref = self.portal.portal_preferences.default_site_preference
150 151 152
    conversion_dict = _getConversionServerDict()
    default_pref.setPreferredOoodocServerAddress(conversion_dict['hostname'])
    default_pref.setPreferredOoodocServerPortNumber(conversion_dict['port'])
153
    default_pref.setPreferredDocumentFileNameRegularExpression(FILENAME_REGULAR_EXPRESSION)
154 155 156 157 158 159 160
    default_pref.setPreferredDocumentReferenceRegularExpression(REFERENCE_REGULAR_EXPRESSION)
    if default_pref.getPreferenceState() == 'disabled':
      default_pref.enable()

  def addSubscriptions(self):
    portal_id = self.getPortalId()
    portal_sync = self.getSynchronizationTool()
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    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',
                      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, 
                      sync_alert_code='two_way',
                      is_activity_enabled=True,
                      user_id='daniele',
                      password='myPassword')
      subscription.validate()
      self.tic()
178 179 180 181

  def addPublications(self):
    portal_id = self.getPortalName()
    portal_sync = self.getSynchronizationTool()
182 183 184 185 186 187 188 189 190 191 192 193
    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,
                             is_activity_enabled=True,)
      publication.validate()
      self.tic()
194

195
  def createDocumentModules(self, one_way=False):
196 197 198 199 200 201 202 203 204
    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
205

206
    if one_way:
207 208 209 210 211
      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):
212
    if getattr(self.portal, 'document_server', None) is not None:
213
      self.portal._delObject(id='document_server')
214
    if getattr(self.portal, 'document_client1', None) is not None:
215 216
      self.portal._delObject(id='document_client1')
    self.tic()
Nicolas Delaby's avatar
Nicolas Delaby committed
217

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

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

  def getSynchronizationTool(self):
229
    return getattr(self.portal, 'portal_synchronizations')
230 231

  def getDocumentClient1(self):
232
    return getattr(self.portal, 'document_client1')
233 234

  def getDocumentClientFromServer(self):
235 236
    return getattr(self.portal, 'document_client_from_server')
  
237
  def getDocumentServer(self):
238
    return getattr(self.portal, 'document_server')
239 240 241 242

  def getPortalId(self):
    return self.portal.getId()

243
  def login(self):
244 245 246 247 248 249
    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
250

251 252
  def resetSignaturePublicationAndSubscription(self):
    portal_sync = self.getSynchronizationTool()
253 254 255 256 257
    publication = portal_sync[self.pub_id]
    subscription1 = portal_sync[self.sub_id1]
    publication.resetSubscriberList()
    subscription1.resetSignatureList()
    subscription1.resetAnchorList()
258 259
    self.tic()

260
  def documentMultiServer(self):
261 262 263 264 265 266 267 268
    # create different document by category documents
    self.createDocumentModules()
    document_id = ''
    document_server = self.getDocumentServer()
    #plain text document
    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)
269
    self.commit()
270 271 272 273 274 275 276 277 278 279
    nb_document = len(document_server.objectValues())
    self.assertEqual(nb_document, len(self.ids[:self.id_max_text]))
    #binary document
    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)
    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
280

281
  def createDocumentServerList(self, one_way=False):
282 283 284
    """
    create document in document_server
     """
285
    self.createDocumentModules(one_way) 
286 287 288 289
    document_id = ''
    document_server = self.getDocumentServer()
    if getattr(document_server, self.id1, None) is not None:
      self.clearDocumentModules()
290
    document_text = document_server.newContent(id=self.id1,
291
                                               portal_type='Text')
292 293
    kw = {'reference': self.reference1, 'Version': self.version1,
          'Language': self.language1, 'Description': self.description1}
294 295 296 297
    document_text.edit(**kw)
    file = makeFileUpload(self.filename_text)
    document_text.edit(file=file)
    self.tic()
298
    document_pdf = document_server.newContent(id=self.id2,
299
                                              portal_type='PDF')
300 301
    kw = {'reference': self.reference2, 'Version': self.version2,
          'Language': self.language2, 'Description': self.description2}
302 303 304 305
    document_pdf.edit(**kw)
    file = makeFileUpload(self.filename_pdf)
    document_pdf.edit(file=file)
    self.tic()
306
    nb_document = len(document_server)
307 308 309 310 311 312 313 314 315 316
    self.assertEqual(nb_document, 2)
    return nb_document

  def createDocument(self, id, file_name=None, portal_type='Text',
             reference='P-SYNCML.Text', version='001', language='en'):
    """
      Create a text document
    """
    document_server = self.getDocumentServer()
    if getattr(document_server, str(id), None) is not None:
Nicolas Dumazet's avatar
Nicolas Dumazet committed
317
      self.clearDocumentModules()
318 319 320 321 322 323 324 325
    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:
      file = makeFileUpload(file_name)
      doc_text.edit(file=file)
    return doc_text

326
  def synchronize(self, id):
327 328 329 330 331
    """
    This just define how we synchronize, we have
    to define it here because it is specific to the unit testing
    """
    portal_sync = self.getSynchronizationTool()
332
    subscription = portal_sync[id]
333
    publication = None
334 335 336 337
    for pub in portal_sync.searchFolder(portal_type='SyncML Publication',
                       source_reference=subscription.getDestinationReference(),
                       validation_state='validated'):
      if pub.getUrlString() == subscription.getUrlString():
338 339 340
        publication = pub
    self.assertTrue(publication is not None)
    # reset files, because we do sync by files
341
    file = open(subscription.getSubscriptionUrlString()[len('file:/'):], 'w')
342 343 344 345 346 347 348 349
    file.write('')
    file.close()
    file = open(self.publication_url[len('file:/'):], 'w')
    file.write('')
    file.close()
    self.tic()
    nb_message = 1
    result = portal_sync.SubSync(subscription.getPath())
350
    while result['has_response']:
351 352 353 354 355 356 357
      portal_sync.PubSync(publication.getPath())
      if self.activity_enabled:
        self.tic()
      result = portal_sync.SubSync(subscription.getPath())
      if self.activity_enabled:
        self.tic()
      nb_message += 1 + result['has_response']
358
    self.tic()
359 360
    return nb_message

361
  def synchronizeWithBrokenMessage(self, id):
362 363 364 365 366 367
    """
    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.email = None # XXX To be removed
368
    subscription = portal_sync[id]
369
    publication = None
370 371 372 373 374
    for pub in portal_sync.searchFolder(portal_type='SyncML Publication',
                       source_reference=subscription.getDestinationReference(),
                       validation_state='validated'):
      if pub.getUrlString() == subscription.getUrlString():
        publication = pub
375 376
    self.assertTrue(publication is not None)
    # reset files, because we do sync by files
377
    file = open(subscription.getSubscriptionUrlString()[len('file:/'):], 'w')
378 379 380 381 382 383 384 385
    file.write('')
    file.close()
    file = open(self.publication_url[len('file:/'):], 'w')
    file.write('')
    file.close()
    self.tic()
    nb_message = 1
    result = portal_sync.SubSync(subscription.getPath())
386
    while result['has_response']:
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
      # We do thing three times, so that we will test
      # if we manage well duplicate messages
      portal_sync.PubSync(publication.getPath())
      if self.activity_enabled:
        self.tic()
      portal_sync.PubSync(publication.getPath())
      if self.activity_enabled:
        self.tic()
      portal_sync.PubSync(publication.getPath())
      if self.activity_enabled:
        self.tic()
      result = portal_sync.SubSync(subscription.getPath())
      if self.activity_enabled:
        self.tic()
      result = portal_sync.SubSync(subscription.getPath())
      if self.activity_enabled:
        self.tic()
      result = portal_sync.SubSync(subscription.getPath())
      if self.activity_enabled:
        self.tic()
      nb_message += 1 + result['has_response']
408
    self.tic()
409 410
    return nb_message

411
  def checkSynchronizationStateIsSynchronized(self):
412 413 414 415 416
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    for document in document_server.objectValues():
      state_list = portal_sync.getSynchronizationState(document)
      for state in state_list:
417
        self.assertEqual(state[1], 'synchronized')
418 419 420 421
    document_client1 = self.getDocumentClient1()
    for document in document_client1.objectValues():
      state_list = portal_sync.getSynchronizationState(document)
      for state in state_list:
422
        self.assertEqual(state[1], 'synchronized')
423
    # Check for each signature that the tempXML is None
424 425 426 427 428 429 430 431
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
      for m in sub.contentValues():
        self.assertEquals(m.getTemporaryData(), None)
        self.assertEquals(m.getPartialData(), None)
    for pub in portal_sync.contentValues(portal_type='SyncML Publication'):
      for sub in pub.contentValues(portal_type='SyncML Subscription'):
        for m in sub.contentValues():
          self.assertEquals(m.getPartialData(), None)
432 433 434 435

  def checkFirstSynchronization(self, nb_document=0):

   portal_sync = self.getSynchronizationTool()
436 437
   subscription1 = portal_sync[self.sub_id1]
   self.assertEqual(len(subscription1), nb_document)
438 439 440 441 442 443 444
   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)
445
   self.assertEqual(doc1_s.getFilename(), self.filename_text)
446 447 448 449 450
   self.assertEquals(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)
451
   self.assertEqual(doc2_s.getFilename(), self.filename_pdf)
452 453 454 455 456 457 458
   self.assertEquals(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)
459
   self.assertEqual(document_c.getFilename(), self.filename_text)
460 461
   self.assertEquals(self.size_filename_text, document_c.get_size())
   self.assertXMLViewIsEqual(self.sub_id1, doc1_s, document_c)
462 463
   self.assertXMLViewIsEqual(self.sub_id1, doc2_s,
                             document_client1._getOb(self.id2))
464 465

  def checkDocument(self, id=id, document=None, filename=None,
466
                    size_filename=None, reference='P-SYNCML.Text', 
467 468 469 470 471 472 473 474 475 476 477 478
                    portal_type='Text', version='001', language='en',
                    description=''):
    """
      Check synchronization with a document and the informations provided
    """
    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:
479
        self.assertEqual(document.getFilename(), filename)
480 481 482
        self.assertEquals(size_filename, document.get_size())
    else:
      self.fail("Document is None for check these informations")
483
 
484 485 486 487 488 489 490 491
  def checkXMLsSynchronized(self):
    document_server = self.getDocumentServer()
    document_client1 = self.getDocumentClient1()
    for id in self.ids:
      doc_s = document_server._getOb(str(id))
      doc_c = document_client1._getOb(str(id))
      self.assertXMLViewIsEqual(self.sub_id1, doc_s, doc_c)

Nicolas Delaby's avatar
Nicolas Delaby committed
492

493 494
  def checkFirstSynchronizationWithMultiDocument(self, nb_document=0):
    portal_sync = self.getSynchronizationTool()
495
    subscription1 = portal_sync[self.sub_id1]
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
    self.assertEqual(len(subscription1.getObjectList()), nb_document)
    document_server = self.getDocumentServer()
    document_client1 = self.getDocumentClient1()
    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,
                       reference=reference,
                       filename=self.filename_text,
                       size_filename=self.size_filename_text)
    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,
                       reference=reference,
                       filename=self.filename_odt,
                       size_filename=self.size_filename_odt)
513
    self.checkXMLsSynchronized() 
Nicolas Delaby's avatar
Nicolas Delaby committed
514 515 516

class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):

517 518 519
  def getTitle(self):
    """
    """
Nicolas Delaby's avatar
Nicolas Delaby committed
520
    return "ERP5 Document SyncML"
521

522
  def setupPublicationAndSubscriptionIdGenerator(self):
523
    portal_sync = self.getSynchronizationTool()
524 525 526 527
    sub1 = portal_sync[self.sub_id1]
    pub = portal_sync[self.pub_id]
    pub.setSynchronizationIdGeneratorMethodId('generateNewId')
    sub1.setSynchronizationIdGeneratorMethodId('generateNewId')
528

529
  def checkSynchronizationStateIsConflict(self, portal_type='Text'):
530 531 532 533 534 535
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    for document in document_server.objectValues():
      if document.getId()==self.id1:
        state_list = portal_sync.getSynchronizationState(document)
        for state in state_list:
536
          self.assertEqual(state[1], 'conflict')
537 538 539 540 541
    document_client1 = self.getDocumentClient1()
    for document in document_client1.objectValues():
      if document.getId()==self.id1:
        state_list = portal_sync.getSynchronizationState(document)
        for state in state_list:
542
          self.assertEqual(state[1], 'conflict')
543 544 545 546
   # make sure sub object are also in a conflict mode
    document = document_client1._getOb(self.id1)
    state_list = portal_sync.getSynchronizationState(document)
    for state in state_list:
547
      self.assertEqual(state[1], 'conflict')
548

549
  def test_01_GetSynchronizationList(self):
550 551 552 553
    # This test the getSynchronizationList, ie,
    # We want to see if we retrieve both the subscription
    # and the publication
    portal_sync = self.getSynchronizationTool()
554
    synchronization_list = portal_sync.contentValues()
555 556
    self.assertEqual(len(synchronization_list), self.nb_synchronization)

557
  def test_02_FirstSynchronization(self):
558 559
    # We will try to populate the folder document_client1
    # with the data form document_server
560
    nb_document = self.createDocumentServerList()
561
    portal_sync = self.getSynchronizationTool()
562 563 564
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
      self.assertEquals(sub.getSyncmlAlertCode(), 'two_way')

565 566
    # Synchronize the first client
    nb_message1 = self.synchronize(self.sub_id1)
567 568
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
      self.assertEquals(sub.getSyncmlAlertCode(), 'two_way')
569 570 571
    self.assertEqual(nb_message1, self.nb_message_first_synchronization)
    self.checkSynchronizationStateIsSynchronized()
    self.checkFirstSynchronization(nb_document=nb_document)
Nicolas Delaby's avatar
Nicolas Delaby committed
572

573
  def test_03_UpdateSimpleData(self):
574
    # Add two objects
575
    self.test_02_FirstSynchronization()
576 577 578 579 580 581 582 583 584 585 586 587 588 589
    # First we do only modification on server
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    document_s = document_server._getOb(self.id1)
    kw = {'reference':self.reference3, 'language':self.language3,
    'version':self.version3}
    document_s.edit(**kw)
    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)
590
    self.assertEqual(document_c.getFilename(), self.filename_text)
591 592 593 594 595 596 597 598 599 600 601 602 603 604
    self.assertEquals(self.size_filename_text, document_c.get_size())
    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)
    file = makeFileUpload(self.filename_odt)
    document_c.edit(file=file)
    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)
605
    self.assertEqual(document_c.getFilename(), self.filename_odt)
606 607 608 609 610 611 612 613 614 615 616 617
    self.assertEquals(self.size_filename_odt, document_c.get_size())
    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
618 619
    document_c = document_client1._getOb(self.id1)
    self.assertEqual(document_c.getDescription(), self.description2)
620
    self.assertEqual(document_s.getShortTitle(), self.short_title1)
Nicolas Delaby's avatar
Nicolas Delaby committed
621
    self.assertEqual(document_s.getBaseData(), document_c.getBaseData())
622 623
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)

624
  def test_04_DeleteObject(self):
625 626 627 628 629
    """
      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
    """
630
    self.test_02_FirstSynchronization()
631 632 633 634 635 636 637 638
    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()
639 640
    publication = portal_sync[self.pub_id]
    subscription1 = portal_sync[self.sub_id1]
641 642 643
    self.assertEqual(len(publication.getObjectList()), 0)
    self.assertEqual(len(subscription1.getObjectList()), 0)

644
  def test_05_FirstMultiSynchronization(self):
645
    #Add document on the server and first synchronization for client
646
    nb_document = self.documentMultiServer()
647 648 649 650 651 652
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    document_client = self.getDocumentClient1()
    nb_message1 = self.synchronize(self.sub_id1)
    self.assertNotEqual(nb_message1, 6)
    # It has transmitted some object
653 654
    for sub in portal_sync.contentValues(portal_type='SyncML Subscription'):
      self.assertEquals(sub.getSyncmlAlertCode(), 'two_way')
655 656 657
    self.checkSynchronizationStateIsSynchronized()
    self.checkFirstSynchronizationWithMultiDocument(nb_document=nb_document)

658 659
  def test_06_UpdateMultiData(self):
    # Add various data in server 
660
    # modification in client and server for synchronize
661
    self.test_05_FirstMultiSynchronization()
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
    # 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)
    file = makeFileUpload(self.filename_odt)
    doc_s.edit(file=file)
    # 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)
    file = makeFileUpload(self.filename_text)
    doc_c.edit(file=file)
    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()

744
  def test_07_SynchronizeWithStrangeIdGenerator(self):
745 746
    """
    By default, the synchronization process use the id in order to
747
    recognize objects (because by default, getGid==getId. Here, we will see 
748 749
    if it also works with a somewhat strange getGid
    """
750 751
    self.setupPublicationAndSubscriptionIdGenerator()
    nb_document = self.createDocumentServerList()
752 753 754 755
    # This will test adding object
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    portal_sync = self.getSynchronizationTool()
756 757 758 759
    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)
760 761 762 763 764 765 766 767 768 769
    gid = self.reference1 +  '-' + self.version1 + '-' + self.language1 # ie the title ''
    gid = b16encode(gid)
    document_c1 = subscription1.getObjectFromGid(gid)
    id_c1 = document_c1.getId()
    self.assertTrue(id_c1 in ('1', '2')) # id given by the default generateNewId
    document_s = publication.getSubscriber(self.subscription_url['two_way']).getObjectFromGid(gid)
    id_s = document_s.getId()
    self.assertEqual(id_s, self.id1)
    # This will test updating object
    document_s.setDescription(self.description3)
770
    self.tic()
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
    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_client1 = self.getDocumentClient1()
    document_server.manage_delObjects(self.id2)
    self.tic()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    self.assertEqual(len(subscription1.getObjectList()), (nb_document-1))
    self.assertEqual(len(publication.getObjectList()), (nb_document-1))
    document_s = publication.getSubscriber(self.subscription_url['two_way']).getObjectFromGid(gid)
    id_s = document_s.getId()
    self.assertEqual(id_s, self.id1)
    document_c1 = subscription1.getObjectFromGid(gid)
    id_c1 = document_c1.getId()
    self.assertTrue(id_c1 in ('1', '2')) # id given by the default generateNewId
    self.assertEqual(document_s.getDescription(), self.description3)
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c1)

794
  def test_08_MultiNodeConflict(self):
795 796 797 798 799
    """
    We will create conflicts with 3 differents nodes, and we will
    solve it by taking one full version of documents.
    """
    #not conflict because is gid
800
    self.test_02_FirstSynchronization()
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
    portal_sync = self.getSynchronizationTool()
    document_server = self.getDocumentServer()
    document_s = document_server._getOb(self.id1)
    kw = {'description':self.description2, 'short_title':self.short_title2}
    document_s.edit(**kw)
    file = makeFileUpload(self.filename_ppt)
    # XXX error with filename_pdf , may be is a PDF?
    document_s.edit(file=file)
    self.tic()
    document_client1 = self.getDocumentClient1()
    document_c1 = document_client1._getOb(self.id1)
    kw = {'description':self.description3, 'short_title':self.short_title3}
    document_c1.edit(**kw)
    file = makeFileUpload(self.filename_odt)
    document_c1.edit(file=file)
816

817 818 819
    self.tic()
    self.synchronize(self.sub_id1)
    conflict_list = portal_sync.getConflictList()
820
    self.assertEqual(len(conflict_list), 9)
821
    self.assertEqual(sorted([x.getPropertyId() for x in conflict_list]),
822
                     ['base_data', 'content_md5', 'content_type',
823
                      'data', 'description', 'filename', 'short_title', 
Nicolas Delaby's avatar
Nicolas Delaby committed
824
                      'size', 'title'])
825 826 827 828 829
    # check if we have the state conflict on all clients
    self.checkSynchronizationStateIsConflict()
    # we will take :
    # description et file on document_server
    # short_title on document_client1
830
    for conflict in conflict_list : 
831 832
      subscriber = conflict.getSubscriber()
      property = conflict.getPropertyId()
833
      resolved = False
834
      if property == 'description':
835
        if subscriber.getSubscriptionUrlString() == self.publication_url:
836
          resolved = True
837 838
          conflict.applySubscriberValue()
      if property == 'short_title':
839
        if subscriber.getSubscriptionUrlString() == self.subscription_url['two_way']:
840
          resolved = True
841
          conflict.applySubscriberValue()
842
      if not resolved:
843 844 845 846 847
        conflict.applyPublisherValue()
    self.synchronize(self.sub_id1)
    self.checkSynchronizationStateIsSynchronized()
    self.assertEqual(document_c1.getDescription(), self.description2)
    self.assertEqual(document_c1.getShortTitle(), self.short_title3)
848
    self.assertEqual(document_c1.getFilename(), self.filename_ppt)
849 850
    #XXX Error in convert XML
    #self.assertEquals(self.size_filename_text, document_c1.get_size())
851 852
    document_s = document_server._getOb(self.id1)
    document_c = document_client1._getOb(self.id1)
853 854
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c1,
                              ignore_processing_status_workflow=True)
855

856
  def test_10_BrokenMessage(self):
857 858 859 860
    """
    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
861

862 863 864
    If we want to make this test more intersting, it is
    better to split messages
    """
865 866 867
    previous_max_lines = SynchronizationTool.MAX_LEN
    SynchronizationTool.MAX_LEN = 1 << 8
    nb_document = self.createDocumentServerList()
868 869
    # Synchronize the first client
    nb_message1 = self.synchronizeWithBrokenMessage(self.sub_id1)
870

871
    portal_sync = self.getSynchronizationTool()
872
    subscription1 = portal_sync[self.sub_id1]
873 874 875 876 877 878 879 880 881
    self.assertEqual(len(subscription1.getObjectList()), 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)
882
    self.assertEqual(document_s.getFilename(), self.filename_text)
883 884
    self.assertEquals(self.size_filename_text, document_c.get_size())
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c)
885
    SynchronizationTool.MAX_LEN = previous_max_lines
886

887
  def addOneWaySyncFromServerSubscription(self):
888 889
    portal_id = self.getPortalId()
    portal_sync = self.getSynchronizationTool()
890 891 892 893 894 895 896 897 898 899 900 901 902 903
    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',
                  is_activity_enabled=True,
                  syncml_alert_code='one_way_from_server',
                  user_id='daniele',
                  password='myPassword')
904

905
  def test_12_OneWaySyncFromServer(self):
906 907 908 909 910
    """
    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.
    """
911 912
    self.addOneWaySyncFromServerSubscription()
    nb_document = self.createDocumentServerList(one_way=True)
913
    portal_sync = self.getSynchronizationTool()
914 915
    sub_from_server = portal_sync[self.sub_id_from_server]
    self.assertEquals(sub_from_server.getSyncmlAlertCode(), 'one_way_from_server')
916 917
    # First do the sync from the server to the client
    nb_message1 = self.synchronize(self.sub_id_from_server)
918 919
    sub_from_server = portal_sync[self.sub_id_from_server]
    self.assertEquals(sub_from_server.getSyncmlAlertCode(), 'one_way_from_server')
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
    self.assertEquals(nb_message1, self.nb_message_first_synchronization)
    self.assertEquals(len(sub_from_server.getObjectList()), 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.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
    file = makeFileUpload(self.filename_odt)
    document_c.edit(file=file)
936 937

    kw = {'short_title' : self.short_title2} 
938 939
    document_s.edit(**kw)
    self.tic()
940
    self.assertEqual(document_s.getFilename(), self.filename_text)
941 942 943 944
    self.assertEquals(self.size_filename_text, document_s.get_size())
    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
945
    self.assertEqual(document_c.getFilename(), self.filename_odt)
946 947
    self.assertEquals(self.size_filename_odt, document_c.get_size())
    self.assertEquals(document_c.getShortTitle(), self.short_title2)
948
    self.assertEqual(document_s.getFilename(), self.filename_text)
949
    self.assertEquals(self.size_filename_text, document_s.get_size())
950
    self.assertEquals(document_s.getShortTitle(), self.short_title2) 
Nicolas Delaby's avatar
Nicolas Delaby committed
951

952 953 954 955
    #reset for refresh sync
    #after synchronize, the client object retrieve value of server
    self.resetSignaturePublicationAndSubscription()
    nb_message1 = self.synchronize(self.sub_id_from_server)
956
    self.assertEqual(document_c.getFilename(), self.filename_text)
957
    self.assertEquals(self.size_filename_text, document_c.get_size())
958
    self.assertEquals(document_c.getShortTitle(), self.short_title2) 
959 960 961
    self.checkSynchronizationStateIsSynchronized()
    document_s = document_server._getOb(self.id1)
    document_c = document_client1._getOb(self.id1)
962 963 964
    # Ignore processing status workflow as 
    self.assertXMLViewIsEqual(self.sub_id1, document_s, document_c, force=True,
                              ignore_processing_status_workflow=True)
965 966 967 968 969

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