Add rate_limit and message_limit to SerialisedService

The API needs these to check whether a service can send a notification.
This commit also updates all the tests in `test_validators.py` to take
a serialised service, not a database object.
This commit is contained in:
Chris Hill-Scott
2020-06-26 13:38:58 +01:00
parent 1f315b06e2
commit 9f41e77bf7
2 changed files with 62 additions and 22 deletions

View File

@@ -120,7 +120,9 @@ class SerialisedService(SerialisedModel):
'active',
'contact_link',
'email_from',
'message_limit',
'permissions',
'rate_limit',
'research_mode',
'restricted',
}

View File

@@ -23,7 +23,7 @@ from app.notifications.validators import (
validate_and_format_recipient,
validate_template,
)
from app.serialised_models import SerialisedService, SerialisedTemplate
from app.serialised_models import SerialisedService, SerialisedTemplate, SerialisedAPIKeyCollection
from app.utils import get_template_instance
from app.v2.errors import (
@@ -42,6 +42,7 @@ from tests.app.db import (
create_service_whitelist,
create_template,
)
from unittest.mock import ANY
# all of these tests should have redis enabled (except where we specifically disable it)
@@ -56,12 +57,20 @@ def test_check_service_message_limit_in_cache_with_unrestricted_service_is_allow
key_type,
sample_service,
mocker):
mocker.patch('app.notifications.validators.redis_store.get', return_value=1)
mocker.patch('app.notifications.validators.redis_store.get', side_effect=[
None, # The serialised service
1, # The rolling count
])
mocker.patch('app.notifications.validators.redis_store.set')
mocker.patch('app.notifications.validators.services_dao')
serialised_service = SerialisedService.from_id(sample_service.id)
check_service_over_daily_message_limit(key_type, sample_service)
app.notifications.validators.redis_store.set.assert_not_called()
check_service_over_daily_message_limit(key_type, serialised_service)
app.notifications.validators.redis_store.set.assert_called_once_with(
f'service-{serialised_service.id}',
ANY,
ex=ANY,
)
assert not app.notifications.validators.services_dao.mock_calls
@@ -70,17 +79,27 @@ def test_check_service_message_limit_in_cache_under_message_limit_passes(
key_type,
sample_service,
mocker):
mocker.patch('app.notifications.validators.redis_store.get', return_value=1)
mocker.patch('app.notifications.validators.redis_store.get', side_effect=[
None, # The serialised service
1, # The rolling count
])
mocker.patch('app.notifications.validators.redis_store.set')
mocker.patch('app.notifications.validators.services_dao')
check_service_over_daily_message_limit(key_type, sample_service)
app.notifications.validators.redis_store.set.assert_not_called()
serialised_service = SerialisedService.from_id(sample_service.id)
check_service_over_daily_message_limit(key_type, serialised_service)
app.notifications.validators.redis_store.set.assert_called_once_with(
f'service-{serialised_service.id}',
ANY,
ex=ANY,
)
assert not app.notifications.validators.services_dao.mock_calls
def test_should_not_interact_with_cache_for_test_key(sample_service, mocker):
mocker.patch('app.notifications.validators.redis_store')
check_service_over_daily_message_limit('test', sample_service)
mocker.patch('app.notifications.validators.redis_store.get', side_effect=[None])
serialised_service = SerialisedService.from_id(sample_service.id)
check_service_over_daily_message_limit('test', serialised_service)
assert not app.notifications.validators.redis_store.mock_calls
@@ -91,22 +110,24 @@ def test_should_set_cache_value_as_value_from_database_if_cache_not_set(
sample_service,
mocker
):
serialised_service = SerialisedService.from_id(sample_service.id)
with freeze_time("2016-01-01 12:00:00.000000"):
for x in range(5):
create_notification(sample_template)
mocker.patch('app.notifications.validators.redis_store.get', return_value=None)
mocker.patch('app.notifications.validators.redis_store.set')
check_service_over_daily_message_limit(key_type, sample_service)
check_service_over_daily_message_limit(key_type, serialised_service)
app.notifications.validators.redis_store.set.assert_called_with(
str(sample_service.id) + "-2016-01-01-count", 5, ex=3600
)
def test_should_not_access_database_if_redis_disabled(notify_api, sample_service, mocker):
serialised_service = SerialisedService.from_id(sample_service.id)
with set_config(notify_api, 'REDIS_ENABLED', False):
db_mock = mocker.patch('app.notifications.validators.services_dao')
check_service_over_daily_message_limit('normal', sample_service)
check_service_over_daily_message_limit('normal', serialised_service)
assert db_mock.method_calls == []
@@ -120,11 +141,12 @@ def test_check_service_message_limit_over_message_limit_fails(key_type, sample_s
sample_service.restricted = True
sample_service.message_limit = 4
template = create_template(sample_service)
serialised_service = SerialisedService.from_id(sample_service.id)
for x in range(5):
create_notification(template)
with pytest.raises(TooManyRequestsError) as e:
check_service_over_daily_message_limit(key_type, sample_service)
check_service_over_daily_message_limit(key_type, serialised_service)
assert e.value.status_code == 429
assert e.value.message == 'Exceeded send limits (4) for today'
assert e.value.fields == []
@@ -139,17 +161,25 @@ def test_check_service_message_limit_in_cache_over_message_limit_fails(
key_type,
mocker):
with freeze_time("2016-01-01 12:00:00.000000"):
mocker.patch('app.redis_store.get', return_value=5)
mocker.patch('app.redis_store.get', side_effect=[
None, # The serialised service
5, # The rolling count
])
mocker.patch('app.notifications.validators.redis_store.set')
mocker.patch('app.notifications.validators.services_dao')
service = create_service(restricted=True, message_limit=4)
serialised_service = SerialisedService.from_id(service.id)
with pytest.raises(TooManyRequestsError) as e:
check_service_over_daily_message_limit(key_type, service)
check_service_over_daily_message_limit(key_type, serialised_service)
assert e.value.status_code == 429
assert e.value.message == 'Exceeded send limits (4) for today'
assert e.value.fields == []
app.notifications.validators.redis_store.set.assert_not_called()
app.notifications.validators.redis_store.set.assert_called_once_with(
f'service-{serialised_service.id}',
ANY,
ex=ANY,
)
assert not app.notifications.validators.services_dao.mock_calls
@@ -194,23 +224,25 @@ def test_check_template_is_active_fails(sample_template):
['test', 'normal'])
def test_service_can_send_to_recipient_passes(key_type, notify_db_session):
trial_mode_service = create_service(service_name='trial mode', restricted=True)
serialised_service = SerialisedService.from_id(trial_mode_service.id)
assert service_can_send_to_recipient(trial_mode_service.users[0].email_address,
key_type,
trial_mode_service) is None
serialised_service) is None
assert service_can_send_to_recipient(trial_mode_service.users[0].mobile_number,
key_type,
trial_mode_service) is None
serialised_service) is None
@pytest.mark.parametrize('key_type',
['test', 'normal'])
def test_service_can_send_to_recipient_passes_for_live_service_non_team_member(key_type, sample_service):
serialised_service = SerialisedService.from_id(sample_service.id)
assert service_can_send_to_recipient("some_other_email@test.com",
key_type,
sample_service) is None
serialised_service) is None
assert service_can_send_to_recipient('07513332413',
key_type,
sample_service) is None
serialised_service) is None
def test_service_can_send_to_recipient_passes_for_whitelisted_recipient_passes(sample_service):
@@ -383,9 +415,11 @@ def test_that_when_exceed_rate_limit_request_fails(
sample_service.restricted = True
api_key = create_api_key(sample_service, key_type=api_key_type)
serialised_service = SerialisedService.from_id(sample_service.id)
serialised_api_key = SerialisedAPIKeyCollection.from_service_id(serialised_service.id)[0]
with pytest.raises(RateLimitError) as e:
check_service_over_api_rate_limit(sample_service, api_key)
check_service_over_api_rate_limit(serialised_service, serialised_api_key)
assert app.redis_store.exceeded_rate_limit.called_with(
"{}-{}".format(str(sample_service.id), api_key.key_type),
@@ -408,8 +442,10 @@ def test_that_when_not_exceeded_rate_limit_request_succeeds(
sample_service.restricted = True
api_key = create_api_key(sample_service)
serialised_service = SerialisedService.from_id(sample_service.id)
serialised_api_key = SerialisedAPIKeyCollection.from_service_id(serialised_service.id)[0]
check_service_over_api_rate_limit(sample_service, api_key)
check_service_over_api_rate_limit(serialised_service, serialised_api_key)
assert app.redis_store.exceeded_rate_limit.called_with(
"{}-{}".format(str(sample_service.id), api_key.key_type),
3000,
@@ -427,9 +463,11 @@ def test_should_not_rate_limit_if_limiting_is_disabled(
mocker.patch('app.notifications.validators.services_dao')
sample_service.restricted = True
api_key = create_api_key(sample_service)
create_api_key(sample_service)
serialised_service = SerialisedService.from_id(sample_service.id)
serialised_api_key = SerialisedAPIKeyCollection.from_service_id(serialised_service.id)[0]
check_service_over_api_rate_limit(sample_service, api_key)
check_service_over_api_rate_limit(serialised_service, serialised_api_key)
assert not app.redis_store.exceeded_rate_limit.called