def test_update_from_response__disabled():
    """Response headers should not be used if cache_control=False"""
    url = 'https://img.site.com/base/img.jpg'
    headers = [('Cache-Control', 'max-age=60')]
    response = MagicMock(url=url,
                         headers=CIMultiDictProxy(CIMultiDict(headers)))

    actions = CacheActions(key='key', cache_control=False, expire_after=30)
    actions.update_from_response(response)
    assert actions.expire_after == 30
def test_update_from_response(headers, expected_expiration):
    """Test with Cache-Control response headers"""
    url = 'https://img.site.com/base/img.jpg'
    response = MagicMock(url=url,
                         headers=CIMultiDictProxy(CIMultiDict(headers)))
    actions = CacheActions(key='key', cache_control=True)
    actions.update_from_response(response)

    if expected_expiration == DO_NOT_CACHE:
        assert actions.skip_write is True
    else:
        assert actions.expire_after == expected_expiration
        assert actions.skip_write is False
def test_init_from_request(
    get_url_expiration,
    request_expire_after,
    url_expire_after,
    header_expire_after,
    expected_expiration,
):
    """Test precedence with various combinations or per-request, per-session, per-URL, and
    Cache-Control expiration
    """
    url = 'https://img.site.com/base/img.jpg'
    get_url_expiration.return_value = url_expire_after
    headers = {
        'Cache-Control': f'max-age={header_expire_after}'
    } if header_expire_after else {}

    actions = CacheActions.from_request(
        key='key',
        url=URL(url),
        request_expire_after=request_expire_after,
        session_expire_after=1,
        cache_control=True,
        headers=headers,
    )
    assert actions.expire_after == expected_expiration
Example #4
0
async def test_session__cache_miss(mock_request):
    cache = MagicMock(spec=CacheBackend)
    cache.request.return_value = None, CacheActions()
    session = CachedSession(cache=cache)

    await session.get('http://test.url')
    assert mock_request.called is True
Example #5
0
async def test_session__default_attrs(mock_request):
    cache = MagicMock(spec=CacheBackend)
    cache.request.return_value = None, CacheActions()
    session = CachedSession(cache=cache)

    response = await session.get('http://test.url')
    assert response.from_cache is False and response.is_expired is False
Example #6
0
    async def request(
        self,
        method: str,
        url: StrOrURL,
        expire_after: ExpirationTime = None,
        **kwargs,
    ) -> Tuple[Optional[CachedResponse], CacheActions]:
        """Fetch a cached response based on request info

        Args:
            method: HTTP method
            url: Request URL
            expire_after: Expiration time to set only for this request; overrides
                ``CachedSession.expire_after``, and accepts all the same values.
            kwargs: All other request arguments
        """
        key = self.create_key(method, url, **kwargs)
        actions = CacheActions.from_request(
            key,
            url=url,
            request_expire_after=expire_after,
            session_expire_after=self.expire_after,
            urls_expire_after=self.urls_expire_after,
            cache_control=self.cache_control,
            **kwargs,
        )

        # Skip reading from the cache, if specified by request headers
        response = None if actions.skip_read else await self.get_response(
            actions.key)
        return response, actions
Example #7
0
async def test_all_param_types(mock_request, params) -> None:
    """Ensure that CachedSession.request() acceepts all the same parameter types as aiohttp"""
    cache = MagicMock(spec=CacheBackend)
    cache.request.return_value = None, CacheActions()
    session = CachedSession(cache=cache)

    response = await session.get('http://test.url', params=params)
    assert response.from_cache is False
Example #8
0
async def test_session__cache_hit(mock_request):
    cache = MagicMock(spec=CacheBackend)
    response = AsyncMock(is_expired=False, url=URL('https://test.com'))
    cache.request.return_value = response, CacheActions()
    session = CachedSession(cache=cache)

    await session.get('http://test.url')
    assert mock_request.called is False
Example #9
0
async def test_save_response(mock_is_cacheable):
    cache = CacheBackend()
    mock_response = get_mock_response()
    mock_response.history = [MagicMock(method='GET', url='test')]
    redirect_key = cache.create_key('GET', 'test')

    await cache.save_response(mock_response, CacheActions(key='key'))
    cached_response = await cache.responses.read('key')
    assert cached_response and isinstance(cached_response, CachedResponse)
    assert await cache.redirects.read(redirect_key) == 'key'
Example #10
0
async def test_session__empty_cookies(mock_request):
    """Previous versions didn't set cookies if they were empty. Just make sure it doesn't explode."""
    cache = MagicMock(spec=CacheBackend)
    response = AsyncMock(is_expired=False,
                         url=URL('https://test.com'),
                         cookies=None)
    cache.request.return_value = response, CacheActions()
    session = CachedSession(cache=cache)

    session.cookie_jar.clear()
    await session.get('http://test.url')
    assert not session.cookie_jar.filter_cookies('https://test.com')
def test_ignored_headers(directive):
    """Ensure that currently unimplemented Cache-Control headers do not affect behavior"""
    url = 'https://img.site.com/base/img.jpg'
    headers = {'Cache-Control': directive}
    # expiration = get_expiration(response, session_expire_after=1, cache_control=True)
    actions = CacheActions.from_request(
        key='key',
        url=URL(url),
        session_expire_after=1,
        cache_control=True,
        headers=headers,
    )
    assert actions.expire_after == 1
def test_init_from_headers(headers, expected_expiration):
    """Test with Cache-Control request headers"""
    actions = CacheActions.from_headers(key='key',
                                        headers=CIMultiDict(headers))

    assert actions.key == 'key'
    if expected_expiration == DO_NOT_CACHE:
        assert actions.skip_read is True
        assert actions.skip_write is True
    else:
        assert actions.expire_after == expected_expiration
        assert actions.skip_read is False
        assert actions.skip_write is False
Example #13
0
    async def save_response(self, response: ClientResponse,
                            actions: CacheActions):
        """Save a response to the cache

        Args:
            response: Response to save
            actions: Specific cache actions to take
        """
        actions.update_from_response(response)
        if not self.is_cacheable(response, actions):
            logger.debug(f'Not caching response for key: {actions.key}')
            return

        logger.debug(f'Saving response for key: {actions.key}')
        cached_response = await CachedResponse.from_client_response(
            response, actions.expires)
        await self.responses.write(actions.key, cached_response)

        # Alias any redirect requests to the same cache key
        for r in response.history:
            await self.redirects.write(self.create_key(r.method, r.url),
                                       actions.key)
Example #14
0
async def test_session__cookies(mock_request):
    cache = MagicMock(spec=CacheBackend)
    response = AsyncMock(
        is_expired=False,
        url=URL('https://test.com'),
        cookies=SimpleCookie({'test_cookie': 'value'}),
    )
    cache.request.return_value = response, CacheActions()
    session = CachedSession(cache=cache)

    session.cookie_jar.clear()
    await session.get('http://test.url')
    cookies = session.cookie_jar.filter_cookies('https://test.com')
    assert cookies['test_cookie'].value == 'value'
def test_init_from_settings(url, request_expire_after, expected_expiration):
    """Test with per-session, per-request, and per-URL expiration"""
    urls_expire_after = {
        '*.site_1.com': timedelta(hours=12),
        'site_2.com/resource_1': timedelta(hours=20),
        'site_2.com/resource_2': timedelta(days=7),
        'site_2.com/static': -1,
    }
    actions = CacheActions.from_settings(
        key='key',
        url=URL(url),
        request_expire_after=request_expire_after,
        session_expire_after=1,
        urls_expire_after=urls_expire_after,
    )
    assert actions.expire_after == expected_expiration