Removed one more utils reference in the README and fixed directory name

Signed-off-by: Carlo Costino <carlo.costino@gsa.gov>
This commit is contained in:
Carlo Costino
2024-05-16 10:41:49 -04:00
parent 99edc88197
commit ac4ebacfeb
40 changed files with 0 additions and 1 deletions

View File

@@ -0,0 +1,221 @@
import uuid
from datetime import datetime
from unittest.mock import Mock, call
import pytest
from freezegun import freeze_time
from notifications_utils.clients.redis.redis_client import RedisClient, prepare_value
@pytest.fixture(scope="function")
def mocked_redis_pipeline():
return Mock()
@pytest.fixture
def delete_mock():
return Mock(return_value=4)
@pytest.fixture(scope="function")
def mocked_redis_client(app, mocked_redis_pipeline, delete_mock, mocker):
app.config["REDIS_ENABLED"] = True
redis_client = RedisClient()
redis_client.init_app(app)
mocker.patch.object(redis_client.redis_store, "get", return_value=100)
mocker.patch.object(redis_client.redis_store, "set")
mocker.patch.object(redis_client.redis_store, "incr")
mocker.patch.object(redis_client.redis_store, "delete")
mocker.patch.object(
redis_client.redis_store, "pipeline", return_value=mocked_redis_pipeline
)
mocker.patch.object(
redis_client, "scripts", {"delete-keys-by-pattern": delete_mock}
)
mocker.patch.object(
redis_client.redis_store,
"hgetall",
return_value={b"template-1111": b"8", b"template-2222": b"8"},
)
return redis_client
@pytest.fixture
def failing_redis_client(mocked_redis_client, delete_mock):
mocked_redis_client.redis_store.get.side_effect = Exception("get failed")
mocked_redis_client.redis_store.set.side_effect = Exception("set failed")
mocked_redis_client.redis_store.incr.side_effect = Exception("incr failed")
mocked_redis_client.redis_store.pipeline.side_effect = Exception("pipeline failed")
mocked_redis_client.redis_store.delete.side_effect = Exception("delete failed")
delete_mock.side_effect = Exception("delete by pattern failed")
return mocked_redis_client
def test_should_not_raise_exception_if_raise_set_to_false(
app, caplog, failing_redis_client, mocker
):
mock_logger = mocker.patch("flask.Flask.logger")
assert failing_redis_client.get("get_key") is None
assert failing_redis_client.set("set_key", "set_value") is None
assert failing_redis_client.incr("incr_key") is None
assert failing_redis_client.exceeded_rate_limit("rate_limit_key", 100, 100) is False
assert failing_redis_client.delete("delete_key") is None
assert failing_redis_client.delete("a", "b", "c") is None
assert failing_redis_client.delete_by_pattern("pattern") == 0
assert mock_logger.mock_calls == [
call.exception("Redis error performing get on get_key"),
call.exception("Redis error performing set on set_key"),
call.exception("Redis error performing incr on incr_key"),
call.exception("Redis error performing rate-limit-pipeline on rate_limit_key"),
call.exception("Redis error performing delete on delete_key"),
call.exception("Redis error performing delete on a, b, c"),
call.exception("Redis error performing delete-by-pattern on pattern"),
]
def test_should_raise_exception_if_raise_set_to_true(
app,
failing_redis_client,
):
with pytest.raises(Exception) as e:
failing_redis_client.get("test", raise_exception=True)
assert str(e.value) == "get failed"
with pytest.raises(Exception) as e:
failing_redis_client.set("test", "test", raise_exception=True)
assert str(e.value) == "set failed"
with pytest.raises(Exception) as e:
failing_redis_client.incr("test", raise_exception=True)
assert str(e.value) == "incr failed"
with pytest.raises(Exception) as e:
failing_redis_client.exceeded_rate_limit("test", 100, 200, raise_exception=True)
assert str(e.value) == "pipeline failed"
with pytest.raises(Exception) as e:
failing_redis_client.delete("test", raise_exception=True)
assert str(e.value) == "delete failed"
with pytest.raises(Exception) as e:
failing_redis_client.delete_by_pattern("pattern", raise_exception=True)
assert str(e.value) == "delete by pattern failed"
def test_should_not_call_if_not_enabled(mocked_redis_client, delete_mock):
mocked_redis_client.active = False
assert mocked_redis_client.get("get_key") is None
assert mocked_redis_client.set("set_key", "set_value") is None
assert mocked_redis_client.incr("incr_key") is None
assert mocked_redis_client.exceeded_rate_limit("rate_limit_key", 100, 100) is False
assert mocked_redis_client.delete("delete_key") is None
assert mocked_redis_client.delete_by_pattern("pattern") == 0
mocked_redis_client.redis_store.get.assert_not_called()
mocked_redis_client.redis_store.set.assert_not_called()
mocked_redis_client.redis_store.incr.assert_not_called()
mocked_redis_client.redis_store.delete.assert_not_called()
mocked_redis_client.redis_store.pipeline.assert_not_called()
delete_mock.assert_not_called()
def test_should_call_set_if_enabled(mocked_redis_client):
mocked_redis_client.set("key", "value")
mocked_redis_client.redis_store.set.assert_called_with(
"key", "value", None, None, False, False
)
def test_should_call_get_if_enabled(mocked_redis_client):
assert mocked_redis_client.get("key") == 100
mocked_redis_client.redis_store.get.assert_called_with("key")
@freeze_time("2001-01-01 12:00:00.000000")
def test_exceeded_rate_limit_should_add_correct_calls_to_the_pipe(
mocked_redis_client, mocked_redis_pipeline
):
mocked_redis_client.exceeded_rate_limit("key", 100, 100)
assert mocked_redis_client.redis_store.pipeline.called
mocked_redis_pipeline.zadd.assert_called_with("key", {978350400.0: 978350400.0})
mocked_redis_pipeline.zremrangebyscore.assert_called_with(
"key", "-inf", 978350300.0
)
mocked_redis_pipeline.zcard.assert_called_with("key")
mocked_redis_pipeline.expire.assert_called_with("key", 100)
assert mocked_redis_pipeline.execute.called
@freeze_time("2001-01-01 12:00:00.000000")
def test_exceeded_rate_limit_should_fail_request_if_over_limit(
mocked_redis_client, mocked_redis_pipeline
):
mocked_redis_pipeline.execute.return_value = [True, True, 100, True]
assert mocked_redis_client.exceeded_rate_limit("key", 99, 100)
@freeze_time("2001-01-01 12:00:00.000000")
def test_exceeded_rate_limit_should_allow_request_if_not_over_limit(
mocked_redis_client, mocked_redis_pipeline
):
mocked_redis_pipeline.execute.return_value = [True, True, 100, True]
assert not mocked_redis_client.exceeded_rate_limit("key", 101, 100)
@freeze_time("2001-01-01 12:00:00.000000")
def test_exceeded_rate_limit_not_exceeded(mocked_redis_client, mocked_redis_pipeline):
mocked_redis_pipeline.execute.return_value = [True, True, 80, True]
assert not mocked_redis_client.exceeded_rate_limit("key", 90, 100)
def test_exceeded_rate_limit_should_not_call_if_not_enabled(
mocked_redis_client, mocked_redis_pipeline
):
mocked_redis_client.active = False
assert not mocked_redis_client.exceeded_rate_limit("key", 100, 100)
assert not mocked_redis_client.redis_store.pipeline.called
def test_delete(mocked_redis_client):
key = "hash-key"
mocked_redis_client.delete(key)
mocked_redis_client.redis_store.delete.assert_called_with(key)
def test_delete_multi(mocked_redis_client):
mocked_redis_client.delete("a", "b", "c")
mocked_redis_client.redis_store.delete.assert_called_with("a", "b", "c")
@pytest.mark.parametrize(
"input,output",
[
(b"asdf", b"asdf"),
("asdf", "asdf"),
(0, 0),
(1.2, 1.2),
(uuid.UUID(int=0), "00000000-0000-0000-0000-000000000000"),
pytest.param({"a": 1}, None, marks=pytest.mark.xfail(raises=ValueError)),
pytest.param(
datetime.utcnow(), None, marks=pytest.mark.xfail(raises=ValueError)
),
],
)
def test_prepare_value(input, output):
assert prepare_value(input) == output
def test_delete_by_pattern(mocked_redis_client, delete_mock):
ret = mocked_redis_client.delete_by_pattern("foo")
assert ret == 4
delete_mock.assert_called_once_with(args=["foo"])

View File

@@ -0,0 +1,190 @@
import pytest
from notifications_utils.clients.redis import RequestCache
from notifications_utils.clients.redis.redis_client import RedisClient
@pytest.fixture(scope="function")
def mocked_redis_client(app):
app.config["REDIS_ENABLED"] = True
redis_client = RedisClient()
redis_client.init_app(app)
return redis_client
@pytest.fixture
def cache(mocked_redis_client):
return RequestCache(mocked_redis_client)
@pytest.mark.parametrize(
"args, kwargs, expected_cache_key",
(
([1, 2, 3], {}, "1-2-3-None-None-None"),
([1, 2, 3, 4, 5, 6], {}, "1-2-3-4-5-6"),
([1, 2, 3], {"x": 4, "y": 5, "z": 6}, "1-2-3-4-5-6"),
([1, 2, 3, 4], {"y": 5}, "1-2-3-4-5-None"),
),
)
def test_set(
mocker,
mocked_redis_client,
cache,
args,
kwargs,
expected_cache_key,
):
mock_redis_set = mocker.patch.object(
mocked_redis_client,
"set",
)
mock_redis_get = mocker.patch.object(
mocked_redis_client,
"get",
return_value=None,
)
@cache.set("{a}-{b}-{c}-{x}-{y}-{z}")
def foo(a, b, c, x=None, y=None, z=None):
return "bar"
assert foo(*args, **kwargs) == "bar"
mock_redis_get.assert_called_once_with(expected_cache_key)
mock_redis_set.assert_called_once_with(
expected_cache_key,
'"bar"',
ex=604_800,
)
@pytest.mark.parametrize(
"cache_set_call, expected_redis_client_ttl",
(
(0, 0),
(1, 1),
(1.111, 1),
("2000", 2_000),
),
)
def test_set_with_custom_ttl(
mocker,
mocked_redis_client,
cache,
cache_set_call,
expected_redis_client_ttl,
):
mock_redis_set = mocker.patch.object(
mocked_redis_client,
"set",
)
mocker.patch.object(
mocked_redis_client,
"get",
return_value=None,
)
@cache.set("foo", ttl_in_seconds=cache_set_call)
def foo():
return "bar"
foo()
mock_redis_set.assert_called_once_with(
"foo",
'"bar"',
ex=expected_redis_client_ttl,
)
def test_raises_if_key_doesnt_match_arguments(cache):
@cache.set("{baz}")
def foo(bar):
pass
with pytest.raises(KeyError):
foo(1)
with pytest.raises(KeyError):
foo()
def test_get(mocker, mocked_redis_client, cache):
mock_redis_get = mocker.patch.object(
mocked_redis_client,
"get",
return_value=b'"bar"',
)
@cache.set("{a}-{b}-{c}")
def foo(a, b, c):
# This function should not be called because the cache has
# returned a value
raise RuntimeError
assert foo(1, 2, 3) == "bar"
mock_redis_get.assert_called_once_with("1-2-3")
def test_delete(mocker, mocked_redis_client, cache):
mock_redis_delete = mocker.patch.object(
mocked_redis_client,
"delete",
)
@cache.delete("{a}-{b}-{c}")
def foo(a, b, c):
return "bar"
assert foo(1, 2, 3) == "bar"
mock_redis_delete.assert_called_once_with("1-2-3")
def test_delete_even_if_call_raises(mocker, mocked_redis_client, cache):
mock_redis_delete = mocker.patch.object(
mocked_redis_client,
"delete",
)
@cache.delete("bar")
def foo():
raise RuntimeError
with pytest.raises(RuntimeError):
foo()
mock_redis_delete.assert_called_once_with("bar")
def test_delete_by_pattern(mocker, mocked_redis_client, cache):
mock_redis_delete = mocker.patch.object(
mocked_redis_client,
"delete_by_pattern",
)
@cache.delete_by_pattern("{a}-{b}-{c}-???")
def foo(a, b, c):
return "bar"
assert foo(1, 2, 3) == "bar"
mock_redis_delete.assert_called_once_with("1-2-3-???")
def test_delete_by_pattern_even_if_call_raises(mocker, mocked_redis_client, cache):
mock_redis_delete = mocker.patch.object(
mocked_redis_client,
"delete_by_pattern",
)
@cache.delete_by_pattern("bar-???")
def foo():
raise RuntimeError
with pytest.raises(RuntimeError):
foo()
mock_redis_delete.assert_called_once_with("bar-???")