Back to index

plone3  3.1.7
testFolderButtons.py
Go to the documentation of this file.
00001 #
00002 # Tests for scripts behind folder_contents view
00003 #
00004 
00005 from cStringIO import StringIO
00006 from zExceptions import Forbidden
00007 from zope.interface import directlyProvides
00008 from zope import component
00009 from zope.app.container.interfaces import IObjectRemovedEvent
00010 from Products.CMFPlone.tests import PloneTestCase
00011 from Products.PloneTestCase.setup import default_user
00012 from Products.PloneTestCase.setup import default_password
00013 from Products.CMFPlone.tests.dummy import Item, ICantBeDeleted, \
00014                                           disallow_delete_handler
00015 import transaction
00016 
00017 PloneTestCase.installProduct('SiteAccess', quiet=1)
00018 
00019 
00020 class TestFolderRename(PloneTestCase.PloneTestCase):
00021     # Tests for folder_rename and folder_rename_form
00022 
00023     def afterSetUp(self):
00024         self.catalog = self.portal.portal_catalog
00025         self.folder.invokeFactory('Folder', id='foo')
00026         self.folder.invokeFactory('Folder', id='bar')
00027         self.folder.foo.invokeFactory('Document', id='doc1')
00028         self.folder.bar.invokeFactory('Document', id='doc2')
00029         # folder_rename requires a non-GET request
00030         self.setRequestMethod('POST')
00031         self.setupAuthenticator()
00032 
00033     def testTitleIsUpdatedOnTitleChange(self):
00034         # Make sure our title is updated on the object
00035         title = 'Test Doc - Snooze!'
00036         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00037         self.folder.folder_rename(paths=[doc_path], new_ids=['doc1'], new_titles=[title])
00038         obj = self.folder.foo.doc1
00039         self.assertEqual(obj.Title(), title)
00040 
00041     def testCatalogTitleIsUpdatedOnFolderTitleChange(self):
00042         # Make sure our title is updated in the catalog
00043         title = 'Test Doc - Snooze!'
00044         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00045         self.folder.folder_rename(paths=[doc_path], new_ids=['doc1'], new_titles=[title])
00046         results = self.catalog(Title='Snooze')
00047         self.failUnless(results)
00048         for result in results:
00049             self.assertEqual(result.Title, title)
00050             self.assertEqual(result.id, 'doc1')
00051 
00052     def testTitleAndIdAreUpdatedOnFolderRename(self):
00053         # Make sure rename updates both title and id
00054         title = 'Test Folder - Snooze!'
00055         transaction.savepoint(optimistic=True) # make rename work
00056         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00057         self.folder.folder_rename(paths=[doc_path], new_ids=['baz'], new_titles=[title])
00058         self.assertEqual(getattr(self.folder.foo, 'doc1', None), None)
00059         self.failUnless(getattr(self.folder.foo, 'baz', None) is not None)
00060         self.assertEqual(self.folder.foo.baz.Title(),title)
00061 
00062     def testCatalogTitleAndIdAreUpdatedOnFolderRename(self):
00063         # Make sure catalog updates title on rename
00064         title = 'Test Folder - Snooze!'
00065         transaction.savepoint(optimistic=True) # make rename work
00066         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00067         self.folder.folder_rename(paths=[doc_path], new_ids=['baz'], new_titles=[title])
00068         results = self.catalog(Title='Snooze')
00069         self.failUnless(results)
00070         for result in results:
00071             self.assertEqual(result.Title, title)
00072             self.assertEqual(result.id, 'baz')
00073 
00074     def testUpdateMultiplePaths(self):
00075         # Ensure this works for multiple paths
00076         title = 'Test Folder - Snooze!'
00077         transaction.savepoint(optimistic=True) # make rename work
00078         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00079         doc2_path = '/'.join(self.folder.bar.doc2.getPhysicalPath())
00080         self.folder.folder_rename(paths=[doc1_path,doc2_path], new_ids=['baz','blah'], new_titles=[title,title])
00081         self.assertEqual(getattr(self.folder.foo, 'doc1', None), None)
00082         self.assertEqual(getattr(self.folder.bar, 'doc2', None), None)
00083         self.failUnless(getattr(self.folder.foo, 'baz', None) is not None)
00084         self.failUnless(getattr(self.folder.bar, 'blah', None) is not None)
00085         self.assertEqual(self.folder.foo.baz.Title(),title)
00086         self.assertEqual(self.folder.bar.blah.Title(),title)
00087 
00088     def testNoErrorOnBadPaths(self):
00089         # Ensure we don't fail on a bad path
00090         self.app.REQUEST.set('paths', ['/garbage/path'])
00091         self.folder.folder_rename_form()
00092 
00093     def testGETRaises(self):
00094         # folder_rename requires a non-GET request and will fail otherwise
00095         self.setRequestMethod('GET')
00096         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00097         self.assertRaises(Forbidden, self.folder.folder_rename,
00098                           [doc1_path], ['bar'], ['Baz'])
00099 
00100 
00101 class TestFolderDelete(PloneTestCase.PloneTestCase):
00102     # Tests for folder_delete.py
00103 
00104     def afterSetUp(self):
00105         self.catalog = self.portal.portal_catalog
00106         self.folder.invokeFactory('Folder', id='foo')
00107         self.folder.invokeFactory('Folder', id='bar')
00108         self.folder.foo.invokeFactory('Document', id='doc1')
00109         self.folder.bar.invokeFactory('Document', id='doc2')
00110         undeletable = Item('no_delete', 'Just Try!')
00111         # make it undeletable
00112         directlyProvides(undeletable, ICantBeDeleted)
00113         component.provideHandler(disallow_delete_handler, [ICantBeDeleted,
00114                                                            IObjectRemovedEvent])
00115         self.folder._setObject('no_delete', undeletable)
00116         # folder_delete requires a non-GET request
00117         self.setRequestMethod('POST')
00118         self.setupAuthenticator()
00119 
00120     def beforeTearDown(self):
00121         # unregister our deletion event subscriber
00122         component.getSiteManager().unregisterHandler(disallow_delete_handler,
00123                                                      [ICantBeDeleted,
00124                                                       IObjectRemovedEvent])
00125 
00126     def testFolderDeletion(self):
00127         # Make sure object gets deleted
00128         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00129         self.app.REQUEST.set('paths', [doc_path])
00130         self.folder.folder_delete()
00131         self.assertEqual(getattr(self.folder.foo, 'doc1', None), None)
00132 
00133     def testCatalogIsUpdatedOnFolderDelete(self):
00134         # Make sure catalog gets updated
00135         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00136         self.app.REQUEST.set('paths', [doc_path])
00137         self.folder.folder_delete()
00138         results = self.catalog(path=doc_path)
00139         self.failIf(results)
00140 
00141     def testDeleteMultiplePaths(self):
00142         # Make sure deletion works for list of paths
00143         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00144         doc2_path = '/'.join(self.folder.bar.doc2.getPhysicalPath())
00145         self.app.REQUEST.set('paths', [doc1_path,doc2_path])
00146         self.folder.folder_delete()
00147         self.assertEqual(getattr(self.folder.foo, 'doc1', None), None)
00148         self.assertEqual(getattr(self.folder.bar, 'doc2', None), None)
00149 
00150     def testNoErrorOnBadPaths(self):
00151         # Ensure we don't fail on a bad path
00152         self.app.REQUEST.set('paths', ['/garbage/path'])
00153         self.folder.folder_delete()
00154 
00155     def testObjectDeleteFailureIsCleanedUp(self):
00156         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00157         doc2_path = '/'.join(self.folder.bar.doc2.getPhysicalPath())
00158         undeletable_path = '/'.join(self.folder.no_delete.getPhysicalPath())
00159         self.app.REQUEST.set('paths', [doc1_path, undeletable_path, doc2_path])
00160         self.folder.folder_delete()
00161         # The two deletable object should have been deleted
00162         self.assertEqual(getattr(self.folder.foo, 'doc1', None), None)
00163         self.assertEqual(getattr(self.folder.bar, 'doc2', None), None)
00164         # but the undeletable object will still be in place
00165         undeletable = getattr(self.folder, 'no_delete', None)
00166         self.failIfEqual(undeletable, None)
00167         # manage_beforeDelete will have been called, but the change it
00168         # makes should have been rolled back
00169         self.failIf(hasattr(undeletable, 'delete_attempted'))
00170 
00171     def testGETRaisesUnauthorized(self):
00172         # folder_delete requires a non-GET request and will fail otherwise
00173         self.setRequestMethod('GET')
00174         self.assertRaises(Forbidden, self.folder.folder_delete)
00175 
00176 
00177 
00178 class TestFolderPublish(PloneTestCase.PloneTestCase):
00179     # Tests for folder_publish and content_status_history and
00180     # content_status_modify
00181 
00182     def afterSetUp(self):
00183         self.catalog = self.portal.portal_catalog
00184         self.wtool = self.portal.portal_workflow
00185         self.folder.invokeFactory('Folder', id='foo')
00186         self.folder.invokeFactory('Folder', id='bar')
00187         self.folder.foo.invokeFactory('Document', id='doc1')
00188         self.folder.bar.invokeFactory('Document', id='doc2')
00189         self.portal.acl_users._doAddUser('reviewer', 'secret', ['Reviewer'], [])
00190         # folder_publish requires a non-GET request
00191         self.setRequestMethod('POST')
00192 
00193     def testFolderPublishing(self):
00194         # Make sure object gets published
00195         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00196         self.login('reviewer')
00197         self.setupAuthenticator()
00198         self.folder.folder_publish(workflow_action='publish',paths=[doc_path])
00199         self.assertEqual(self.wtool.getInfoFor(self.folder.foo.doc1, 'review_state',None), 'published')
00200 
00201     def testCatalogIsUpdatedOnFolderPublish(self):
00202         # Make sure catalog gets updated
00203         doc_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00204         self.login('reviewer')
00205         self.setupAuthenticator()
00206         self.folder.folder_publish(workflow_action='publish',paths=[doc_path])
00207         results = self.catalog(path=doc_path)
00208         self.assertEqual(len(results),1)
00209         self.assertEqual(results[0].review_state,'published')
00210 
00211     def testPublishMultiplePaths(self):
00212         # Make sure publish works for list of paths
00213         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00214         doc2_path = '/'.join(self.folder.bar.doc2.getPhysicalPath())
00215         self.login('reviewer')
00216         self.setupAuthenticator()
00217         self.folder.folder_publish('publish',paths=[doc1_path,doc2_path])
00218         self.assertEqual(self.wtool.getInfoFor(self.folder.foo.doc1, 'review_state',None), 'published')
00219         self.assertEqual(self.wtool.getInfoFor(self.folder.bar.doc2, 'review_state',None), 'published')
00220 
00221     def testNoErrorOnBadPaths(self):
00222         # Ensure we don't fail on a bad path, but transition the good ones
00223         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00224         doc2_path = '/'.join(self.folder.bar.doc2.getPhysicalPath())
00225         paths=[doc1_path, '/garbage/path', doc2_path]
00226         self.login('reviewer')
00227         self.setupAuthenticator()
00228         self.folder.folder_publish('publish', paths=paths)
00229         self.assertEqual(self.wtool.getInfoFor(self.folder.foo.doc1,
00230                                                'review_state', None),
00231                          'published')
00232         self.assertEqual(self.wtool.getInfoFor(self.folder.bar.doc2,
00233                                                'review_state', None),
00234                          'published')
00235 
00236     def testPublishFailureIsCleanedUp(self):
00237         # Ensure we don't fail on a bad path, but transition the good ones
00238 
00239         # First we add a failing notifySuccess method to the workflow
00240         # via a nasty monkey-patch
00241         from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
00242         def notifySuccess(self, obj, action, result):
00243             raise Exception, 'Cannot transition'
00244         orig_notify = DCWorkflowDefinition.notifySuccess
00245         DCWorkflowDefinition.notifySuccess = notifySuccess
00246 
00247         # now we perform the transition
00248         doc1_path = '/'.join(self.folder.foo.doc1.getPhysicalPath())
00249         self.login('reviewer')
00250         self.setupAuthenticator()
00251         self.folder.folder_publish('publish', paths=[doc1_path])
00252         # because an error was raised during post transition the
00253         # transaction should have been rolled-back and the state
00254         # should not have changed
00255         self.failIfEqual(self.wtool.getInfoFor(self.folder.foo.doc1,
00256                                                'review_state', None),
00257                          'published')
00258 
00259         # undo our nasty patch
00260         DCWorkflowDefinition.notifySuccess = orig_notify
00261 
00262     def testGETRaises(self):
00263         # folder_rename requires a non-GET request and will fail otherwise
00264         self.setRequestMethod('GET')
00265         self.assertRaises(Forbidden, self.folder.folder_publish,
00266                           'publish', paths=['bogus'])
00267 
00268 
00269 class TestFolderCutCopy(PloneTestCase.PloneTestCase):
00270     # Tests for folder_cut.py and folder_copy.py
00271 
00272     def testCutNoErrorOnBadPaths(self):
00273         # Ensure we don't fail on a bad path
00274         self.app.REQUEST.set('paths', ['/garbage/path'])
00275         self.folder.folder_cut()
00276 
00277     def testCopyNoErrorOnBadPaths(self):
00278         # Ensure we don't fail on a bad path
00279         self.app.REQUEST.set('paths', ['/garbage/path'])
00280         self.folder.folder_copy()
00281 
00282 
00283 class TestObjectActions(PloneTestCase.FunctionalTestCase):
00284     
00285     def afterSetUp(self):
00286         self.basic_auth = '%s:%s' % (default_user, default_password)
00287     
00288     def assertStatusEqual(self, a, b, msg=''):
00289         if a != b:
00290             entries = self.portal.error_log.getLogEntries()
00291             if entries:
00292                 msg = entries[0]['tb_text']
00293             else:
00294                 if not msg:
00295                     msg = 'no error log entry available'
00296         self.failUnlessEqual(a, b, msg)
00297     
00298     def testObjectRenameWithoutVHM(self):
00299         self.folder.invokeFactory('Document', 'd1', title='Doc1')
00300         folderUrl = self.folder.absolute_url()
00301         objPath = '/'.join(self.folder.d1.getPhysicalPath())
00302         objectUrl = self.folder.d1.absolute_url()
00303         origTemplate = self.folder.d1.absolute_url() + '/document_view?foo=bar'
00304 
00305         response = self.publish(objPath + '/object_rename', self.basic_auth, extra={'HTTP_REFERER' : origTemplate})
00306 
00307         self.assertStatusEqual(response.getStatus(), 302) # Redirect to edit
00308         
00309         location = response.getHeader('Location').split('?')[0]
00310         params = {}
00311         for p in response.getHeader('Location').split('?')[1].split('&'):
00312             key, val = p.split('=')
00313             self.failIf(key in params)
00314             params[key] = val
00315         self.failUnless('paths%3Alist' in params)
00316         self.failUnless('orig_template' in params)
00317 
00318         self.failUnless(location.startswith(objectUrl), location)
00319         self.failUnless(location.endswith('folder_rename_form'), location)
00320 
00321         # Perform the redirect
00322         editFormPath = location[len(self.app.REQUEST.SERVER_URL):]
00323         newParams = "orig_template=%s" % params['orig_template']
00324         newParams += "&paths:list=%s" % params['paths%3Alist']
00325         response = self.publish("%s?%s" % (editFormPath, newParams,), self.basic_auth)
00326         self.assertStatusEqual(response.getStatus(), 200)
00327 
00328         # Set up next set of params, faking user submission
00329         newParams += "&form.submitted=1"
00330         newParams += "&form.button.renameAll=Rename All"
00331         newParams += "&new_ids:list=%s" % 'new-id'
00332         newParams += "&new_titles:list=%s" % 'New title'
00333         newParams += '&%s=%s' % self.getAuthenticator()
00334 
00335         data = StringIO(newParams)
00336         response = self.publish(editFormPath, self.basic_auth,
00337                                 request_method='POST', stdin=data)
00338         self.assertStatusEqual(response.getStatus(), 302)
00339         
00340         # Make sure we landed in the right place
00341         location = response.getHeader('Location').split('?')[0]
00342         self.failUnless(location.startswith(folderUrl + '/new-id'), location)
00343         self.failUnless(location.endswith('document_view'), location)
00344         
00345         self.failUnless('new-id' in self.folder.objectIds())
00346         self.failIf('d1' in self.folder.objectIds())
00347         self.assertEqual(self.folder['new-id'].Title(), 'New title')
00348 
00349     def testObjectRenameWithVHM(self):
00350         adding = self.app.manage_addProduct['SiteAccess']
00351         adding.manage_addVirtualHostMonster('vhm')
00352         
00353         vhmBasePath = "/VirtualHostBase/http/example.org:80/%s/VirtualHostRoot/" % self.portal.getId()
00354         vhmBaseUrl = 'http://example.org/'
00355         
00356         self.folder.invokeFactory('Document', 'd1', title='Doc1')
00357         folderPath = vhmBasePath + '/'.join(self.folder.getPhysicalPath()[2:])
00358         folderUrl = vhmBaseUrl + '/'.join(self.folder.getPhysicalPath()[2:]) 
00359         objPath = vhmBasePath + '/'.join(self.folder.d1.getPhysicalPath()[2:])
00360         objectUrl = vhmBaseUrl + '/'.join(self.folder.d1.getPhysicalPath()[2:])
00361         
00362         origTemplate = objectUrl + '/document_view?foo=bar'
00363 
00364         response = self.publish(objPath + '/object_rename', self.basic_auth, extra={'HTTP_REFERER' : origTemplate})
00365 
00366         self.assertStatusEqual(response.getStatus(), 302) # Redirect to edit
00367         
00368         location = response.getHeader('Location').split('?')[0]
00369         params = {}
00370         for p in response.getHeader('Location').split('?')[1].split('&'):
00371             key, val = p.split('=')
00372             self.failIf(key in params)
00373             params[key] = val
00374         self.failUnless('paths%3Alist' in params)
00375         self.failUnless('orig_template' in params)
00376 
00377         self.failUnless(location.startswith(objectUrl), location)
00378         self.failUnless(location.endswith('folder_rename_form'), location)
00379 
00380         # Perform the redirect
00381         editFormPath = vhmBasePath + location[len(vhmBaseUrl):]
00382         newParams = "orig_template=%s" % params['orig_template']
00383         newParams += "&paths:list=%s" % params['paths%3Alist']
00384         response = self.publish("%s?%s" % (editFormPath, newParams,), self.basic_auth)
00385         self.assertStatusEqual(response.getStatus(), 200)
00386 
00387         # Set up next set of params, faking user submission
00388         newParams += "&form.submitted=1"
00389         newParams += "&form.button.renameAll=Rename All"
00390         newParams += "&new_ids:list=%s" % 'new-id'
00391         newParams += "&new_titles:list=%s" % 'New title'
00392         newParams += '&%s=%s' % self.getAuthenticator()
00393 
00394         data = StringIO(newParams)
00395         response = self.publish(editFormPath, self.basic_auth,
00396                                 request_method='POST', stdin=data)
00397         self.assertStatusEqual(response.getStatus(), 302)
00398         
00399         # Make sure we landed in the right place
00400         location = response.getHeader('Location').split('?')[0]
00401         self.failUnless(location.startswith(folderUrl + '/new-id'), location)
00402         self.failUnless(location.endswith('document_view'), location)
00403         
00404         self.failUnless('new-id' in self.folder.objectIds())
00405         self.failIf('d1' in self.folder.objectIds())
00406         self.assertEqual(self.folder['new-id'].Title(), 'New title')
00407 
00408 
00409 def test_suite():
00410     from unittest import TestSuite, makeSuite
00411     suite = TestSuite()
00412     suite.addTest(makeSuite(TestFolderRename))
00413     suite.addTest(makeSuite(TestFolderDelete))
00414     suite.addTest(makeSuite(TestFolderPublish))
00415     suite.addTest(makeSuite(TestFolderCutCopy))
00416     suite.addTest(makeSuite(TestObjectActions))
00417     return suite