Skip to content

Commit

Permalink
Merge pull request #95 from JWCook/fix-tests
Browse files Browse the repository at this point in the history
Fix tests
  • Loading branch information
JWCook authored Mar 3, 2024
2 parents 148c460 + 6399696 commit 361083e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 32 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,16 @@ class CachedLimiterSession(CacheMixin, LimiterMixin, Session):


# Optionally use SQLite as both the bucket backend and the cache backend
session = CachedLimiterSession(per_second=5,
cache_name='cache.db',
bucket_class=SQLiteBucket,
bucket_kwargs={"path": "cache.db", 'isolation_level': "EXCLUSIVE", 'check_same_thread': False})
session = CachedLimiterSession(
per_second=5,
cache_name='cache.db',
bucket_class=SQLiteBucket,
bucket_kwargs={
"path": "cache.db",
'isolation_level': "EXCLUSIVE",
'check_same_thread': False,
},
)
```

This example has an extra benefit: cache hits won't count against your rate limit!
93 changes: 92 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pytest = "^7.2"
pytest-cov = ">=4.0"
pytest-xdist = ">=3.1"
requests-mock = ">=1.11"
requests_cache = ">=1.2"
requests-cache = ">=1.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
79 changes: 53 additions & 26 deletions test/test_requests_ratelimiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
General rate-limiting behavior is covered by pyrate-limiter unit tests. These tests should cover
additional behavior specific to requests-ratelimiter.
"""
import os

from test.conftest import (
MOCKED_URL,
Expand All @@ -19,7 +18,8 @@
from pyrate_limiter import Duration, Limiter, RequestRate, SQLiteBucket
from requests import Response, Session
from requests.adapters import HTTPAdapter
from requests_cache import CacheMixin, SQLiteCache
from requests_cache import CacheMixin

from requests_ratelimiter import LimiterAdapter, LimiterMixin, LimiterSession
from requests_ratelimiter.requests_ratelimiter import _convert_rate

Expand Down Expand Up @@ -96,6 +96,7 @@ def test_custom_session(mock_sleep):
session.get(MOCKED_URL)
assert mock_sleep.called is True


@patch_sleep
def test_429(mock_sleep):
"""After receiving a 429 response, the bucket should be filled, allowing no more requests"""
Expand Down Expand Up @@ -158,9 +159,17 @@ def test_convert_rate(limit, interval, expected_limit, expected_interval):


@patch_sleep
def test_sqlite_backend(mock_sleep):
def test_sqlite_backend(mock_sleep, tmp_path):
"""Check that the SQLite backend works as expected"""
session = get_mock_session(per_second=5, bucket_class=SQLiteBucket, bucket_kwargs={"path": "rate_limit.db", 'isolation_level': "EXCLUSIVE", 'check_same_thread': False})
session = get_mock_session(
per_second=5,
bucket_class=SQLiteBucket,
bucket_kwargs={
'path': tmp_path / 'rate_limit.db',
'isolation_level': 'EXCLUSIVE',
'check_same_thread': False,
},
)

for _ in range(5):
session.get(MOCKED_URL)
Expand All @@ -169,16 +178,34 @@ def test_sqlite_backend(mock_sleep):
session.get(MOCKED_URL)
assert mock_sleep.called is True

# force the database to close & clean up
del session
os.remove("rate_limit.db")


@patch_sleep
def test_custom_bucket(mock_sleep):
"""With custom buckets, each session can be called independently without triggering rate limiting but requires a common backend such as sqlite"""
session_a = get_mock_session(per_second=5, bucket_name="a", bucket_class=SQLiteBucket, bucket_kwargs={"path": "rate_limit.db", 'isolation_level': "EXCLUSIVE", 'check_same_thread': False})
session_b = get_mock_session(per_second=5, bucket_name="b", bucket_class=SQLiteBucket, bucket_kwargs={"path": "rate_limit.db", 'isolation_level': "EXCLUSIVE", 'check_same_thread': False})
def test_custom_bucket(mock_sleep, tmp_path):
"""With custom buckets, each session can be called independently without triggering rate
limiting but requires a common backend such as sqlite
"""
ratelimit_path = tmp_path / 'rate_limit.db'

session_a = get_mock_session(
per_second=5,
bucket_name="a",
bucket_class=SQLiteBucket,
bucket_kwargs={
'path': ratelimit_path,
'isolation_level': 'EXCLUSIVE',
'check_same_thread': False,
},
)
session_b = get_mock_session(
per_second=5,
bucket_name='b',
bucket_class=SQLiteBucket,
bucket_kwargs={
'path': ratelimit_path,
'isolation_level': 'EXCLUSIVE',
'check_same_thread': False,
},
)

for _ in range(5):
session_a.get(MOCKED_URL)
Expand All @@ -188,14 +215,9 @@ def test_custom_bucket(mock_sleep):
session_a.get(MOCKED_URL)
assert mock_sleep.called is True

# force the database to close & clean up
del session_a
del session_b
os.remove("rate_limit.db")


@patch_sleep
def test_caching(mock_sleep):
def test_cache_with_limiter(mock_sleep, tmp_path_factory):
"""Check that caching integration works as expected"""

class CachedLimiterSession(CacheMixin, LimiterMixin, Session):
Expand All @@ -204,16 +226,21 @@ class CachedLimiterSession(CacheMixin, LimiterMixin, Session):
LimiterSession and CachedSession.
"""

session = CachedLimiterSession(per_second=5, cache_name='cache.db', bucket_class=SQLiteBucket, bucket_kwargs={"path": "cache.db", 'isolation_level': "EXCLUSIVE", 'check_same_thread': False})
cache_path = tmp_path_factory.mktemp('pytest') / 'cache.db'
ratelimit_path = tmp_path_factory.mktemp('pytest') / 'rate_limit.db'

session = CachedLimiterSession(
per_second=5,
cache_name=str(cache_path),
bucket_class=SQLiteBucket,
bucket_kwargs={
'path': str(ratelimit_path),
'isolation_level': 'EXCLUSIVE',
'check_same_thread': False,
},
)
session = mount_mock_adapter(session)

for _ in range(10):
session.get(MOCKED_URL)
assert mock_sleep.called is False


# force the database to close & clean up
del session
os.remove("cache.db")


0 comments on commit 361083e

Please sign in to comment.