Back to index

plone3  3.1.7
testCatalogTool.py
Go to the documentation of this file.
00001 #
00002 # CatalogTool tests
00003 #
00004 
00005 import unittest
00006 import zope.interface
00007 
00008 from Products.CMFPlone.tests import PloneTestCase
00009 
00010 from Acquisition import aq_base
00011 from Globals import REPLACEABLE
00012 from DateTime import DateTime
00013 from Products.CMFCore.permissions import AccessInactivePortalContent
00014 import transaction
00015 
00016 from Products.CMFPlone.CatalogTool import ExtensibleIndexableObjectWrapper
00017 from Products.CMFPlone.CatalogTool import _eioRegistry
00018 
00019 from Products.CMFPlone.CatalogTool import is_folderish
00020 from Products.CMFPlone.tests import dummy
00021 
00022 portal_name = PloneTestCase.portal_name
00023 default_user  = PloneTestCase.default_user
00024 
00025 user2  = 'u2'
00026 group2 = 'g2'
00027 
00028 base_content = ['Members', 'aggregator', 'aggregator',
00029                 'events', 'news', 'previous',
00030                 default_user, 'front-page', 'doc']
00031 
00032 
00033 class TestCatalogSetup(PloneTestCase.PloneTestCase):
00034 
00035     def afterSetUp(self):
00036         self.catalog = self.portal.portal_catalog
00037 
00038     def testSearchableTextIsZCTextIndex(self):
00039         # SearchableText index should be a ZCTextIndex
00040         itype = self.catalog.Indexes['SearchableText'].__class__.__name__
00041         self.assertEqual(itype, 'ZCTextIndex')
00042 
00043     def testDescriptionIsZCTextIndex(self):
00044         # Description index should be a ZCTextIndex
00045         itype = self.catalog.Indexes['Description'].__class__.__name__
00046         self.assertEqual(itype, 'ZCTextIndex')
00047 
00048     def testTitleIsZCTextIndex(self):
00049         # Title index should be a ZCTextIndex
00050         itype = self.catalog.Indexes['Title'].__class__.__name__
00051         self.assertEqual(itype, 'ZCTextIndex')
00052 
00053     def testPloneLexiconIsZCTextLexicon(self):
00054         # Lexicon should be a ZCTextIndex lexicon
00055         self.failUnless(hasattr(aq_base(self.catalog), 'plone_lexicon'))
00056         self.assertEqual(self.catalog.plone_lexicon.meta_type,\
00057                          'ZCTextIndex Lexicon')
00058 
00059     def testPathIsExtendedPathIndex(self):
00060         # path index should be an ExtendedPathIndex
00061         self.assertEqual(self.catalog.Indexes['path'].__class__.__name__,
00062                          'ExtendedPathIndex')
00063 
00064     def testGetObjPositionInParentIsFieldIndex(self):
00065         # getObjPositionInParent index should be a FieldIndex
00066         # also see TestCatalogOrdering below
00067         self.assertEqual(self.catalog.Indexes['getObjPositionInParent'].__class__.__name__,
00068                          'FieldIndex')
00069 
00070     def testGetObjSizeInSchema(self):
00071         # getObjSize column should be in catalog schema
00072         self.failUnless('getObjSize' in self.catalog.schema())
00073 
00074     def testExclude_from_navInSchema(self):
00075         # exclude_from_nav column should be in catalog schema
00076         self.failUnless('exclude_from_nav' in self.catalog.schema())
00077 
00078     def testIs_folderishInSchema(self):
00079         # is_folderish should be in catalog schema
00080         self.failUnless('is_folderish' in self.catalog.schema())
00081 
00082     def testIs_folderishIsFieldIndex(self):
00083         # is_folderish should be a FieldIndex
00084         self.failUnless(self.catalog.Indexes['is_folderish'].__class__.__name__,
00085                         'FieldIndex')
00086 
00087     def testDateIsDateIndex(self):
00088         # Date should be a DateIndex
00089         self.assertEqual(self.catalog.Indexes['Date'].__class__.__name__,
00090                          'DateIndex')
00091 
00092     def testCreatedIsDateIndex(self):
00093         # created should be a DateIndex
00094         self.assertEqual(self.catalog.Indexes['created'].__class__.__name__,
00095                          'DateIndex')
00096 
00097     def testEffectiveIsDateIndex(self):
00098         # effective should be a DateIndex
00099         self.assertEqual(self.catalog.Indexes['effective'].__class__.__name__,
00100                          'DateIndex')
00101 
00102     def testEndIsDateIndex(self):
00103         # end should be a DateIndex
00104         self.assertEqual(self.catalog.Indexes['end'].__class__.__name__,
00105                          'DateIndex')
00106 
00107     def testExpiresIsDateIndex(self):
00108         # expires should be a DateIndex
00109         self.assertEqual(self.catalog.Indexes['expires'].__class__.__name__,
00110                          'DateIndex')
00111 
00112     def testModifiedIsDateIndex(self):
00113         # modified should be a DateIndex
00114         self.assertEqual(self.catalog.Indexes['modified'].__class__.__name__,
00115                          'DateIndex')
00116 
00117     def testStartIsDateIndex(self):
00118         # start should be a DateIndex
00119         self.assertEqual(self.catalog.Indexes['start'].__class__.__name__,
00120                          'DateIndex')
00121 
00122     def testEffectiveRangeIsDateRangeIndex(self):
00123         # effectiveRange should be a DateRangeIndex
00124         self.assertEqual(self.catalog.Indexes['effectiveRange'].__class__.__name__,
00125                          'DateRangeIndex')
00126 
00127     def testSortable_TitleIsFieldIndex(self):
00128         # sortable_title should be a FieldIndex
00129         self.assertEqual(self.catalog.Indexes['sortable_title'].__class__.__name__,
00130                          'FieldIndex')
00131 
00132     def testExpirationDateInSchema(self):
00133         # ExpirationDate column should be in catalog schema
00134         self.failUnless('ExpirationDate' in self.catalog.schema())
00135 
00136     def testExpiresDateNotInSchema(self):
00137         # ExpirationDate column should be in catalog schema
00138         self.failIf('ExpiresDate' in self.catalog.schema())
00139 
00140     def testIs_Default_PageIsFieldIndex(self):
00141         # sortable_title should be a FieldIndex
00142         self.assertEqual(self.catalog.Indexes['is_default_page'].__class__.__name__,
00143                          'FieldIndex')
00144 
00145 
00146 class TestCatalogIndexing(PloneTestCase.PloneTestCase):
00147 
00148     def afterSetUp(self):
00149         self.catalog = self.portal.portal_catalog
00150         self.folder.invokeFactory('Document', id='doc', title='Foo', description='Bar')
00151         self.catalog.unindexObject(self.folder.doc)
00152 
00153     def assertResults(self, result, expect):
00154         # Verifies ids of catalog results against expected ids
00155         lhs = [r.getId for r in result]
00156         lhs.sort()
00157         rhs = list(expect)
00158         rhs.sort()
00159         self.assertEqual(lhs, rhs)
00160 
00161     def testFixture(self):
00162         self.assertEqual(self.folder.doc.getId(), 'doc')
00163         self.assertEqual(self.folder.doc.Title(), 'Foo')
00164         self.assertEqual(self.folder.doc.Description(), 'Bar')
00165         self.assertEqual(len(self.catalog(getId='doc')), 0)
00166         self.assertEqual(len(self.catalog(Title='Foo')), 0)
00167         self.assertEqual(len(self.catalog(Description='Bar')), 0)
00168 
00169     def testIndexObject(self):
00170         # Object should be indexed
00171         self.catalog.indexObject(self.folder.doc)
00172         self.assertEqual(len(self.catalog(getId='doc')), 1)
00173         self.assertEqual(len(self.catalog(Title='Foo')), 1)
00174         self.assertEqual(len(self.catalog(Description='Bar')), 1)
00175 
00176     def testReindexObject(self):
00177         # Object should be indexed
00178         self.catalog.reindexObject(self.folder.doc)
00179         self.assertEqual(len(self.catalog(getId='doc')), 1)
00180         self.assertEqual(len(self.catalog(Title='Foo')), 1)
00181         self.assertEqual(len(self.catalog(Description='Bar')), 1)
00182 
00183     def testUnindexObject(self):
00184         # Object should be unindexed
00185         self.catalog.indexObject(self.folder.doc)
00186         self.assertEqual(len(self.catalog(getId='doc')), 1)
00187         self.catalog.unindexObject(self.folder.doc)
00188         self.assertEqual(len(self.catalog(getId='doc')), 0)
00189 
00190     def testIndexObjectUpdatesMetadata(self):
00191         # Indexing should update metadata
00192         self.catalog.indexObject(self.folder.doc)
00193         brain = self.catalog(getId='doc')[0]
00194         self.assertEqual(brain.getId, 'doc')
00195         self.assertEqual(brain.Title, 'Foo')
00196         self.assertEqual(brain.Description, 'Bar')
00197 
00198     def testReindexObjectUpdatesMetadata(self):
00199         # Reindexing should update metadata
00200         self.catalog.indexObject(self.folder.doc)
00201         self.folder.doc.setTitle('Fred')
00202         self.folder.doc.setDescription('BamBam')
00203         self.catalog.reindexObject(self.folder.doc)
00204         brain = self.catalog(getId='doc')[0]
00205         self.assertEqual(brain.getId, 'doc')
00206         self.assertEqual(brain.Title, 'Fred')
00207         self.assertEqual(brain.Description, 'BamBam')
00208 
00209     def testReindexObjectSkipsMetadata(self):
00210         # Reindexing should not update metadata when update_metadata=0
00211         self.catalog.indexObject(self.folder.doc)
00212         self.folder.doc.setTitle('Fred')
00213         self.folder.doc.setDescription('BamBam')
00214         self.catalog.reindexObject(self.folder.doc, update_metadata=0)
00215         brain = self.catalog(getId='doc')[0]
00216         # Metadata did not change
00217         self.assertEqual(brain.getId, 'doc')
00218         self.assertEqual(brain.Title, 'Foo')
00219         self.assertEqual(brain.Description, 'Bar')
00220 
00221     def testReindexTitleOnly(self):
00222         # Reindexing should only index the Title
00223         self.catalog.indexObject(self.folder.doc)
00224         self.folder.doc.setTitle('Fred')
00225         self.folder.doc.setDescription('BamBam')
00226         self.catalog.reindexObject(self.folder.doc, idxs=['Title'])
00227         self.assertEqual(len(self.catalog(getId='doc')), 1)
00228         self.assertEqual(len(self.catalog(Title='Fred')), 1)
00229         # Description index did not change
00230         self.assertEqual(len(self.catalog(Description='Bar')), 1)
00231         self.assertEqual(len(self.catalog(Description='BamBam')), 0)
00232 
00233     def testReindexTitleOnlyUpdatesMetadata(self):
00234         # Reindexing Title should update metadata
00235         self.catalog.indexObject(self.folder.doc)
00236         self.folder.doc.setTitle('Fred')
00237         self.folder.doc.setDescription('BamBam')
00238         self.catalog.reindexObject(self.folder.doc, idxs=['Title'])
00239         brain = self.catalog(getId='doc')[0]
00240         self.assertEqual(brain.getId, 'doc')
00241         self.assertEqual(brain.Title, 'Fred')
00242         self.assertEqual(brain.Description, 'BamBam')
00243 
00244     def testReindexTitleOnlySkipsMetadata(self):
00245         # Reindexing Title should not update metadata when update_metadata=0
00246         self.catalog.indexObject(self.folder.doc)
00247         self.folder.doc.setTitle('Fred')
00248         self.folder.doc.setDescription('BamBam')
00249         self.catalog.reindexObject(self.folder.doc, idxs=['Title'], update_metadata=0)
00250         brain = self.catalog(getId='doc')[0]
00251         # Metadata did not change
00252         self.assertEqual(brain.getId, 'doc')
00253         self.assertEqual(brain.Title, 'Foo')
00254         self.assertEqual(brain.Description, 'Bar')
00255 
00256     def testIndexTitleOnly(self):
00257         # Indexing should only index the Title
00258         #
00259         # TODO: This does not work as expected. The object
00260         # appears to be in the catalog but is not returned
00261         # by searchResults()!?
00262         #
00263         self.catalog.indexObject(self.folder.doc, idxs=['Title'])
00264         # The document is cataloged
00265         path = self.catalog._CatalogTool__url(self.folder.doc)
00266         self.failUnless(path in self.catalog._catalog.paths.values())
00267         # But it is not returned when searching...
00268         self.assertEqual(len(self.catalog(getId='doc')), 0)
00269         self.assertEqual(len(self.catalog(Title='Foo')), 0) # <-- Should be 1
00270         self.assertEqual(len(self.catalog(Description='Bar')), 0)
00271 
00272     def testIndexIdOnly(self):
00273         # Indexing should only index the id
00274         #
00275         # TODO: Demonstrate that the behavior is independent
00276         # of index type.
00277         #
00278         self.catalog.indexObject(self.folder.doc, idxs=['getId'])
00279         # The document is cataloged
00280         path = self.catalog._CatalogTool__url(self.folder.doc)
00281         self.failUnless(path in self.catalog._catalog.paths.values())
00282         # But it is not returned when searching...
00283         self.assertEqual(len(self.catalog(getId='doc')), 0) # <-- Should be 1
00284         self.assertEqual(len(self.catalog(Title='Foo')), 0)
00285         self.assertEqual(len(self.catalog(Description='Bar')), 0)
00286 
00287     def testClearFindAndRebuildRemovesBadContent(self):
00288         # Index the doc for consistency
00289         self.catalog.indexObject(self.folder.doc)
00290         res = self.catalog.searchResults()
00291         self.assertResults(res, base_content)
00292         # index an object which shouldn't be there
00293         self.catalog.indexObject(self.portal.portal_skins)
00294         res = self.catalog.searchResults()
00295         self.assertResults(res, base_content+['portal_skins'])
00296         self.catalog.clearFindAndRebuild()
00297         # This will remove the extraneous item and add the document added
00298         # in afterSetup
00299         res = self.catalog.searchResults()
00300         self.assertResults(res, base_content)
00301 
00302     def testClearFindAndRebuildAddsMissingContent(self):
00303         # Index the doc for consistency
00304         self.catalog.indexObject(self.folder.doc)
00305         res = self.catalog.searchResults()
00306         self.assertResults(res, base_content)
00307         # index an object which shouldn't be there
00308         self.catalog.unindexObject(self.portal.Members)
00309         altered_content = base_content[:]
00310         altered_content.remove('Members')
00311         res = self.catalog.searchResults()
00312         self.assertResults(res, altered_content)
00313         self.catalog.clearFindAndRebuild()
00314         # This will add the missing item and also the document added
00315         # in afterSetup
00316         res = self.catalog.searchResults()
00317         self.assertResults(res, base_content)
00318 
00319     def testClearFindAndRebuildKeepsModificationDate(self):
00320         # Index the doc for consistency
00321         self.catalog.indexObject(self.folder.doc)
00322         self.folder.doc.setModificationDate(DateTime(0))
00323         self.catalog.clearFindAndRebuild()
00324         self.assertEquals(self.folder.doc.modified(), DateTime(0))
00325         self.assertEquals(len(self.catalog(modified=DateTime(0))), 1)
00326 
00327 
00328 class TestCatalogSearching(PloneTestCase.PloneTestCase):
00329 
00330     def afterSetUp(self):
00331         self.catalog = self.portal.portal_catalog
00332         self.workflow = self.portal.portal_workflow
00333         self.groups = self.portal.portal_groups
00334 
00335         self.portal.acl_users._doAddUser(user2, 'secret', [], [])
00336 
00337         self.folder.invokeFactory('Document', id='doc', text='foo')
00338         self.folder.invokeFactory('Folder', id='folder2')
00339         self.folder.folder2.invokeFactory('Document', id='doc2', text='bar')
00340         self.workflow.doActionFor(self.folder.doc, 'hide', comment='')
00341         self.workflow.doActionFor(self.folder.folder2, 'hide', comment='')
00342         self.workflow.doActionFor(self.folder.folder2.doc2, 'hide', comment='')
00343 
00344         # Used for testing AND/OR search functionality below
00345         self.folder.invokeFactory('Document', id='aaa', text='aaa', title='ccc')
00346         self.folder.invokeFactory('Document', id='bbb', text='bbb')
00347 
00348         self.setupAuthenticator()
00349 
00350     def addUser2ToGroup(self):
00351         self.groups.groupWorkspacesCreationFlag = 0
00352         self.groups.addGroup(group2, None, [], [])
00353         group = self.groups.getGroupById(group2)
00354         self.loginAsPortalOwner() # GRUF 3.52
00355         group.addMember(user2)
00356         self.login(default_user) # Back to normal
00357         return group2
00358 
00359     def testListAllowedRolesAndUsers(self):
00360         # Should include the group in list of allowed users
00361         groupname = self.addUser2ToGroup()
00362         uf = self.portal.acl_users
00363         self.failUnless(('user:%s' % groupname) in
00364                 self.catalog._listAllowedRolesAndUsers(uf.getUser(user2)))
00365 
00366     def testSearchReturnsDocument(self):
00367         # Document should be found when owner does a search
00368         self.assertEqual(self.catalog(SearchableText='aaa')[0].id, 'aaa')
00369 
00370     def testSearchDoesNotReturnDocument(self):
00371         # Document should not be found when user2 does a search
00372         self.login(user2)
00373         self.assertEqual(len(self.catalog(SearchableText='foo')), 0)
00374 
00375     def testSearchReturnsDocumentUsing_DefaultAND(self):
00376         # Documents should not be found when searching 'aaa bbb' (which should default to AND)
00377         self.assertEqual(len(self.catalog(SearchableText='aaa bbb')), 0)
00378         self.assertEqual(len(self.catalog(SearchableText='aaa ccc')), 1)
00379 
00380     def testSearchReturnsDocumentUsing_AND(self):
00381         # Documents should not be found when owner does a search using AND
00382         self.assertEqual(len(self.catalog(SearchableText='aaa AND bbb')), 0)
00383         self.assertEqual(len(self.catalog(SearchableText='aaa AND ccc')), 1)
00384 
00385     def testSearchReturnsDocumentUsing_OR(self):
00386         # Two documents (aaa, bbb)  should be found when owner does a search using OR
00387         results = self.catalog(SearchableText='aaa OR bbb')
00388         self.assertEqual(len(results), 2)
00389 
00390     def testSearchReturnsDocumentWhenPermissionIsTroughLocalRole(self):
00391         # After adding a group with access rights and containing user2,
00392         # a search must find the document.
00393         groupname = self.addUser2ToGroup()
00394         self.setRequestMethod('POST')
00395         self.folder.folder_localrole_edit('add', [groupname], 'Owner')
00396         self.setRequestMethod('GET')
00397         self.login(user2)
00398         self.assertEqual(self.catalog(SearchableText='aaa')[0].id, 'aaa')
00399 
00400     def testSearchRespectsLocalRoleAcquisition(self):
00401         # After adding a group with access rights and containing user2,
00402         # a search must find the document in subfolders.
00403         groupname = self.addUser2ToGroup()
00404         self.setRequestMethod('POST')
00405         self.folder.folder_localrole_edit('add', [groupname], 'Owner')
00406         self.setRequestMethod('GET')
00407         self.login(user2)
00408         # Local Role works in subfolder
00409         self.assertEqual(self.catalog(SearchableText='bbb')[0].id, 'bbb')
00410 
00411     def testSearchRespectsLocalRoleAcquisitionDisabled(self):
00412         # After adding a group with access rights and containing user2,
00413         # a search should not find documents in subfolders which have
00414         # disabled local role acquisition.
00415         groupname = self.addUser2ToGroup()
00416         self.setRequestMethod('POST')
00417         self.folder.folder_localrole_edit('add', [groupname], 'Owner')
00418         # Acquisition off for folder2
00419         self.folder.folder2.folder_localrole_set(use_acquisition=0)
00420         self.setRequestMethod('GET')
00421         # Everything in subfolder should be invisible
00422         self.login(user2)
00423         self.failIf(self.catalog(SearchableText='bar'))
00424 
00425 
00426 class TestCatalogSorting(PloneTestCase.PloneTestCase):
00427 
00428     def afterSetUp(self):
00429         self.catalog = self.portal.portal_catalog
00430 
00431         self.folder.invokeFactory('Document', id='doc', text='foo')
00432         self.folder.doc.setTitle('12 Document 25')
00433         self.folder.invokeFactory('Document', id='doc2', text='foo')
00434         self.folder.doc2.setTitle('3 Document 4')
00435         self.folder.invokeFactory('Document', id='doc3', text='foo')
00436         self.folder.doc3.setTitle('12 Document 4')
00437 
00438         self.folder.invokeFactory('Document', id='doc4', text='bar')
00439         self.folder.doc4.setTitle('document 12')
00440         self.folder.invokeFactory('Document', id='doc5', text='bar')
00441         self.folder.doc5.setTitle('Document 2')
00442         self.folder.invokeFactory('Document', id='doc6', text='bar')
00443         self.folder.doc6.setTitle('DOCUMENT 4')
00444         self.folder.doc.reindexObject()
00445         self.folder.doc2.reindexObject()
00446         self.folder.doc3.reindexObject()
00447         self.folder.doc4.reindexObject()
00448         self.folder.doc5.reindexObject()
00449         self.folder.doc6.reindexObject()
00450 
00451     def testSortTitleReturnsProperOrderForNumbers(self):
00452         # Documents should be returned in proper numeric order
00453         results = self.catalog(SearchableText='foo', sort_on='sortable_title')
00454         self.assertEqual(results[0].getId, 'doc2')
00455         self.assertEqual(results[1].getId, 'doc3')
00456         self.assertEqual(results[2].getId, 'doc')
00457 
00458     def testSortTitleIgnoresCase(self):
00459         # Documents should be returned in case insensitive order
00460         results = self.catalog(SearchableText='bar', sort_on='sortable_title')
00461         self.assertEqual(results[0].getId, 'doc5')
00462         self.assertEqual(results[1].getId, 'doc6')
00463         self.assertEqual(results[2].getId, 'doc4')
00464 
00465     def testSortableTitleOutput(self):
00466         doc = self.folder.doc
00467         wrapped = ExtensibleIndexableObjectWrapper(doc, self.portal)
00468         wrapped.update(vars={})
00469 
00470         self.assertEqual(wrapped.sortable_title, u'00000012 document 00000025')
00471 
00472     def testSortableNonASCIITitles(self):
00473         #test a utf-8 encoded string gets properly unicode converted
00474         title = 'La Pe\xc3\xb1a'
00475         doc = self.folder.doc
00476         doc.setTitle(title)
00477         wrapped = ExtensibleIndexableObjectWrapper(doc, self.portal)
00478         wrapped.update(vars={})
00479         self.assertEqual(wrapped.sortable_title, u'la pe\xf1a'.encode('utf-8'))
00480 
00481 
00482 class TestFolderCataloging(PloneTestCase.PloneTestCase):
00483     # Tests for http://dev.plone.org/plone/ticket/2876
00484     # folder_edit must recatalog. folder_rename must recatalog.
00485 
00486     def afterSetUp(self):
00487         self.catalog = self.portal.portal_catalog
00488         self.folder.invokeFactory('Folder', id='foo')
00489         self.setupAuthenticator()
00490 
00491     def testFolderTitleIsUpdatedOnEdit(self):
00492         # Test for catalog that searches to ensure folder titles are
00493         # updated in the catalog.
00494         title = 'Test Folder - Snooze!'
00495         self.folder.foo.folder_edit(title, '')
00496         results = self.catalog(Title='Snooze')
00497         self.failUnless(results)
00498         for result in results:
00499             self.assertEqual(result.Title, title)
00500             self.assertEqual(result.getId, 'foo')
00501 
00502     def testFolderTitleIsUpdatedOnRename(self):
00503         # Test for catalog that searches to ensure folder titles are
00504         # updated in the catalog.
00505         title = 'Test Folder - Snooze!'
00506         transaction.savepoint(optimistic=True) # make rename work
00507         self.folder.foo.folder_edit(title, '', id='bar')
00508         results = self.catalog(Title='Snooze')
00509         self.failUnless(results)
00510         for result in results:
00511             self.assertEqual(result.Title, title)
00512             self.assertEqual(result.getId, 'bar')
00513 
00514     def testFolderTitleIsUpdatedOnFolderTitleChange(self):
00515         # The bug in fact talks about folder_rename
00516         title = 'Test Folder - Snooze!'
00517         foo_path = '/'.join(self.folder.foo.getPhysicalPath())
00518         self.setRequestMethod('POST')
00519         self.folder.folder_rename(paths=[foo_path], new_ids=['foo'], new_titles=[title])
00520         results = self.catalog(Title='Snooze')
00521         self.failUnless(results)
00522         for result in results:
00523             self.assertEqual(result.Title, title)
00524             self.assertEqual(result.getId, 'foo')
00525 
00526     def testFolderTitleIsUpdatedOnFolderRename(self):
00527         # The bug in fact talks about folder_rename
00528         title = 'Test Folder - Snooze!'
00529         transaction.savepoint(optimistic=True) # make rename work
00530         foo_path = '/'.join(self.folder.foo.getPhysicalPath())
00531         self.setRequestMethod('POST')
00532         self.folder.folder_rename(paths=[foo_path], new_ids=['bar'], new_titles=[title])
00533         results = self.catalog(Title='Snooze')
00534         self.failUnless(results)
00535         for result in results:
00536             self.assertEqual(result.Title, title)
00537             self.assertEqual(result.getId, 'bar')
00538 
00539     def testSetTitleDoesNotUpdateCatalog(self):
00540         # setTitle() should not update the catalog
00541         title = 'Test Folder - Snooze!'
00542         self.failUnless(self.catalog(getId='foo'))
00543         self.folder.foo.setTitle(title)
00544         #Title is a TextIndex
00545         self.failIf(self.catalog(Title='Snooze'))
00546 
00547 
00548 class TestCatalogOrdering(PloneTestCase.PloneTestCase):
00549 
00550     def afterSetUp(self):
00551         self.catalog = self.portal.portal_catalog
00552         self.folder.invokeFactory('Document', id='doc1', text='foo')
00553         self.folder.invokeFactory('Document', id='doc2', text='bar')
00554         self.folder.invokeFactory('Document', id='doc3', text='bloo')
00555         self.folder.invokeFactory('Document', id='doc4', text='blee')
00556 
00557     def testInitialOrder(self):
00558         self.failUnlessEqual(self.folder.getObjectPosition('doc1'), 0)
00559         self.failUnlessEqual(self.folder.getObjectPosition('doc2'), 1)
00560         self.failUnlessEqual(self.folder.getObjectPosition('doc3'), 2)
00561         self.failUnlessEqual(self.folder.getObjectPosition('doc4'), 3)
00562 
00563     def testOrderIsUpdatedOnMoveDown(self):
00564         self.folder.folder_position('down','doc1')
00565         folder_docs = self.catalog(portal_type = 'Document',
00566                                    path = self.folder.getPhysicalPath(),
00567                                    sort_on = 'getObjPositionInParent')
00568         expected = ['doc2','doc1','doc3','doc4', 'front-page']
00569         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00570 
00571     def testOrderIsUpdatedOnMoveUp(self):
00572         self.folder.folder_position('up','doc3')
00573         folder_docs = self.catalog(portal_type = 'Document',
00574                                    path = self.folder.getPhysicalPath(),
00575                                    sort_on = 'getObjPositionInParent')
00576         expected = ['doc1','doc3','doc2','doc4', 'front-page']
00577         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00578 
00579     def testOrderIsUpdatedOnMoveTop(self):
00580         self.folder.folder_position('top','doc3')
00581         folder_docs = self.catalog(portal_type = 'Document',
00582                                    path = self.folder.getPhysicalPath(),
00583                                    sort_on = 'getObjPositionInParent')
00584         expected = ['doc3','doc1','doc2','doc4', 'front-page']
00585         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00586 
00587     def testOrderIsUpdatedOnMoveBottom(self):
00588         self.folder.folder_position('bottom','doc3')
00589         folder_docs = self.catalog(portal_type = 'Document',
00590                                    path = self.folder.getPhysicalPath(),
00591                                    sort_on = 'getObjPositionInParent')
00592         expected = ['doc1','doc2','doc4','doc3', 'front-page']
00593         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00594 
00595     def testOrderIsFineWithObjectCreation(self):
00596         self.folder.invokeFactory('Document', id='doc5', text='blam')
00597         folder_docs = self.catalog(portal_type = 'Document',
00598                                    path = self.folder.getPhysicalPath(),
00599                                    sort_on = 'getObjPositionInParent')
00600         expected = ['doc1','doc2','doc3','doc4','doc5', 'front-page']
00601         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00602 
00603     def testOrderIsFineWithObjectDeletion(self):
00604         self.folder.manage_delObjects(['doc3',])
00605         folder_docs = self.catalog(portal_type = 'Document',
00606                                    path = self.folder.getPhysicalPath(),
00607                                    sort_on = 'getObjPositionInParent')
00608         expected = ['doc1','doc2','doc4', 'front-page']
00609         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00610 
00611     def testOrderIsFineWithObjectRenaming(self):
00612 
00613         # I don't know why this is failing. manage_renameObjects throws an error
00614         # that blames permissions or lack of support by the obj. The obj is a
00615         # Plone Document, and the owner of doc2 is portal_owner. Harumph.
00616 
00617         transaction.savepoint(optimistic=True)
00618 
00619         self.folder.manage_renameObjects(['doc2'], ['buzz'])
00620         folder_docs = self.catalog(portal_type = 'Document',
00621                                    path = self.folder.getPhysicalPath(),
00622                                    sort_on = 'getObjPositionInParent')
00623         expected = ['doc1','buzz','doc3','doc4', 'front-page']
00624         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00625 
00626     def testOrderAfterALotOfChanges(self):
00627         # ['doc1','doc2','doc3','doc4']
00628 
00629         self.folder.folder_position('down','doc1')
00630         self.folder.folder_position('down','doc1')
00631         # ['doc2','doc3','doc1','doc4']
00632 
00633         self.folder.folder_position('top','doc3')
00634         # ['doc3','doc2','doc1','doc4']
00635 
00636         self.folder.invokeFactory('Document', id='doc5', text='blam')
00637         self.folder.invokeFactory('Document', id='doc6', text='blam')
00638         self.folder.invokeFactory('Document', id='doc7', text='blam')
00639         self.folder.invokeFactory('Document', id='doc8', text='blam')
00640         # ['doc3','doc2','doc1','doc4','doc5','doc6','doc7','doc8',]
00641 
00642         #self.folder.manage_renameObjects('Document', id='doc5', text='blam')
00643 
00644         self.folder.manage_delObjects(['doc3','doc4','doc5','doc7'])
00645         expected = ['doc2','doc1','doc6','doc8', 'front-page']
00646 
00647         folder_docs = self.catalog(portal_type = 'Document',
00648                                    path = self.folder.getPhysicalPath(),
00649                                    sort_on = 'getObjPositionInParent')
00650         self.failUnlessEqual([b.getId for b in folder_docs], expected)
00651 
00652     def testAllObjectsHaveOrder(self):
00653         #Make sure that a query with sort_on='getObjPositionInParent'
00654         #returns the same number of results as one without, make sure
00655         #the Members folder is in the catalog and has getObjPositionInParent
00656         all_objs = self.catalog()
00657         sorted_objs = self.catalog(sort_on='getObjPositionInParent')
00658         self.failUnlessEqual(len(all_objs), len(sorted_objs))
00659 
00660         members = self.portal.Members
00661         members_path = '/'.join(members.getPhysicalPath())
00662         members_query = self.catalog(path=members_path)
00663         members_sorted = self.catalog(path=members_path, sort_on = 'getObjPositionInParent')
00664         self.failUnless(len(members_query))
00665         self.failUnlessEqual(len(members_query),len(members_sorted))
00666 
00667 
00668 class TestCatalogBugs(PloneTestCase.PloneTestCase):
00669 
00670     def afterSetUp(self):
00671         self.catalog = self.portal.portal_catalog
00672 
00673     def testCanPastePortalIfLexiconExists(self):
00674         # Should be able to copy/paste a portal containing
00675         # a catalog tool. Triggers manage_afterAdd of portal_catalog
00676         # thereby exposing a bug which is now going to be fixed.
00677         self.loginAsPortalOwner()
00678         cb = self.app.manage_copyObjects([portal_name])
00679         self.app.manage_pasteObjects(cb)
00680         self.failUnless(hasattr(self.app, 'copy_of_'+portal_name))
00681 
00682     def testCanPasteCatalog(self):
00683         # Should be able to copy/paste a portal_catalog. Triggers
00684         # manage_afterAdd of portal_catalog thereby exposing another bug :-/
00685         self.setRoles(['Manager'])
00686         self.catalog.__replaceable__ = REPLACEABLE
00687         cb = self.portal.manage_copyObjects(['portal_catalog'])
00688         self.folder.manage_pasteObjects(cb)
00689         self.failUnless(hasattr(aq_base(self.folder), 'portal_catalog'))
00690 
00691     def testPastingCatalogPreservesTextIndexes(self):
00692         # Pasting the catalog should not cause indexes to be removed.
00693         self.setRoles(['Manager'])
00694         self.catalog.__replaceable__ = REPLACEABLE
00695         cb = self.portal.manage_copyObjects(['portal_catalog'])
00696         self.folder.manage_pasteObjects(cb)
00697         self.failUnless(hasattr(aq_base(self.folder), 'portal_catalog'))
00698         cat = self.folder.portal_catalog
00699         self.failUnless('SearchableText' in cat.indexes())
00700         # CMF added lexicons should stick around too
00701         self.failUnless(hasattr(aq_base(cat), 'plaintext_lexicon'))
00702 
00703     def testCanRenamePortalIfLexiconExists(self):
00704         # Should be able to rename a Plone portal
00705         # This test is to demonstrate that http://dev.plone.org/plone/ticket/1745
00706         # is fixed and can be closed.
00707         self.loginAsPortalOwner()
00708         self.app.manage_renameObjects([portal_name], ['foo'])
00709         self.failUnless(hasattr(self.app, 'foo'))
00710 
00711 
00712 class TestCatalogUnindexing(PloneTestCase.PloneTestCase):
00713     # Tests for http://dev.plone.org/plone/ticket/3547
00714     # Published objects are not unindexed on delete?
00715 
00716     def afterSetUp(self):
00717         self.catalog = self.portal.portal_catalog
00718         self.workflow = self.portal.portal_workflow
00719         self.folder.invokeFactory('Document', id='doc')
00720         self.setupAuthenticator()
00721 
00722     def testVisibleIsDefault(self):
00723         state = self.workflow.getInfoFor(self.folder.doc, 'review_state')
00724         self.assertEqual(state, 'visible')
00725 
00726     def testVisibleCanBeFound(self):
00727         self.failUnless(self.catalog(getId='doc'))
00728 
00729     def testVisibleIsUnindexed(self):
00730         self.folder._delObject('doc')
00731         self.failIf(self.catalog(getId='doc'))
00732 
00733     def testPrivateCanBeFound(self):
00734         self.workflow.doActionFor(self.folder.doc, 'hide')
00735         self.failUnless(self.catalog(getId='doc'))
00736 
00737     def testPrivateIsUnindexed(self):
00738         self.workflow.doActionFor(self.folder.doc, 'hide')
00739         self.folder._delObject('doc')
00740         self.failIf(self.catalog(getId='doc'))
00741 
00742     def testPendingCanBeFound(self):
00743         self.workflow.doActionFor(self.folder.doc, 'submit')
00744         self.failUnless(self.catalog(getId='doc'))
00745 
00746     def testPendingIsUnindexed(self):
00747         self.workflow.doActionFor(self.folder.doc, 'submit')
00748         self.folder._delObject('doc')
00749         self.failIf(self.catalog(getId='doc'))
00750 
00751     def testPublishedCanBeFound(self):
00752         self.setRoles(['Manager'])
00753         self.workflow.doActionFor(self.folder.doc, 'publish')
00754         self.failUnless(self.catalog(getId='doc'))
00755 
00756     def testPublishedIsUnindexed(self):
00757         # Works here!
00758         self.setRoles(['Manager'])
00759         self.workflow.doActionFor(self.folder.doc, 'publish')
00760         self.folder._delObject('doc')
00761         self.failIf(self.catalog(getId='doc'))
00762 
00763     def testPublishedIsUnindexedIfOwnerDeletes(self):
00764         # Works here!
00765         self.setRoles(['Manager'])
00766         self.workflow.doActionFor(self.folder.doc, 'publish')
00767         self.setRoles(['Member'])
00768         self.folder._delObject('doc')
00769         self.failIf(self.catalog(getId='doc'))
00770 
00771     def testPublishedIsUnindexedByFolderDeleteScript(self):
00772         # Works here too!
00773         self.setRoles(['Manager'])
00774         self.workflow.doActionFor(self.folder.doc, 'publish')
00775         self.setRoles(['Member'])
00776         doc_path = '/'.join(self.folder.doc.getPhysicalPath())
00777         self.app.REQUEST.set('paths', [doc_path])
00778         # folder_delete requires a non-GET request
00779         self.setRequestMethod('POST')
00780         self.folder.folder_delete()
00781         self.setRequestMethod('GET')
00782         self.failIf(self.catalog(getId='doc'))
00783 
00784     def testPublishedIsUnindexedWhenDeletingParentFolder(self):
00785         # Works here too!
00786         self.setRoles(['Manager'])
00787         self.workflow.doActionFor(self.folder.doc, 'publish')
00788         self.setRoles(['Member'])
00789         self.folder.aq_parent._delObject(self.folder.getId())
00790         self.failIf(self.catalog(getId='doc'))
00791 
00792 
00793 class TestCatalogExpirationFiltering(PloneTestCase.PloneTestCase):
00794 
00795     def afterSetUp(self):
00796         self.catalog = self.portal.portal_catalog
00797         self.folder.invokeFactory('Document', id='doc')
00798 
00799     def nofx(self):
00800         # Removes effective and expires to make sure we only test
00801         # the DateRangeIndex.
00802         self.catalog.delIndex('effective')
00803         self.catalog.delIndex('expires')
00804 
00805     def assertResults(self, result, expect):
00806         # Verifies ids of catalog results against expected ids
00807         lhs = [r.getId for r in result]
00808         lhs.sort()
00809         rhs = list(expect)
00810         rhs.sort()
00811         self.assertEqual(lhs, rhs)
00812 
00813     def testCeilingPatch(self):
00814         self.assertEqual(self.folder.doc.expires(), DateTime(2500, 0))
00815 
00816     def testSearchResults(self):
00817         res = self.catalog.searchResults()
00818         self.assertResults(res, base_content)
00819 
00820     def testCall(self):
00821         res = self.catalog()
00822         self.assertResults(res, base_content)
00823 
00824     def testSearchResultsExpired(self):
00825         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00826         self.folder.doc.reindexObject()
00827         self.nofx()
00828         res = self.catalog.searchResults()
00829         self.assertResults(res, base_content[:-1])
00830 
00831     def testCallExpired(self):
00832         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00833         self.folder.doc.reindexObject()
00834         self.nofx()
00835         res = self.catalog()
00836         self.assertResults(res, base_content[:-1])
00837 
00838     def testSearchResultsExpiredWithExpiredDisabled(self):
00839         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00840         self.folder.doc.reindexObject()
00841         self.nofx()
00842         res = self.catalog.searchResults(show_inactive=True)
00843         self.assertResults(res, base_content)
00844 
00845     def testCallExpiredWithExpiredDisabled(self):
00846         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00847         self.folder.doc.reindexObject()
00848         self.nofx()
00849         res = self.catalog(show_inactive=True)
00850         self.assertResults(res, base_content)
00851 
00852     def testSearchResultsExpiredWithPermission(self):
00853         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00854         self.folder.doc.reindexObject()
00855         self.nofx()
00856         self.setPermissions([AccessInactivePortalContent])
00857         res = self.catalog.searchResults()
00858         self.assertResults(res, base_content)
00859 
00860     def testCallExpiredWithPermission(self):
00861         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00862         self.folder.doc.reindexObject()
00863         self.nofx()
00864         self.setPermissions([AccessInactivePortalContent])
00865         res = self.catalog()
00866         self.assertResults(res, base_content)
00867 
00868     def testSearchResultsWithAdditionalExpiryFilter(self):
00869         # For this test we want the expires and effective indices in place,
00870         # let's make sure everything still works
00871         self.folder.doc.setExpirationDate(DateTime(2000, 12, 31))
00872         self.folder.doc.reindexObject()
00873         res = self.catalog.searchResults()
00874         self.assertResults(res, base_content[:-1])
00875         # Now make the object expire at some fixed date in the future
00876         self.folder.doc.setExpirationDate(DateTime()+2)
00877         self.folder.doc.reindexObject()
00878         res = self.catalog.searchResults()
00879         self.assertResults(res, base_content)
00880         # We should be able to further limit the search using the exipres
00881         # and efective indices.
00882         res = self.catalog.searchResults(expires={'query':DateTime()+3,
00883                                                   'range':'min'})
00884         self.assertResults(res, base_content[:-1])
00885 
00886     def testSearchResultsExpiredWithAdditionalExpiryFilter(self):
00887         # Now make the object expire at some date in the recent past
00888         self.folder.doc.setExpirationDate(DateTime()-2)
00889         self.folder.doc.reindexObject()
00890         res = self.catalog.searchResults()
00891         self.assertResults(res, base_content[:-1])
00892         # Even if we explicitly ask for it, we shouldn't get expired content
00893         res = self.catalog.searchResults(expires={'query':DateTime()-3,
00894                                                   'range':'min'})
00895         self.assertResults(res, base_content[:-1])
00896 
00897 
00898 def dummyMethod(obj, **kwargs):
00899     return 'a dummy'
00900 
00901 class TestExtensibleIndexableObjectWrapper(PloneTestCase.PloneTestCase):
00902     """Tests for the wrapper
00903     """
00904 
00905     def afterSetUp(self):
00906         self.folder.invokeFactory('Document', 'doc', title='document')
00907         self.doc = self.folder.doc
00908         _eioRegistry.register('dummy', dummyMethod)
00909 
00910     def testSetup(self):
00911         doc = self.doc
00912         self.failUnlessEqual(doc.getId(), 'doc')
00913         self.failUnlessEqual(doc.Title(), 'document')
00914 
00915     def testWrapper(self):
00916         doc = self.doc
00917         vars = {'var' : 'a var'}
00918         wrapped = ExtensibleIndexableObjectWrapper(doc, self.portal)
00919         wrapped.update(vars)
00920         self.failUnlessEqual(wrapped.var, 'a var')
00921         self.failUnlessEqual(wrapped.getId(), 'doc')
00922         self.failUnlessEqual(wrapped.Title(), 'document')
00923         self.failUnlessEqual(wrapped.dummy, 'a dummy')
00924 
00925     def beforeTearDown(self):
00926         _eioRegistry.unregister('dummy')
00927 
00928     def test_is_folderishWithNonFolder(self):
00929         i = dummy.Item()
00930         self.failIf(is_folderish(i))
00931 
00932     def test_is_folderishWithFolder(self):
00933         f = dummy.Folder('struct_folder')
00934         self.failUnless(is_folderish(f))
00935 
00936     def test_is_folderishWithNonStructuralFolder(self):
00937         f = dummy.NonStructuralFolder('ns_folder')
00938         self.failIf(is_folderish(f))
00939 
00940     def test_provided(self):
00941         from Products.CMFCore.interfaces import IContentish
00942         from Products.CMFCore.interfaces import IIndexableObjectWrapper
00943         from Products.CMFCore.tests.base.dummy import DummyContent
00944 
00945         obj = DummyContent()
00946         w = ExtensibleIndexableObjectWrapper(obj, self.portal)
00947         w.update(vars={})
00948         self.failUnless(IContentish.providedBy(w))
00949         self.failUnless(IIndexableObjectWrapper.providedBy(w))
00950 
00951     def test_getIcon(self):
00952         doc = self.doc
00953         iconname = doc.getIcon(relative_to_portal=1)
00954         wrapped = ExtensibleIndexableObjectWrapper(doc, self.portal)
00955         self.failUnlessEqual(wrapped.getIcon, iconname)
00956 
00957 
00958 class TestObjectProvidedIndexExtender(unittest.TestCase):
00959     def _index(self, object):
00960         from Products.CMFPlone.CatalogTool import object_provides
00961         return object_provides(object, None)
00962     
00963     def testNoInterfaces(self):
00964         class Dummy(object):
00965             pass
00966         self.assertEqual(self._index(Dummy()), ['zope.interface.Interface'])
00967         
00968     def testSimpleInterface(self):
00969         class IDummy(zope.interface.Interface):
00970             pass
00971         class Dummy(object):
00972             zope.interface.implements(IDummy)
00973         self.assertEqual(self._index(Dummy()), [
00974             'Products.CMFPlone.tests.testCatalogTool.IDummy',
00975             'zope.interface.Interface'])
00976 
00977 
00978 def test_suite():
00979     from unittest import TestSuite, makeSuite
00980     suite = TestSuite()
00981     suite.addTest(makeSuite(TestCatalogSetup))
00982     suite.addTest(makeSuite(TestCatalogIndexing))
00983     suite.addTest(makeSuite(TestCatalogSearching))
00984     suite.addTest(makeSuite(TestFolderCataloging))
00985     suite.addTest(makeSuite(TestCatalogOrdering))
00986     suite.addTest(makeSuite(TestCatalogBugs))
00987     suite.addTest(makeSuite(TestCatalogUnindexing))
00988     suite.addTest(makeSuite(TestCatalogExpirationFiltering))
00989     suite.addTest(makeSuite(TestExtensibleIndexableObjectWrapper))
00990     suite.addTest(makeSuite(TestCatalogSorting))
00991     suite.addTest(makeSuite(TestObjectProvidedIndexExtender))
00992     return suite