mò .á¸Ec@sdZdkZdkZdkZdkZdkZdkZdklZlZdk l Z dk l Z dk l Z dklZdklZdklZd klZd klZd klZd fd „ƒYZdfd„ƒYZdfd„ƒYZdeifd„ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZ d„Z!dei"fd„ƒYZ#dk$Z$dk%Z%e&djo$e'e$i(i)e%i(ddƒƒnd e*fd!„ƒYZ+d"„Z,d#„Z-d$„Z.e&djoei/d%d&ƒndS('sNTraverse unit tests. $Id: testTraverse.py 69331 2006-08-01 19:30:42Z alecm $ N(sSecurityManagers Unauthorized(saccess_contents_information(snewSecurityManager(snoSecurityManager(saq_base(s Application(smanage_addFolder(smanage_addFile(s SimpleItem(s makerequesttUnitTestSecurityPolicycBs2tZdZeeeeeed„Zd„ZRS(sN Stub out the existing security policy for unit testing purposes. c OsdS(Ni(( tselftaccessedt containertnametvaluetcontexttrolestargstkw((t5/data/zmath/zope/lib/python/OFS/tests/testTraverse.pytvalidate+s cCsdS(Ni((Rt permissiontobjectR((R tcheckPermission6s(t__name__t __module__t__doc__tNoneR R(((R R$s  tCruelSecurityPolicycBs tZdZd„Zd„ZRS(sDenies everything cGs t|‚dS(N(t UnauthorizedR(RRRRRR((R R @scCsdS(Ni((RR R R((R RCs(RRRR R(((R R:s  tProtectedMethodSecurityPolicycBstZdZd„ZRS(s.Check security strictly on bound methods. cGsˆtt|ƒddƒdjodSn|djo t‚n|ii}t||dt ƒƒ}|djodSnt|ƒ‚dS(Ntim_selfit __roles__( tgetattrtaq_baseRRRRRt __class__tklassR R(RRRRRRRR((R R Js    (RRRR (((R RGs t UnitTestUsercBs)tZdZd„ZeZed„ZRS(s< Stubbed out manager for unit testing purposes. cCsdS(Nt unit_tester((R((R tgetId]scCsdS(Ni((RR t object_roles((R tallowedbs(RRRRt getUserNameRR (((R RYs  tBoboTraversablecBs&tZdZd„Zd„ZdZRS(NicCsp|djotƒi|ƒSnL|djo |iSn4|djo |iSn|djodSnt‚dS(Nt bb_subitemt bb_methodt bb_statust manufacturedi*(RR"t__of__RR$R%tKeyError(RtrequestR((R t__bobo_traverse__is      cCsdS(s Test MethodN((R((R R$ustscreechy(RRt*__allow_access_to_unprotected_subobjects__R*R$R%(((R R"fs t RestrictedcBs5tZdZeZd„ZfZd„Zd„ZRS(s<Instance we'll check with ProtectedMethodSecurityPolicy cCs|iS(N(Rtid(R((R R€scCsdS(Nsprivate!((R((R tprivate„scCsdS(Nsohno!((R((R tohnoˆs( RRRRtgetId__roles__Rtprivate__roles__R/R0(((R R-|s   tBoboTraversableWithAcquisitioncBstZdZd„ZRS(s› A BoboTraversable class which may use acquisition to find objects. This is similar to how the __bobo_traverse__ added by Five behaves). cCsti||ƒS(N(t Acquisitiontaq_getRR(RR)R((R R*’s(RRRR*(((R R3Œs cCs<dk}dkl}|ddd>ƒ}|i|ƒiƒS(N(s DemoStoragetquotaii(tZODBtZODB.DemoStoraget DemoStoragetstDBtopen(R:R7R9((R tmakeConnection–s  t TestTraversecBs¼tZd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Z d „Z d „Z d „Z d „Z d „Zd„Zd„Zd„Zd„Zd„Zd„Zd„ZRS(NcCsLtƒ|_yÆ|iiƒ}tƒ}||d<||_tiƒ}|_ t |id|ƒ|_ t |i dƒt |i dƒ}hdd<dd<dd žs( &         t__main__is framework.pyt SimpleClasscBstZdZRS(s Class with no __bobo_traverse__.(RRR(((R R�„s cCsdS(s� Test the behaviour of unrestrictedTraverse and views. The tests are copies from Five.browser.tests.test_traversable, but instead of publishing they do unrestrictedTraverse. >>> import Products.Five >>> from Products.Five import zcml >>> zcml.load_config("configure.zcml", Products.Five) >>> from Testing.makerequest import makerequest >>> self.app = makerequest(self.app) ``SimpleContent`` is a traversable class by default. Its fallback traverser should raise NotFound when traversal fails. (Note: If we return None in __fallback_traverse__, this test passes but for the wrong reason: None doesn't have a docstring so BaseRequest raises NotFoundError.) >>> from Products.Five.tests.testing.simplecontent import manage_addSimpleContent >>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid') >>> from zExceptions import NotFound >>> try: ... self.folder.testoid.unrestrictedTraverse('doesntexist') ... except NotFound: ... pass Now let's take class which already has a __bobo_traverse__ method. Five should correctly use that as a fallback. >>> configure_zcml = ''' ... ... ... ... ... ... ... ... ... ... ... ''' >>> zcml.load_string(configure_zcml) >>> from Products.Five.tests.testing.fancycontent import manage_addFancyContent >>> info = manage_addFancyContent(self.folder, 'fancy', '') In the following test we let the original __bobo_traverse__ method kick in: >>> self.folder.fancy.unrestrictedTraverse('something-else').index_html({}) 'something-else' Once we have a custom __bobo_traverse__ method, though, it always takes over. Therefore, unless it raises AttributeError or KeyError, it will be the only way traversal is done. >>> self.folder.fancy.unrestrictedTraverse('fancyview').index_html({}) 'fancyview' Note that during publishing, if the original __bobo_traverse__ method *does* raise AttributeError or KeyError, we can get normal view look-up. In unrestrictedTraverse, we don't. Maybe we should? Needs discussing. >>> self.folder.fancy.unrestrictedTraverse('raise-attributeerror')() u'Fancy, fancy' >>> self.folder.fancy.unrestrictedTraverse('raise-keyerror')() u'Fancy, fancy' >>> try: ... self.folder.fancy.unrestrictedTraverse('raise-valueerror') ... except ValueError: ... pass In the Zope 2 ZPublisher, an object with a __bobo_traverse__ will not do attribute lookup unless the __bobo_traverse__ method itself does it (i.e. the __bobo_traverse__ is the only element used for traversal lookup). Let's demonstrate: >>> from Products.Five.tests.testing.fancycontent import manage_addNonTraversableFancyContent >>> info = manage_addNonTraversableFancyContent(self.folder, 'fancy_zope2', '') >>> self.folder.fancy_zope2.an_attribute = 'This is an attribute' >>> self.folder.fancy_zope2.unrestrictedTraverse('an_attribute').index_html({}) 'an_attribute' Without a __bobo_traverse__ method this would have returned the attribute value 'This is an attribute'. Let's make sure the same thing happens for an object that has been marked traversable by Five: >>> self.folder.fancy.an_attribute = 'This is an attribute' >>> self.folder.fancy.unrestrictedTraverse('an_attribute').index_html({}) 'an_attribute' Clean up: >>> from zope.app.testing.placelesssetup import tearDown >>> tearDown() Verify that after cleanup, there's no cruft left from five:traversable:: >>> from Products.Five.browser.tests.test_traversable import SimpleClass >>> hasattr(SimpleClass, '__bobo_traverse__') False >>> hasattr(SimpleClass, '__fallback_traverse__') False >>> from Products.Five.tests.testing.fancycontent import FancyContent >>> hasattr(FancyContent, '__bobo_traverse__') True >>> hasattr(FancyContent.__bobo_traverse__, '__five_method__') False >>> hasattr(FancyContent, '__fallback_traverse__') False N((((R ttest_traversableˆsˆcCsdS(sž Test that views don't shadow attributes, e.g. items in a folder. Let's first define a browser page for object managers called ``eagle``: >>> configure_zcml = ''' ... ... ... ... ... ... ''' >>> import Products.Five >>> from Products.Five import zcml >>> zcml.load_config("configure.zcml", Products.Five) >>> zcml.load_string(configure_zcml) Then we create a traversable folder... >>> from Products.Five.tests.testing.folder import manage_addFiveTraversableFolder >>> manage_addFiveTraversableFolder(self.folder, 'ftf') and add an object called ``eagle`` to it: >>> from Products.Five.tests.testing.simplecontent import manage_addIndexSimpleContent >>> manage_addIndexSimpleContent(self.folder.ftf, 'eagle', 'Eagle') When we publish the ``ftf/eagle`` now, we expect the attribute to take precedence over the view during traversal: >>> self.folder.ftf.unrestrictedTraverse('eagle').index_html({}) 'Default index_html called' Of course, unless we explicitly want to lookup the view using @@: >>> self.folder.ftf.unrestrictedTraverse('@@eagle')() u'The eagle has landed' Some weird implementations of __bobo_traverse__, like the one found in OFS.Application, raise NotFound. Five still knows how to deal with this, hence views work there too: >>> self.app.unrestrictedTraverse('@@eagle')() u'The eagle has landed' However, acquired attributes *should* be shadowed. See discussion on http://codespeak.net/pipermail/z3-five/2006q2/001474.html >>> manage_addIndexSimpleContent(self.folder, 'mouse', 'Mouse') >>> self.folder.ftf.unrestrictedTraverse('mouse')() u'The mouse has been eaten by the eagle' Head requests have some unusual behavior in Zope 2, in particular, a failed item lookup on an ObjectManager returns a NullResource, rather than raising a KeyError. We need to make sure that this doesn't result in acquired attributes being shadowed by the NullResource, but that unknown names still give NullResources: >>> self.app.REQUEST.maybe_webdav_client = True >>> self.app.REQUEST['REQUEST_METHOD'] = 'HEAD' >>> self.folder.ftf.unrestrictedTraverse('mouse')() u'The mouse has been eaten by the eagle' >>> self.folder.ftf.unrestrictedTraverse('nonsense') Clean up: >>> from zope.app.testing.placelesssetup import tearDown >>> tearDown() N((((R t!test_view_doesnt_shadow_attributesXcCsCtiƒ}|ititƒƒdkl}|i|ƒƒ|S(N(sFunctionalDocTestSuite(tunittestt TestSuitetsuitetaddTestt makeSuiteR>tTesting.ZopeTestCasetFunctionalDocTestSuite(R’R–((R t test_suitels   t defaultTestR—(0RR�RLRSR7R4t AccessControlRXRtAccessControl.PermissionsR‚t AccessControl.SecurityManagementR[R]RtOFS.ApplicationR?t OFS.FolderRQt OFS.ImageRDtOFS.SimpleItemt SimpleItemtTesting.makerequestRORRRtImplicitRR"R-R3R=tTestCaseR>tostsysRtexecfiletpathtjoinR R�RŽR�R—tmain(RLRXR]R�RR?R3RQR�R>RŽRR�R-RDRR"RRR[R‚R¥R R7R4RSR=RORR—R¤((R t?s@               á $ Š Z