##############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Unit tests for AcceleratedCacheManager module.

$Id$
"""

import unittest
from Products.StandardCacheManagers.AcceleratedHTTPCacheManager \
     import AcceleratedHTTPCache, AcceleratedHTTPCacheManager


class DummyObject:

    def __init__(self, path='/path/to/object', urlpath=None):
        self.path = path
        if urlpath is None:
            self.urlpath = path
        else:
            self.urlpath = urlpath

    def getPhysicalPath(self):
        return tuple(self.path.split('/'))

    def absolute_url_path(self):
        return self.urlpath

class MockResponse:
    status = '200'
    reason = "who knows, I'm just a mock"

def MockConnectionClassFactory():
    # Returns both a class that mocks an HTTPConnection,
    # and a reference to a data structure where it logs requests.
    request_log = []

    class MockConnection:
        # Minimal replacement for httplib.HTTPConnection.
        def __init__(self, host):
            self.host = host
            self.request_log = request_log

        def request(self, method, path):
            self.request_log.append({'method':method,
                                     'host':self.host,
                                     'path':path,})
        def getresponse(self):
            return MockResponse()

    return MockConnection, request_log


class AcceleratedHTTPCacheTests(unittest.TestCase):

    def _getTargetClass(self):
        return AcceleratedHTTPCache

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def test_PURGE_passes_Host_header(self):
        _TO_NOTIFY = 'localhost:1888'
        cache = self._makeOne()
        cache.notify_urls = ['http://%s' % _TO_NOTIFY]
        cache.connection_factory, requests = MockConnectionClassFactory()
        dummy = DummyObject()
        cache.ZCache_invalidate(dummy)
        self.assertEqual(len(requests), 1)
        result = requests[-1]
        self.assertEqual(result['method'], 'PURGE')
        self.assertEqual(result['host'], _TO_NOTIFY)
        self.assertEqual(result['path'], dummy.path)

    def test_multiple_notify(self):
        cache = self._makeOne()
        cache.notify_urls = ['http://foo', 'bar', 'http://baz/bat']
        cache.connection_factory, requests = MockConnectionClassFactory()
        cache.ZCache_invalidate(DummyObject())
        self.assertEqual(len(requests), 3)
        self.assertEqual(requests[0]['host'], 'foo')
        self.assertEqual(requests[1]['host'], 'bar')
        self.assertEqual(requests[2]['host'], 'baz')
        cache.ZCache_invalidate(DummyObject())
        self.assertEqual(len(requests), 6)

    def test_vhost_purging_1447(self):
        # Test for http://www.zope.org/Collectors/Zope/1447
        cache = self._makeOne()
        cache.notify_urls = ['http://foo.com']
        cache.connection_factory, requests = MockConnectionClassFactory()
        dummy = DummyObject(urlpath='/published/elsewhere')
        cache.ZCache_invalidate(dummy)
        # That should fire off two invalidations,
        # one for the physical path and one for the abs. url path.
        self.assertEqual(len(requests), 2)
        self.assertEqual(requests[0]['path'], dummy.absolute_url_path())
        self.assertEqual(requests[1]['path'], dummy.path)


class CacheManagerTests(unittest.TestCase):

    def _getTargetClass(self):
        return AcceleratedHTTPCacheManager

    def _makeOne(self, *args, **kw):
        return self._getTargetClass()(*args, **kw)

    def _makeContext(self):
        from OFS.Folder import Folder
        root = Folder()
        root.getPhysicalPath = lambda: ('', 'some_path',)
        cm_id = 'http_cache'
        manager = self._makeOne(cm_id)
        root._setObject(cm_id, manager)
        manager = root[cm_id]
        return root, manager

    def test_add(self):
        # ensure __init__ doesn't raise errors.
        root, cachemanager = self._makeContext()

    def test_ZCacheManager_getCache(self):
        root, cachemanager = self._makeContext()
        cache = cachemanager.ZCacheManager_getCache()
        self.assert_(isinstance(cache, AcceleratedHTTPCache))

    def test_getSettings(self):
        root, cachemanager = self._makeContext()
        settings = cachemanager.getSettings()
        self.assert_('anonymous_only' in settings.keys())
        self.assert_('interval' in settings.keys())
        self.assert_('notify_urls' in settings.keys())


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

if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

