Back to index

plone3  3.1.7
test_CachingPolicyManager.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """ Unit tests for CachingPolicyManager module.
00014 
00015 $Id: test_CachingPolicyManager.py 77113 2007-06-26 20:36:26Z yuppie $
00016 """
00017 
00018 import unittest
00019 import Testing
00020 
00021 import base64
00022 import os
00023 from os.path import join as path_join
00024 
00025 from AccessControl.SecurityManagement import newSecurityManager
00026 from Acquisition import Implicit
00027 from App.Common import rfc1123_date
00028 from DateTime.DateTime import DateTime
00029 from OFS.Cache import Cacheable
00030 
00031 from Products.CMFCore.FSDTMLMethod import FSDTMLMethod
00032 from Products.CMFCore.FSPageTemplate import FSPageTemplate
00033 from Products.CMFCore.testing import FunctionalZCMLLayer
00034 from Products.CMFCore.testing import TraversingZCMLLayer
00035 from Products.CMFCore.tests.base.dummy import DummyContent
00036 from Products.CMFCore.tests.base.dummy import DummySite
00037 from Products.CMFCore.tests.base.dummy import DummyTool
00038 from Products.CMFCore.tests.base.testcase import FSDVTest
00039 from Products.CMFCore.tests.base.testcase import RequestTest
00040 
00041 ACCLARK = DateTime( '2001/01/01' )
00042 portal_owner = 'portal_owner'
00043 
00044 
00045 class DummyContent2:
00046 
00047     __allow_access_to_unprotected_subobjects__ = 1
00048 
00049     def __init__(self, modified ):
00050         self.modified = modified
00051 
00052     def Type( self ):
00053         return 'Dummy'
00054 
00055     def modified( self ):
00056         return self.modified
00057 
00058 class CacheableDummyContent(Implicit, Cacheable):
00059 
00060     __allow_access_to_unprotected_subobjects__ = 1
00061 
00062     def __init__(self, id):
00063         self.id = id
00064         self.modified = DateTime()
00065 
00066     def getId(self):
00067         """ """
00068         return self.id
00069 
00070     def modified( self ):
00071         return self.modified
00072 
00073     def __call__(self):
00074         """ """
00075         if self.ZCacheable_isCachingEnabled():
00076             result = self.ZCacheable_get(default=None)
00077             if result is not None:
00078                 # We will always get None from RAMCacheManager and HTTP
00079                 # Accelerated Cache Manager but we will get
00080                 # something implementing the IStreamIterator interface
00081                 # from a "FileCacheManager"
00082                 return result
00083 
00084         self.ZCacheable_set(None)
00085 
00086 class DummyView(CacheableDummyContent):
00087 
00088     meta_type = 'DTML Method'
00089 
00090 
00091 class CachingPolicyTests(unittest.TestCase):
00092 
00093     layer = TraversingZCMLLayer
00094 
00095     def _makePolicy( self, policy_id, **kw ):
00096         from Products.CMFCore.CachingPolicyManager import CachingPolicy
00097 
00098         return CachingPolicy( policy_id, **kw )
00099 
00100     def _makeContext( self, **kw ):
00101         from Products.CMFCore.CachingPolicyManager import createCPContext
00102         return createCPContext( DummyContent2(self._epoch)
00103                               , 'foo_view', kw, self._epoch )
00104 
00105     def setUp(self):
00106         self._epoch = DateTime(0)
00107 
00108     def test_z3interfaces(self):
00109         from zope.interface.verify import verifyClass
00110         from Products.CMFCore.CachingPolicyManager import CachingPolicy
00111         from Products.CMFCore.interfaces import ICachingPolicy
00112 
00113         verifyClass(ICachingPolicy, CachingPolicy)
00114 
00115     def test_empty( self ):
00116         policy = self._makePolicy( 'empty' )
00117         context = self._makeContext()
00118         headers = policy.getHeaders( context )
00119 
00120         self.assertEqual( len( headers ), 1 )
00121         self.assertEqual( headers[0][0], 'Last-modified' )
00122         self.assertEqual( headers[0][1]
00123                         , rfc1123_date(self._epoch.timeTime()) )
00124 
00125     def test_noPassPredicate( self ):
00126 
00127         policy = self._makePolicy( 'noPassPredicate', predicate='nothing' )
00128         context = self._makeContext()
00129         headers = policy.getHeaders( context )
00130 
00131         self.assertEqual( len( headers ), 0 )
00132 
00133     def test_typePredicate( self ):
00134 
00135         policy = self._makePolicy( 'typePredicate'
00136                            , predicate='python:object.Type() == "Dummy"' )
00137         context = self._makeContext()
00138         headers = policy.getHeaders( context )
00139 
00140         self.assertEqual( len( headers ), 1 )
00141         self.assertEqual( headers[0][0] , 'Last-modified' )
00142         self.assertEqual( headers[0][1] , rfc1123_date(self._epoch.timeTime()) )
00143 
00144     def test_typePredicateMiss( self ):
00145 
00146         policy = self._makePolicy( 'typePredicate'
00147                         , predicate='python:object.Type() == "Foolish"' )
00148         context = self._makeContext()
00149         headers = policy.getHeaders( context )
00150 
00151         self.assertEqual( len( headers ), 0 )
00152 
00153     def test_viewPredicate( self ):
00154 
00155         policy = self._makePolicy( 'viewPredicate'
00156                                  , predicate='python:view == "foo_view"' )
00157         context = self._makeContext()
00158         headers = policy.getHeaders( context )
00159 
00160         self.assertEqual( len( headers ), 1 )
00161         self.assertEqual( headers[0][0], 'Last-modified' )
00162         self.assertEqual( headers[0][1]
00163                         , rfc1123_date(self._epoch.timeTime()) )
00164 
00165     def test_viewPredicateMiss( self ):
00166 
00167         policy = self._makePolicy( 'viewPredicateMiss'
00168                            , predicate='python:view == "bar_view"' )
00169         context = self._makeContext()
00170         headers = policy.getHeaders( context )
00171 
00172         self.assertEqual( len( headers ), 0 )
00173 
00174     def test_kwPredicate( self ):
00175 
00176         policy = self._makePolicy( 'kwPredicate'
00177                                  , predicate='python:"foo" in keywords.keys()' )
00178         context = self._makeContext( foo=1 )
00179         headers = policy.getHeaders( context )
00180 
00181         self.assertEqual( len( headers ), 1 )
00182         self.assertEqual( headers[0][0], 'Last-modified' )
00183         self.assertEqual( headers[0][1]
00184                         , rfc1123_date(self._epoch.timeTime()) )
00185 
00186     def test_kwPredicateMiss( self ):
00187 
00188         policy = self._makePolicy( 'kwPredicateMiss'
00189                                  , predicate='python:"foo" in keywords.keys()' )
00190         context = self._makeContext( bar=1 )
00191         headers = policy.getHeaders( context )
00192 
00193         self.assertEqual( len( headers ), 0 )
00194 
00195         context = self._makeContext()
00196         headers = policy.getHeaders( context )
00197 
00198         self.assertEqual( len( headers ), 0 )
00199 
00200     def test_mtimeFunc( self ):
00201 
00202         policy = self._makePolicy( 'mtimeFunc'
00203                                  , mtime_func='string:2001/01/01' )
00204         context = self._makeContext()
00205         headers = policy.getHeaders( context )
00206 
00207         self.assertEqual( len( headers ), 1 )
00208         self.assertEqual( headers[0][0], 'Last-modified' )
00209         self.assertEqual( headers[0][1]
00210                         , rfc1123_date(ACCLARK.timeTime()) )
00211 
00212     def test_mtimeFuncNone( self ):
00213 
00214         policy = self._makePolicy( 'mtimeFuncNone'
00215                                  , mtime_func='nothing' )
00216         context = self._makeContext()
00217         headers = policy.getHeaders( context )
00218 
00219         self.assertEqual( len( headers ), 0 )
00220 
00221     def test_maxAge( self ):
00222 
00223         policy = self._makePolicy( 'aged', max_age_secs=86400 )
00224         context = self._makeContext()
00225         headers = policy.getHeaders( context )
00226 
00227         self.assertEqual( len( headers ), 3 )
00228         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00229         self.assertEqual( headers[0][1]
00230                         , rfc1123_date(self._epoch.timeTime()) )
00231         self.assertEqual( headers[1][0].lower() , 'expires' )
00232         self.assertEqual( headers[1][1]
00233                         , rfc1123_date((self._epoch+1).timeTime()) )
00234         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00235         self.assertEqual( headers[2][1] , 'max-age=86400' )
00236 
00237     def test_sMaxAge( self ):
00238 
00239         policy = self._makePolicy( 's_aged', s_max_age_secs=86400 )
00240         context = self._makeContext()
00241         headers = policy.getHeaders( context )
00242 
00243         self.assertEqual( len( headers ), 2 )
00244         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00245         self.assertEqual( headers[0][1]
00246                         , rfc1123_date(self._epoch.timeTime()) )
00247         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00248         self.assertEqual( headers[1][1] , 's-maxage=86400' )
00249         self.assertEqual(policy.getSMaxAgeSecs(), 86400)
00250 
00251     def test_noCache( self ):
00252 
00253         policy = self._makePolicy( 'noCache', no_cache=1 )
00254         context = self._makeContext()
00255         headers = policy.getHeaders( context )
00256 
00257         self.assertEqual( len( headers ), 3 )
00258         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00259         self.assertEqual( headers[0][1]
00260                         , rfc1123_date(self._epoch.timeTime()) )
00261         self.assertEqual( headers[1][0].lower() , 'pragma' )
00262         self.assertEqual( headers[1][1] , 'no-cache' )
00263         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00264         self.assertEqual( headers[2][1] , 'no-cache' )
00265 
00266     def test_noStore( self ):
00267 
00268         policy = self._makePolicy( 'noStore', no_store=1 )
00269         context = self._makeContext()
00270         headers = policy.getHeaders( context )
00271 
00272         self.assertEqual( len( headers ), 2 )
00273         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00274         self.assertEqual( headers[0][1]
00275                         , rfc1123_date(self._epoch.timeTime()) )
00276         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00277         self.assertEqual( headers[1][1] , 'no-store' )
00278 
00279     def test_mustRevalidate( self ):
00280 
00281         policy = self._makePolicy( 'mustRevalidate', must_revalidate=1 )
00282         context = self._makeContext()
00283         headers = policy.getHeaders( context )
00284 
00285         self.assertEqual( len( headers ), 2 )
00286         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00287         self.assertEqual( headers[0][1]
00288                         , rfc1123_date(self._epoch.timeTime()) )
00289         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00290         self.assertEqual( headers[1][1] , 'must-revalidate' )
00291 
00292     def test_proxyRevalidate( self ):
00293 
00294         policy = self._makePolicy( 'proxyRevalidate', proxy_revalidate=1 )
00295         context = self._makeContext()
00296         headers = policy.getHeaders( context )
00297 
00298         self.assertEqual( len( headers ), 2 )
00299         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00300         self.assertEqual( headers[0][1]
00301                         , rfc1123_date(self._epoch.timeTime()) )
00302         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00303         self.assertEqual( headers[1][1] , 'proxy-revalidate' )
00304         self.assertEqual(policy.getProxyRevalidate(), 1)
00305 
00306     def test_public( self ):
00307 
00308         policy = self._makePolicy( 'public', public=1 )
00309         context = self._makeContext()
00310         headers = policy.getHeaders( context )
00311 
00312         self.assertEqual( len( headers ), 2 )
00313         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00314         self.assertEqual( headers[0][1]
00315                         , rfc1123_date(self._epoch.timeTime()) )
00316         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00317         self.assertEqual( headers[1][1] , 'public' )
00318         self.assertEqual(policy.getPublic(), 1)
00319 
00320     def test_private( self ):
00321 
00322         policy = self._makePolicy( 'private', private=1 )
00323         context = self._makeContext()
00324         headers = policy.getHeaders( context )
00325 
00326         self.assertEqual( len( headers ), 2 )
00327         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00328         self.assertEqual( headers[0][1]
00329                         , rfc1123_date(self._epoch.timeTime()) )
00330         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00331         self.assertEqual( headers[1][1] , 'private' )
00332         self.assertEqual(policy.getPrivate(), 1)
00333 
00334     def test_noTransform( self ):
00335 
00336         policy = self._makePolicy( 'noTransform', no_transform=1 )
00337         context = self._makeContext()
00338         headers = policy.getHeaders( context )
00339 
00340         self.assertEqual( len( headers ), 2 )
00341         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00342         self.assertEqual( headers[0][1]
00343                         , rfc1123_date(self._epoch.timeTime()) )
00344         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00345         self.assertEqual( headers[1][1] , 'no-transform' )
00346         self.assertEqual(policy.getNoTransform(), 1)
00347 
00348     def test_lastModified( self ):
00349 
00350         policy = self._makePolicy( 'lastModified', last_modified=0 )
00351         context = self._makeContext()
00352         headers = policy.getHeaders( context )
00353 
00354         self.assertEqual( len( headers ), 0 )
00355         self.assertEqual(policy.getLastModified(), 0)
00356 
00357     def test_preCheck( self ):
00358 
00359         policy = self._makePolicy( 'preCheck', pre_check=1 )
00360         context = self._makeContext()
00361         headers = policy.getHeaders( context )
00362 
00363         self.assertEqual( len( headers ), 2 )
00364         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00365         self.assertEqual( headers[0][1]
00366                         , rfc1123_date(self._epoch.timeTime()) )
00367         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00368         self.assertEqual( headers[1][1] , 'pre-check=1' )
00369         self.assertEqual(policy.getPreCheck(), 1)
00370         self.assertEqual(policy.getPostCheck(), None)
00371 
00372     def test_postCheck( self ):
00373 
00374         policy = self._makePolicy( 'postCheck', post_check=1 )
00375         context = self._makeContext()
00376         headers = policy.getHeaders( context )
00377 
00378         self.assertEqual( len( headers ), 2 )
00379         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00380         self.assertEqual( headers[0][1]
00381                         , rfc1123_date(self._epoch.timeTime()) )
00382         self.assertEqual( headers[1][0].lower() , 'cache-control' )
00383         self.assertEqual( headers[1][1] , 'post-check=1' )
00384         self.assertEqual(policy.getPostCheck(), 1)
00385         self.assertEqual(policy.getPreCheck(), None)
00386 
00387     def test_ETag( self ):
00388 
00389         # With an empty etag_func, no ETag should be produced
00390         policy = self._makePolicy( 'ETag', etag_func='' )
00391         context = self._makeContext()
00392         headers = policy.getHeaders( context )
00393 
00394         self.assertEqual( len( headers ), 1)
00395         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00396         self.assertEqual( headers[0][1]
00397                         , rfc1123_date(self._epoch.timeTime()) )
00398 
00399         policy = self._makePolicy( 'ETag', etag_func='string:foo' )
00400         context = self._makeContext()
00401         headers = policy.getHeaders( context )
00402 
00403         self.assertEqual( len( headers ), 2)
00404         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00405         self.assertEqual( headers[0][1]
00406                         , rfc1123_date(self._epoch.timeTime()) )
00407         self.assertEqual( headers[1][0].lower(), 'etag' )
00408         self.assertEqual( headers[1][1], 'foo' )
00409 
00410     def test_combined( self ):
00411 
00412         policy = self._makePolicy( 'noStore', no_cache=1, no_store=1 )
00413         context = self._makeContext()
00414         headers = policy.getHeaders( context )
00415 
00416         self.assertEqual( len( headers ), 3 )
00417         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00418         self.assertEqual( headers[0][1]
00419                         , rfc1123_date(self._epoch.timeTime()) )
00420         self.assertEqual( headers[1][0].lower() , 'pragma' )
00421         self.assertEqual( headers[1][1] , 'no-cache' )
00422         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00423         self.assertEqual( headers[2][1] , 'no-cache, no-store' )
00424 
00425 
00426 class CachingPolicyManagerTests(unittest.TestCase):
00427 
00428     layer = TraversingZCMLLayer
00429 
00430     def _makeOne(self, *args, **kw):
00431         from Products.CMFCore.CachingPolicyManager import CachingPolicyManager
00432 
00433         return CachingPolicyManager(*args, **kw)
00434 
00435     def setUp(self):
00436         self._epoch = DateTime()
00437 
00438     def assertEqualDelta( self, lhs, rhs, delta ):
00439         self.failUnless( abs( lhs - rhs ) <= delta )
00440 
00441     def test_z2interfaces(self):
00442         from Interface.Verify import verifyClass
00443         from Products.CMFCore.CachingPolicyManager import CachingPolicyManager
00444         from Products.CMFCore.interfaces.CachingPolicyManager \
00445                 import CachingPolicyManager as ICachingPolicyManager
00446 
00447         verifyClass(ICachingPolicyManager, CachingPolicyManager)
00448 
00449     def test_z3interfaces(self):
00450         from zope.interface.verify import verifyClass
00451         from Products.CMFCore.CachingPolicyManager import CachingPolicyManager
00452         from Products.CMFCore.interfaces import ICachingPolicyManager
00453 
00454         verifyClass(ICachingPolicyManager, CachingPolicyManager)
00455 
00456     def test_empty( self ):
00457 
00458         mgr = self._makeOne()
00459 
00460         self.assertEqual( len( mgr.listPolicies() ), 0 )
00461         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00462                                            , view_method='foo_view'
00463                                            , keywords={}
00464                                            , time=self._epoch
00465                                            )
00466         self.assertEqual( len( headers ), 0 )
00467 
00468         self.assertRaises( KeyError, mgr._updatePolicy,
00469                            'xyzzy', None, None, None, None, None, None, '',
00470                            '', None, None, None, None, None )
00471         self.assertRaises( KeyError, mgr._removePolicy, 'xyzzy' )
00472         self.assertRaises( KeyError, mgr._reorderPolicy, 'xyzzy', -1 )
00473 
00474     def test_addAndUpdatePolicy( self ):
00475 
00476         mgr = self._makeOne()
00477         mgr.addPolicy('first', 'python:1', 'mtime', 1, 0, 1, 0, 'vary',
00478                       'etag', None, 2, 1, 0, 1, 0, 1, 0, 2, 3)
00479         p = mgr._policies['first']
00480         self.assertEqual(p.getPolicyId(), 'first')
00481         self.assertEqual(p.getPredicate(), 'python:1')
00482         self.assertEqual(p.getMTimeFunc(), 'mtime')
00483         self.assertEqual(p.getMaxAgeSecs(), 1)
00484         self.assertEqual(p.getNoCache(), 0)
00485         self.assertEqual(p.getNoStore(), 1)
00486         self.assertEqual(p.getMustRevalidate(), 0)
00487         self.assertEqual(p.getVary(), 'vary')
00488         self.assertEqual(p.getETagFunc(), 'etag')
00489         self.assertEqual(p.getSMaxAgeSecs(), 2)
00490         self.assertEqual(p.getProxyRevalidate(), 1)
00491         self.assertEqual(p.getPublic(), 0)
00492         self.assertEqual(p.getPrivate(), 1)
00493         self.assertEqual(p.getNoTransform(), 0)
00494         self.assertEqual(p.getEnable304s(), 1)
00495         self.assertEqual(p.getLastModified(), 0)
00496         self.assertEqual(p.getPreCheck(), 2)
00497         self.assertEqual(p.getPostCheck(), 3)
00498 
00499         mgr.updatePolicy('first', 'python:0', 'mtime2', 2, 1, 0, 1, 'vary2',
00500                          'etag2', None, 1, 0, 1, 0, 1, 0, 1, 3, 2)
00501         p = mgr._policies['first']
00502         self.assertEqual(p.getPolicyId(), 'first')
00503         self.assertEqual(p.getPredicate(), 'python:0')
00504         self.assertEqual(p.getMTimeFunc(), 'mtime2')
00505         self.assertEqual(p.getMaxAgeSecs(), 2)
00506         self.assertEqual(p.getNoCache(), 1)
00507         self.assertEqual(p.getNoStore(), 0)
00508         self.assertEqual(p.getMustRevalidate(), 1)
00509         self.assertEqual(p.getVary(), 'vary2')
00510         self.assertEqual(p.getETagFunc(), 'etag2')
00511         self.assertEqual(p.getSMaxAgeSecs(), 1)
00512         self.assertEqual(p.getProxyRevalidate(), 0)
00513         self.assertEqual(p.getPublic(), 1)
00514         self.assertEqual(p.getPrivate(), 0)
00515         self.assertEqual(p.getNoTransform(), 1)
00516         self.assertEqual(p.getEnable304s(), 0)
00517         self.assertEqual(p.getLastModified(), 1)
00518         self.assertEqual(p.getPreCheck(), 3)
00519         self.assertEqual(p.getPostCheck(), 2)
00520 
00521     def test_reorder( self ):
00522 
00523         mgr = self._makeOne()
00524 
00525         policy_ids = ( 'foo', 'bar', 'baz', 'qux' )
00526 
00527         for policy_id in policy_ids:
00528             mgr._addPolicy( policy_id
00529                           , 'python:"%s" in keywords.keys()' % policy_id
00530                           , None, 0, 0, 0, 0, '', '' )
00531 
00532         ids = tuple( map( lambda x: x[0], mgr.listPolicies() ) )
00533         self.assertEqual( ids, policy_ids )
00534 
00535         mgr._reorderPolicy( 'bar', 3 )
00536 
00537         ids = tuple( map( lambda x: x[0], mgr.listPolicies() ) )
00538         self.assertEqual( ids, ( 'foo', 'baz', 'qux', 'bar' ) )
00539 
00540     def _makeOneWithPolicies( self ):
00541 
00542         mgr = self._makeOne()
00543 
00544         policy_tuples = ( ( 'foo', None  )
00545                         , ( 'bar', 0     )
00546                         , ( 'baz', 3600  )
00547                         , ( 'qux', 86400 )
00548                         )
00549 
00550         for policy_id, max_age_secs in policy_tuples:
00551             mgr._addPolicy( policy_id
00552                           , 'python:"%s" in keywords.keys()' % policy_id
00553                           , None, max_age_secs, 0, 0, 0, '', '' )
00554 
00555         return mgr
00556 
00557     def test_lookupNoMatch( self ):
00558 
00559         mgr = self._makeOneWithPolicies()
00560         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00561                                            , view_method='foo_view'
00562                                            , keywords={}
00563                                            , time=self._epoch
00564                                            )
00565         self.assertEqual( len( headers ), 0 )
00566 
00567     def test_lookupMatchFoo( self ):
00568 
00569         mgr = self._makeOneWithPolicies()
00570         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00571                                            , view_method='foo_view'
00572                                            , keywords={ 'foo' : 1 }
00573                                            , time=self._epoch
00574                                            )
00575         self.assertEqual( len( headers ), 1 )
00576         self.assertEqual( headers[0][0].lower(), 'last-modified' )
00577         self.assertEqual( headers[0][1]
00578                         , rfc1123_date(self._epoch.timeTime()) )
00579 
00580     def test_lookupMatchBar( self ):
00581 
00582         mgr = self._makeOneWithPolicies()
00583         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00584                                            , view_method='foo_view'
00585                                            , keywords={ 'bar' : 1 }
00586                                            , time=self._epoch
00587                                            )
00588         self.assertEqual( len( headers ), 3 )
00589         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00590         self.assertEqual( headers[0][1]
00591                         , rfc1123_date(self._epoch.timeTime()) )
00592         self.assertEqual( headers[1][0].lower() , 'expires' )
00593         self.assertEqual( headers[1][1]
00594                         , rfc1123_date(self._epoch.timeTime()) )
00595         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00596         self.assertEqual( headers[2][1], 'max-age=0' )
00597 
00598     def test_lookupMatchBaz( self ):
00599 
00600         mgr = self._makeOneWithPolicies()
00601         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00602                                            , view_method='foo_view'
00603                                            , keywords={ 'baz' : 1 }
00604                                            , time=self._epoch
00605                                            )
00606         self.assertEqual( len( headers ), 3 )
00607         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00608         self.assertEqual( headers[0][1]
00609                         , rfc1123_date(self._epoch.timeTime()) )
00610         self.assertEqual( headers[1][0].lower() , 'expires' )
00611 
00612         exp_time = DateTime( headers[1][1] )
00613         target = self._epoch + ( 1.0 / 24.0 )
00614         self.assertEqualDelta( exp_time, target, 0.01 )
00615 
00616         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00617         self.assertEqual( headers[2][1] , 'max-age=3600' )
00618 
00619     def test_lookupMatchQux( self ):
00620 
00621         mgr = self._makeOneWithPolicies()
00622         headers = mgr.getHTTPCachingHeaders( content=DummyContent2(self._epoch)
00623                                            , view_method='foo_view'
00624                                            , keywords={ 'qux' : 1 }
00625                                            , time=self._epoch
00626                                            )
00627         self.assertEqual( len( headers ), 3 )
00628         self.assertEqual( headers[0][0].lower() , 'last-modified' )
00629         self.assertEqual( headers[0][1]
00630                         , rfc1123_date(self._epoch.timeTime()) )
00631         self.assertEqual( headers[1][0].lower() , 'expires' )
00632 
00633         exp_time = DateTime( headers[1][1] )
00634         target = self._epoch + 1.0
00635         self.assertEqualDelta( exp_time, target, 0.01 )
00636 
00637         self.assertEqual( headers[2][0].lower() , 'cache-control' )
00638         self.assertEqual( headers[2][1] , 'max-age=86400' )
00639 
00640 
00641 class CachingPolicyManager304Tests(RequestTest, FSDVTest):
00642 
00643     layer = FunctionalZCMLLayer
00644 
00645     def setUp(self):
00646         from Products.CMFCore import CachingPolicyManager
00647 
00648         RequestTest.setUp(self)
00649         FSDVTest.setUp(self)
00650 
00651         now = DateTime()
00652 
00653         # Create a fake portal and the tools we need
00654         self.portal = DummySite(id='portal').__of__(self.root)
00655         self.portal._setObject('portal_types', DummyTool())
00656 
00657         # This is a FSPageTemplate that will be used as the View for
00658         # our content objects. It doesn't matter what it returns.
00659         path = os.path.join(self.skin_path_name, 'testPT2.pt')
00660         self.portal._setObject('dummy_view', FSPageTemplate('dummy_view', path))
00661 
00662         uf = self.root.acl_users
00663         password = 'secret'
00664         uf.userFolderAddUser(portal_owner, password, ['Manager'], [])
00665         user = uf.getUserById(portal_owner)
00666         if not hasattr(user, 'aq_base'):
00667             user = user.__of__(uf)
00668         newSecurityManager(None, user)
00669         owner_auth = '%s:%s' % (portal_owner, password)
00670         self.auth_header = "Basic %s" % base64.encodestring(owner_auth)
00671 
00672         self.portal._setObject('doc1', DummyContent('doc1'))
00673         self.portal._setObject('doc2', DummyContent('doc2'))
00674         self.portal._setObject('doc3', DummyContent('doc3'))
00675         self.portal.doc1.modified_date = now
00676         self.portal.doc2.modified_date = now
00677         self.portal.doc3.modified_date = now
00678 
00679         CachingPolicyManager.manage_addCachingPolicyManager(self.portal)
00680         cpm = self.portal.caching_policy_manager
00681 
00682         # This policy only applies to doc1. It will not emit any ETag header
00683         # but it enables If-modified-since handling.
00684         cpm.addPolicy(policy_id = 'policy_no_etag',
00685                       predicate = 'python:object.getId()=="doc1"',
00686                       mtime_func = '',
00687                       max_age_secs = 0,
00688                       no_cache = 0,
00689                       no_store = 0,
00690                       must_revalidate = 0,
00691                       vary = '',
00692                       etag_func = '',
00693                       enable_304s = 1)
00694 
00695         # This policy only applies to doc2. It will emit an ETag with
00696         # the constant value "abc" and also enable if-modified-since handling.
00697         cpm.addPolicy(policy_id = 'policy_etag',
00698                       predicate = 'python:object.getId()=="doc2"',
00699                       mtime_func = '',
00700                       max_age_secs = 0,
00701                       no_cache = 0,
00702                       no_store = 0,
00703                       must_revalidate = 0,
00704                       vary = '',
00705                       etag_func = 'string:abc',
00706                       enable_304s = 1)
00707 
00708         # This policy only applies to doc3. Etags with constant values of
00709         # "abc" are emitted, but if-modified-since handling is turned off.
00710         cpm.addPolicy(policy_id = 'policy_disabled',
00711                       predicate = 'python:object.getId()=="doc3"',
00712                       mtime_func = '',
00713                       max_age_secs = 0,
00714                       no_cache = 0,
00715                       no_store = 0,
00716                       must_revalidate = 0,
00717                       vary = '',
00718                       etag_func = 'string:abc',
00719                       enable_304s = 0)
00720 
00721     def tearDown(self):
00722         RequestTest.tearDown(self)
00723         FSDVTest.tearDown(self)
00724 
00725     def _cleanup(self):
00726         # Clean up request and response
00727         req = self.portal.REQUEST
00728 
00729         for header in ( 'IF_MODIFIED_SINCE'
00730                       , 'HTTP_AUTHORIZATION'
00731                       , 'IF_NONE_MATCH'
00732                       ):
00733             if req.environ.get(header, None) is not None:
00734                 del req.environ[header]
00735 
00736         req.RESPONSE.setStatus(200)
00737 
00738     def testUnconditionalGET(self):
00739         # In this case the Request does not specify any if-modified-since
00740         # value to take into account, thereby completely circumventing any
00741         # if-modified-since handling. This must not produce a response status
00742         # of 304, regardless of any other headers.
00743         self.portal.doc1()
00744         response = self.portal.REQUEST.RESPONSE
00745         self.assertEqual(response.getStatus(), 200)
00746 
00747     def testConditionalGETNoETag(self):
00748         yesterday = DateTime() - 1
00749         doc1 = self.portal.doc1
00750         request = doc1.REQUEST
00751         response = request.RESPONSE
00752 
00753         # If doc1 has beeen modified since yesterday (which it has), we want
00754         # the full rendering.
00755         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(yesterday)
00756         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00757         doc1()
00758         self.assertEqual(response.getStatus(), 200)
00759         self._cleanup()
00760 
00761         # If doc1 has been modified since its creation (which it hasn't), we
00762         # want the full rendering. This must return a 304 response.
00763         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc1.modified_date)
00764         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00765         doc1()
00766         self.assertEqual(response.getStatus(), 304)
00767         self._cleanup()
00768 
00769         # ETag handling is not enabled in the policy for doc1, so asking for
00770         # one will not produce any matches. We get the full rendering.
00771         request.environ['IF_NONE_MATCH'] = '"123"'
00772         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00773         doc1()
00774         self.assertEqual(response.getStatus(), 200)
00775         self._cleanup()
00776 
00777         # We are asking for an ETag as well as modifications after doc2 has
00778         # been created. Both won't match and wwe get the full rendering.
00779         request.environ['IF_NONE_MATCH'] = '"123"'
00780         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc1.modified_date)
00781         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00782         doc1()
00783         self.assertEqual(response.getStatus(), 200)
00784         self._cleanup()
00785 
00786     def testConditionalGETETag(self):
00787         yesterday = DateTime() - 1
00788         doc2 = self.portal.doc2
00789         request = doc2.REQUEST
00790         response = request.RESPONSE
00791 
00792         # Has doc2 been modified since yesterday? Yes it has, so we get the
00793         # full rendering.
00794         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(yesterday)
00795         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00796         doc2()
00797         self.assertEqual(response.getStatus(), 200)
00798         self._cleanup()
00799 
00800         # If doc2 has not been modified since its creation (which it hasn't),
00801         # we would get a 304 here. However, the policy for doc2 also expects
00802         # to get an ETag in the request, which we are not setting here. So
00803         # the policy fails and we get a full rendering.
00804         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc2.modified_date)
00805         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00806         doc2()
00807         self.assertEqual(response.getStatus(), 200)
00808         self._cleanup()
00809 
00810         # Now we are setting an ETag in our request, but an ETag that does not
00811         # match the policy's expected value. The policy fails and we get the
00812         # full rendering.
00813         request.environ['IF_NONE_MATCH'] = '"123"'
00814         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00815         doc2()
00816         self.assertEqual(response.getStatus(), 200)
00817         self._cleanup()
00818 
00819         # Here we provide the correct and matching ETag value, and we don't
00820         # specify any if-modified-since condition. This is enough for our
00821         # policy to trigger 304.
00822         request.environ['IF_NONE_MATCH'] = '"abc"'
00823         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00824         doc2()
00825         self.assertEqual(response.getStatus(), 304)
00826         self._cleanup()
00827 
00828         # We specify an ETag and a modification time condition that dooes not
00829         # match, so we get the full rendering
00830         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc2.modified_date)
00831         request.environ['IF_NONE_MATCH'] = '"123"'
00832         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00833         doc2()
00834         self.assertEqual(response.getStatus(), 200)
00835         self._cleanup()
00836 
00837         # We hand in a matching modified time condition which is supposed to
00838         # trigger full rendering. This will lead the ETag condition to be
00839         # overrridden.
00840         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(yesterday)
00841         request.environ['IF_NONE_MATCH'] = '"abc"'
00842         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00843         doc2()
00844         self.assertEqual(response.getStatus(), 200)
00845         self._cleanup()
00846 
00847         # Now we pass an ETag that matches the policy and a modified time
00848         # condition that is not fulfilled. It is safe to serve a 304.
00849         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc2.modified_date)
00850         request.environ['IF_NONE_MATCH'] = '"abc"'
00851         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00852         doc2()
00853         self.assertEqual(response.getStatus(), 304)
00854         self._cleanup()
00855 
00856     def testConditionalGETDisabled(self):
00857         yesterday = DateTime() - 1
00858         doc3 = self.portal.doc3
00859         request = doc3.REQUEST
00860         response = request.RESPONSE
00861 
00862         # Our policy disables any 304-handling, so even though the ETag matches
00863         # the policy, we will get the full rendering.
00864         request.environ['IF_NONE_MATCH'] = '"abc"'
00865         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00866         doc3()
00867         self.assertEqual(response.getStatus(), 200)
00868         self._cleanup()
00869 
00870         # Now both the ETag and the modified condition would trigger a 304
00871         # response *if* 304-handling was enabled. It is not in our policy, so
00872         # we get the full rendering again.
00873         request.environ['IF_MODIFIED_SINCE'] = rfc1123_date(doc3.modified_date)
00874         request.environ['IF_NONE_MATCH'] = '"abc"'
00875         request.environ['HTTP_AUTHORIZATION'] = self.auth_header
00876         doc3()
00877         self.assertEqual(response.getStatus(), 200)
00878         self._cleanup()
00879 
00880 class FSObjMaker(FSDVTest):
00881 
00882     def _makeFSPageTemplate( self, id, filename ):
00883         path = path_join(self.skin_path_name, filename)
00884         return FSPageTemplate( id, path )
00885 
00886     def _makeFSDTMLMethod( self, id, filename ):
00887         path = path_join(self.skin_path_name, filename)
00888         return FSDTMLMethod( id, path )
00889 
00890 class NestedTemplateTests( RequestTest, FSObjMaker ):
00891 
00892     layer = TraversingZCMLLayer
00893 
00894     def setUp(self):
00895         FSObjMaker.setUp(self)
00896         RequestTest.setUp(self)
00897 
00898         # Create a fake portal and the tools we need
00899         self.portal = DummySite(id='portal').__of__(self.root)
00900         self.portal._setObject('portal_types', DummyTool())
00901 
00902         from Products.CMFCore import CachingPolicyManager
00903         CachingPolicyManager.manage_addCachingPolicyManager(self.portal)
00904 
00905     def tearDown(self):
00906         RequestTest.tearDown(self)
00907         FSObjMaker.tearDown(self)
00908 
00909     def test_subtemplate_cpm_1( self ):
00910         # test that subtemplates dont call the cpm
00911         # set up site
00912         portal = self.portal
00913         now = DateTime()
00914         cpm = portal.caching_policy_manager
00915         cpm.addPolicy(policy_id = 'policy_op2',
00916                       predicate = 'python:view=="output_page_2"',
00917                       mtime_func = '',
00918                       max_age_secs = 100,
00919                       no_cache = 0,
00920                       no_store = 0,
00921                       must_revalidate = 0,
00922                       vary = 'doc1',
00923                       etag_func = '',
00924                       s_max_age_secs=100
00925                       )
00926 
00927         content = DummyContent(id='content', view_id='output_page_1')
00928         content.modified_date = now
00929         portal._setObject('content', content)
00930 
00931         output_page_1 = self._makeFSPageTemplate('output_page_1', 'output_page_1.zpt')
00932         output_page_2 = self._makeFSPageTemplate('output_page_2', 'output_page_2.zpt')
00933         portal._setObject('output_page_1', output_page_1)
00934         portal._setObject('output_page_2', output_page_2)
00935 
00936         portal.content()
00937 
00938         # no headers should be added by the CPM if all is well
00939         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
00940         self.failIf('x-cache-headers-set-by' in headers)
00941         self.failIf('vary' in headers)
00942 
00943     def test_subtemplate_cpm_2( self ):
00944         # test that calling content from a template doesnt call the cpm
00945         # just calling an FSDTMLMethod directly from another template does
00946         # not activate the bug because RESPONSE is not passed in
00947         portal = self.portal
00948         now = DateTime()
00949         cpm = portal.caching_policy_manager
00950         cpm.addPolicy(policy_id = 'policy_op4',
00951                       predicate = 'python:view=="output_page_4"',
00952                       mtime_func = '',
00953                       max_age_secs = 100,
00954                       no_cache = 0,
00955                       no_store = 0,
00956                       must_revalidate = 0,
00957                       vary = 'doc1',
00958                       etag_func = '',
00959                       s_max_age_secs=100
00960                       )
00961 
00962         content = DummyContent(id='content', view_id='output_page_3')
00963         content.modified_date = now
00964         portal._setObject('content', content)
00965         content2 = DummyContent(id='content2', view_id='output_page_4')
00966         content2.modified_date = now
00967         portal._setObject('content2', content2)
00968 
00969         output_page_3 = self._makeFSDTMLMethod('output_page_3', 'output_page_3.dtml')
00970         output_page_4 = self._makeFSDTMLMethod('output_page_4', 'output_page_4.dtml')
00971         portal._setObject('output_page_4',output_page_4)
00972         portal._setObject('output_page_3',output_page_3)
00973 
00974         # call the content
00975         portal.content()
00976 
00977         # no headers should be added by the CPM if all is well
00978         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
00979         self.failIf('x-cache-headers-set-by' in headers)
00980         self.failIf('vary' in headers)
00981 
00982     def test_subtemplate_cpm_3( self ):
00983         # test a bigger mix of zpt templates
00984         # set up site
00985         portal = self.portal
00986         now = DateTime()
00987         cpm = portal.caching_policy_manager
00988         cpm.addPolicy(policy_id = 'policy_nv1',
00989                       predicate = 'python:view=="nested_view_1"',
00990                       mtime_func = '',
00991                       max_age_secs = 100,
00992                       no_cache = 0,
00993                       no_store = 0,
00994                       must_revalidate = 0,
00995                       vary = 'doc1',
00996                       etag_func = '',
00997                       s_max_age_secs=100
00998                       )
00999 
01000         doc1 = DummyContent(id='doc1', view_id='nested_view')
01001         doc1.modified_date = now
01002         portal._setObject('doc1',doc1)
01003         doc2 = DummyContent(id='doc2', view_id='nested_view_1')
01004         doc2.modified_date = now
01005         portal._setObject('doc2',doc2)
01006         doc3 = DummyContent(id='doc3', view_id='nested_view_2')
01007         doc3.modified_date = now
01008         portal._setObject('doc3',doc3)
01009 
01010         nested_view = self._makeFSPageTemplate('nested_view', 'nested_view.zpt')
01011         nested_view_1 = self._makeFSPageTemplate('nested_view_1', 'nested_view_1.zpt')
01012         nested_view_2 = self._makeFSPageTemplate('nested_view_2', 'nested_view_2.zpt')
01013         portal._setObject('nested_view', nested_view)
01014         portal._setObject('nested_view_1', nested_view_1)
01015         portal._setObject('nested_view_2', nested_view_2)
01016 
01017         data = portal.doc1()
01018 
01019         # no headers should be added by the CPM if all is well
01020         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
01021         self.failIf('x-cache-headers-set-by' in headers)
01022         self.failIf('vary' in headers)
01023 
01024     def test_mixed_subtemplate_cpm( self ):
01025         # test a mix of zpt and dtml templates
01026         # set up site
01027         now = DateTime()
01028         portal = self.portal
01029         cpm = portal.caching_policy_manager
01030         cpm.addPolicy(policy_id = 'policy_nv1',
01031                       predicate = 'python:view=="nested_view_1"',
01032                       mtime_func = '',
01033                       max_age_secs = 100,
01034                       no_cache = 0,
01035                       no_store = 0,
01036                       must_revalidate = 0,
01037                       vary = 'doc1',
01038                       etag_func = '',
01039                       s_max_age_secs=100
01040                       )
01041 
01042         doc1 = DummyContent(id='doc1', view_id='nested_view', modified_date=now)
01043         portal._setObject('doc1', doc1)
01044         doc2 = DummyContent(id='doc2', view_id='nested_view_1', modified_date=now)
01045         portal._setObject('doc2', doc2)
01046         doc3 = DummyContent(id='doc3', view_id='nested_view_2', modified_date=now)
01047         portal._setObject('doc3', doc3)
01048 
01049         nested_view = self._makeFSPageTemplate('nested_view', 'nested_view.zpt')
01050         nested_view_1 = self._makeFSPageTemplate('nested_view_1', 'nested_view_1.zpt')
01051         nested_view_2 = self._makeFSDTMLMethod('nested_view_2', 'nested_view_2.dtml')
01052         portal._setObject('nested_view', nested_view)
01053         portal._setObject('nested_view_1', nested_view_1)
01054         portal._setObject('nested_view_2', nested_view_2)
01055 
01056         portal.doc1()
01057 
01058         # no headers should be added by the CPM if all is well
01059         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
01060         self.failIf('x-cache-headers-set-by' in headers)
01061         self.failIf('vary' in headers)
01062 
01063     def test_fireForSubtemplates(self):
01064         # This is a FSPageTemplate that will be used as the View for 
01065         # our content objects. It doesn't matter what it returns.
01066         dv = self._makeFSPageTemplate('dummy_view', 'testPT_CPM1.zpt')
01067         self.portal._setObject('dummy_view', dv)
01068 
01069         # These are the subtemplates we use
01070         sv1 = self._makeFSPageTemplate('subview_1', 'testPT_CPM2.zpt')
01071         sv2 = self._makeFSDTMLMethod('subview_2', 'testDTML_CPM3.dtml')
01072         self.portal._setObject('subview_1', sv1)
01073         self.portal._setObject( 'subview_2', sv2)
01074 
01075         for i in (1,2,3):
01076             id = 'doc%i' % i
01077             title = 'Document %i' % i
01078             description = 'This is document %i' % i
01079             modified_date = DateTime()
01080             doc = DummyContent(id)
01081             doc.title = title
01082             doc.description = description
01083             doc.modified_date = modified_date
01084             self.portal._setObject(id, doc)
01085 
01086         cpm = self.portal.caching_policy_manager
01087 
01088         # This policy only applies to doc2.
01089         cpm.addPolicy(policy_id = 'policy_doc2',
01090                       predicate = 'python:object.getId()=="doc2"',
01091                       mtime_func = '',
01092                       max_age_secs = 200,
01093                       no_cache = 0,
01094                       no_store = 0,
01095                       must_revalidate = 0,
01096                       vary = 'doc2',
01097                       etag_func = '',
01098                       pre_check=1
01099                       )
01100 
01101         # This policy only applies to doc3. 
01102         cpm.addPolicy(policy_id = 'policy_doc3',
01103                       predicate = 'python:object.getId()=="doc3"',
01104                       mtime_func = '',
01105                       max_age_secs = 300,
01106                       no_cache = 0,
01107                       no_store = 0,
01108                       must_revalidate = 0,
01109                       vary = 'doc3',
01110                       etag_func = '',
01111                       post_check=1
01112                       )
01113 
01114         # http://www.zope.org/Collectors/CMF/456
01115         # In cases where one view (ZPT or DTML) is rendered from another
01116         # view, we want to ensure only the view requested by the visitor
01117         # will get caching rules applied.
01118         self.portal.doc1.dummy_view()
01119 
01120         # no headers should be added by the CPM if all is well
01121         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
01122         self.failIf('x-cache-headers-set-by' in headers)
01123         self.failIf('vary' in headers)
01124 
01125     def test_fireForSubtemplates2(self):
01126         # This is a FSPageTemplate that will be used as the View for 
01127         # our content objects. It doesn't matter what it returns.
01128         dv = self._makeFSPageTemplate('dummy_view', 'testPT_CPM1.zpt')
01129         self.portal._setObject('dummy_view', dv)
01130 
01131         # These are the subtemplates we use
01132         sv1 = self._makeFSPageTemplate('subview_1', 'testPT_CPM2.zpt')
01133         sv2 = self._makeFSDTMLMethod('subview_2', 'testDTML_CPM3.dtml')
01134         self.portal._setObject('subview_1', sv1)
01135         self.portal._setObject( 'subview_2', sv2)
01136 
01137         for i in (1,2,3):
01138             id = 'doc%i' % i
01139             title = 'Document %i' % i
01140             description = 'This is document %i' % i
01141             modified_date = DateTime()
01142             doc = DummyContent(id)
01143             doc.title = title
01144             doc.description = description
01145             doc.modified_date = modified_date
01146             self.portal._setObject(id, doc)
01147 
01148         cpm = self.portal.caching_policy_manager
01149 
01150         # This policy only applies to doc1.
01151         cpm.addPolicy(policy_id = 'policy_doc1',
01152                       predicate = 'python:object.getId()=="doc1"',
01153                       mtime_func = '',
01154                       max_age_secs = 100,
01155                       no_cache = 0,
01156                       no_store = 0,
01157                       must_revalidate = 0,
01158                       vary = 'doc1',
01159                       etag_func = '',
01160                       s_max_age_secs=100
01161                       )
01162 
01163         # This policy only applies to doc2.
01164         cpm.addPolicy(policy_id = 'policy_doc2',
01165                       predicate = 'python:object.getId()=="doc2"',
01166                       mtime_func = '',
01167                       max_age_secs = 200,
01168                       no_cache = 0,
01169                       no_store = 0,
01170                       must_revalidate = 0,
01171                       vary = 'doc2',
01172                       etag_func = '',
01173                       pre_check=1
01174                       )
01175 
01176         # This policy only applies to doc3. 
01177         cpm.addPolicy(policy_id = 'policy_doc3',
01178                       predicate = 'python:object.getId()=="doc3"',
01179                       mtime_func = '',
01180                       max_age_secs = 300,
01181                       no_cache = 0,
01182                       no_store = 0,
01183                       must_revalidate = 0,
01184                       vary = 'doc3',
01185                       etag_func = '',
01186                       post_check=1
01187                       )
01188 
01189         # http://www.zope.org/Collectors/CMF/456
01190         # In cases where one view (ZPT or DTML) is rendered from another
01191         # view, we want to ensure only the view requested by the visitor
01192         # will get caching rules applied.
01193         self.portal.doc1.dummy_view()
01194 
01195         # We want to make sure the correct policy (policy_doc1) has fired
01196         # Just to be sure, change headers so they are definitely all 
01197         # lower-cased
01198         headers = {}
01199         header_info = self.RESPONSE.headers.items()
01200         [headers.__setitem__(x[0].lower(), x[1]) for x in header_info]
01201 
01202         self.failUnless(headers.get('x-cache-headers-set-by'))
01203         self.assertEquals(headers.get('vary'), 'doc1')
01204         self.assertEquals( headers.get('cache-control')
01205                          , 'max-age=100, s-maxage=100'
01206                          )
01207 
01208 
01209 class OFSCacheTests(RequestTest):
01210 
01211     layer = FunctionalZCMLLayer
01212 
01213     def setUp(self):
01214         from Products.CMFCore import CachingPolicyManager
01215 
01216         RequestTest.setUp(self)
01217 
01218         # Create a fake portal and the tools we need
01219         self.portal = DummySite(id='portal').__of__(self.root)
01220         self.portal._setObject('doc1', CacheableDummyContent('doc1'))
01221         self.portal._setObject('doc2', CacheableDummyContent('doc2'))
01222         CachingPolicyManager.manage_addCachingPolicyManager(self.portal)
01223         cpm = self.portal.caching_policy_manager
01224 
01225         # This policy only applies to doc1. It will not emit any ETag header
01226         # but it enables If-modified-since handling.
01227         cpm.addPolicy(policy_id = 'policy_1',
01228                       predicate = 'python:object.getId()=="doc1"',
01229                       mtime_func = '',
01230                       max_age_secs = 100,
01231                       no_cache = 0,
01232                       no_store = 0,
01233                       must_revalidate = 0,
01234                       vary = 'doc1',
01235                       etag_func = '',
01236                       enable_304s = 0)
01237 
01238     def test_empty(self):
01239 
01240         from Products.CMFCore.CachingPolicyManager import CPMCache
01241 
01242         cpm = self.portal.caching_policy_manager
01243         doc1 = self.portal.doc1
01244         self.failUnless(cpm._isCacheManager)
01245         self.failUnless(isinstance(cpm.ZCacheManager_getCache(), CPMCache))
01246         self.assertEquals( doc1.ZCacheable_getManagerIds()
01247                          , ({'id':cpm.getId(), 'title':''},)
01248                          )
01249 
01250     def test_no_association(self):
01251         # Render an item that would match the CPM policy, but don't 
01252         # associate it with the CPM.
01253         self.portal.doc1()
01254 
01255         # no headers should be added by the CPM if all is well
01256         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
01257         self.failIf('x-cache-headers-set-by' in headers)
01258         self.failIf('vary' in headers)
01259 
01260     def test_unsuitable_association(self):
01261         # Render an item that is associated with the CPM, but that does not
01262         # match any policy.
01263         cpm = self.portal.caching_policy_manager
01264         doc2 = self.portal.doc2
01265         doc2.ZCacheable_setManagerId(cpm.getId())
01266 
01267         doc2()
01268 
01269         # no headers should be added by the CPM if all is well
01270         headers = [x.lower() for x in self.RESPONSE.headers.keys()]
01271         self.failIf('x-cache-headers-set-by' in headers)
01272         self.failIf('vary' in headers)
01273 
01274     def test_suitable_association(self):
01275         # Render a content item that will trigger the CPM
01276         cpm = self.portal.caching_policy_manager
01277         doc1 = self.portal.doc1
01278         doc1.ZCacheable_setManagerId(cpm.getId())
01279 
01280         doc1()
01281 
01282         # Policy "policy_1" should have triggered
01283         # Just to be sure, change headers so they are definitely all 
01284         # lower-cased
01285         headers = {}
01286         header_info = self.RESPONSE.headers.items()
01287         [headers.__setitem__(x[0].lower(), x[1]) for x in header_info]
01288 
01289         self.failUnless(headers.get('x-cache-headers-set-by'))
01290         self.assertEquals(headers.get('vary'), 'doc1')
01291         self.assertEquals( headers.get('cache-control')
01292                          , 'max-age=100'
01293                          )
01294 
01295     def test_with_view(self):
01296         # Render a view for a content item that will trigger the CPM
01297         cpm = self.portal.caching_policy_manager
01298         self.portal._setObject('a_view', DummyView(id='a_view'))
01299         self.portal.a_view.ZCacheable_setManagerId(cpm.getId())
01300         doc1 = self.portal.doc1
01301 
01302         doc1.a_view()
01303 
01304         # Policy "policy_1" should have triggered
01305         # Just to be sure, change headers so they are definitely all 
01306         # lower-cased
01307         headers = {}
01308         header_info = self.RESPONSE.headers.items()
01309         [headers.__setitem__(x[0].lower(), x[1]) for x in header_info]
01310 
01311         self.failUnless(headers.get('x-cache-headers-set-by'))
01312         self.assertEquals(headers.get('vary'), 'doc1')
01313         self.assertEquals( headers.get('cache-control')
01314                          , 'max-age=100'
01315                          )
01316 
01317 
01318 def test_suite():
01319     return unittest.TestSuite((
01320         unittest.makeSuite(CachingPolicyTests),
01321         unittest.makeSuite(CachingPolicyManagerTests),
01322         unittest.makeSuite(CachingPolicyManager304Tests),
01323         unittest.makeSuite(NestedTemplateTests),
01324         unittest.makeSuite(OFSCacheTests),
01325         ))
01326 
01327 if __name__ == '__main__':
01328     from Products.CMFCore.testing import run
01329     run(test_suite())