diff options
author | Jordan Cook <jordan.cook@pioneer.com> | 2021-09-13 17:55:05 -0500 |
---|---|---|
committer | Jordan Cook <jordan.cook@pioneer.com> | 2021-09-18 17:43:12 -0500 |
commit | 252868973c81d13b6407f2e74513d5e8b0a37a55 (patch) | |
tree | 77efc11226679f3ef71861bc886b67b2291143ac | |
parent | dbee4da8293e8410deeb77ddb5c2a6f5cc4c31eb (diff) | |
download | requests-cache-252868973c81d13b6407f2e74513d5e8b0a37a55.tar.gz |
Use only integers for expire_after values in seconds, since this is what Cache-Control: max-age requires
-rw-r--r-- | requests_cache/cache_control.py | 5 | ||||
-rw-r--r-- | tests/integration/base_cache_test.py | 26 | ||||
-rw-r--r-- | tests/unit/test_session.py | 22 |
3 files changed, 28 insertions, 25 deletions
diff --git a/requests_cache/cache_control.py b/requests_cache/cache_control.py index 79add63..434c5c2 100644 --- a/requests_cache/cache_control.py +++ b/requests_cache/cache_control.py @@ -14,6 +14,7 @@ from datetime import datetime, timedelta, timezone from email.utils import parsedate_to_datetime from fnmatch import fnmatch from logging import getLogger +from math import ceil from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Tuple, Union from attr import define, field @@ -152,10 +153,10 @@ def get_expiration_datetime(expire_after: ExpirationTime) -> Optional[datetime]: return datetime.utcnow() + expire_after -def get_expiration_seconds(expire_after: ExpirationTime) -> Optional[float]: +def get_expiration_seconds(expire_after: ExpirationTime) -> Optional[int]: """Convert an expiration value in any supported format to an expiration time in seconds""" expires = get_expiration_datetime(expire_after) - return (expires - datetime.utcnow()).total_seconds() if expires else None + return ceil((expires - datetime.utcnow()).total_seconds()) if expires else None def get_cache_directives(headers: Mapping) -> Dict: diff --git a/tests/integration/base_cache_test.py b/tests/integration/base_cache_test.py index 3542cd5..b435b4f 100644 --- a/tests/integration/base_cache_test.py +++ b/tests/integration/base_cache_test.py @@ -132,30 +132,32 @@ class BaseCacheTest: response_3 = get_json(httpbin('cookies/set/test3/test4')) assert response_3 == get_json(httpbin('cookies')) - @pytest.mark.parametrize('cache_control', [True, False]) @pytest.mark.parametrize( - 'request_headers, expected_expiration', + 'cache_control, request_headers, expected_expiration', [ - ({}, 60), - ({'Cache-Control': 'max-age=360'}, 360), - ({'Cache-Control': 'no-store'}, None), - ({'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 360), + (True, {}, 60), + (True, {'Cache-Control': 'max-age=360'}, 60), + (True, {'Cache-Control': 'no-store'}, None), + (True, {'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 60), + (False, {}, None), + (False, {'Cache-Control': 'max-age=360'}, 360), + (False, {'Cache-Control': 'no-store'}, None), + (False, {'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 360), ], ) - def test_cache_control_expiration(self, request_headers, expected_expiration, cache_control): + def test_cache_control_expiration(self, cache_control, request_headers, expected_expiration): """Test cache headers for both requests and responses. The `/cache/{seconds}` endpoint returns Cache-Control headers, which should be used unless request headers are sent. No headers should be used if `cache_control=False`. """ session = self.init_session(cache_control=cache_control) - now = datetime.utcnow() session.get(httpbin('cache/60'), headers=request_headers) response = session.get(httpbin('cache/60'), headers=request_headers) - if expected_expiration is None or cache_control is False: + if expected_expiration is None: assert response.expires is None else: - assert_delta_approx_equal(now, response.expires, expected_expiration) + assert_delta_approx_equal(datetime.utcnow(), response.expires, expected_expiration) @pytest.mark.parametrize( 'cached_response_headers, expected_from_cache', @@ -205,13 +207,13 @@ class BaseCacheTest: assert session.post(httpbin('post'), files={'file1': BytesIO(b'10' * 1024)}).from_cache def test_remove_expired_responses(self): - session = self.init_session(expire_after=0.01) + session = self.init_session(expire_after=1) # Populate the cache with several responses that should expire immediately for response_format in HTTPBIN_FORMATS: session.get(httpbin(response_format)) session.get(httpbin('redirect/1')) - sleep(0.01) + sleep(1) # Cache a response + redirects, which should be the only non-expired cache items session.get(httpbin('get'), expire_after=-1) diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py index 697e55a..73f522a 100644 --- a/tests/unit/test_session.py +++ b/tests/unit/test_session.py @@ -636,18 +636,20 @@ def test_remove_expired_responses__per_request(mock_session): mock_session.mock_adapter.register_uri('GET', second_url, status_code=200) mock_session.mock_adapter.register_uri('GET', third_url, status_code=200) mock_session.get(MOCKED_URL) - mock_session.get(second_url, expire_after=0.4) - mock_session.get(third_url, expire_after=0.8) + mock_session.get(second_url, expire_after=1) + mock_session.get(third_url, expire_after=2) # All 3 responses should still be cached mock_session.remove_expired_responses() + for response in mock_session.cache.responses.values(): + print('Expires:', response.expires - datetime.utcnow() if response.expires else None) assert len(mock_session.cache.responses) == 3 - # One should be expired after 0.4s, and another should be expired after 0.8s - time.sleep(0.4) + # One should be expired after 1s, and another should be expired after 2s + time.sleep(1) mock_session.remove_expired_responses() assert len(mock_session.cache.responses) == 2 - time.sleep(0.4) + time.sleep(2) mock_session.remove_expired_responses() assert len(mock_session.cache.responses) == 1 @@ -655,21 +657,19 @@ def test_remove_expired_responses__per_request(mock_session): def test_per_request__expiration(mock_session): """No per-session expiration is set, but then overridden with per-request expiration""" mock_session.expire_after = None - response = mock_session.get(MOCKED_URL, expire_after=0.01) + response = mock_session.get(MOCKED_URL, expire_after=1) assert response.from_cache is False - time.sleep(0.01) + time.sleep(1) response = mock_session.get(MOCKED_URL) assert response.from_cache is False def test_per_request__no_expiration(mock_session): """A per-session expiration is set, but then overridden with no per-request expiration""" - mock_session.expire_after = 0.01 + mock_session.expire_after = 1 response = mock_session.get(MOCKED_URL, expire_after=-1) assert response.from_cache is False - time.sleep(0.01) - response = mock_session.get(MOCKED_URL) - assert response.from_cache is True + assert response.expires is None def test_unpickle_errors(mock_session): |