Files
notifications-admin/tests/app/notify_client/test_service_api_client.py
Katie Smith 5b52b6f9bf Clear user cache when broadcast service settings form is submitted
When the broadcast service settings form is submitted it now removes all
permissions for users in notifications-api. This means it should be
clearing the user cache.
2021-07-13 17:06:35 +01:00

619 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from unittest.mock import call
from uuid import uuid4
import pytest
from app import invite_api_client, service_api_client, user_api_client
from app.notify_client.service_api_client import ServiceAPIClient
from tests.conftest import SERVICE_ONE_ID
FAKE_TEMPLATE_ID = uuid4()
def test_client_posts_archived_true_when_deleting_template(mocker):
mocker.patch('app.notify_client.current_user', id='1')
mock_redis_delete_by_pattern = mocker.patch('app.extensions.RedisClient.delete_cache_keys_by_pattern')
expected_data = {
'archived': True,
'created_by': '1'
}
expected_url = '/service/{}/template/{}'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID)
client = ServiceAPIClient()
mock_post = mocker.patch('app.notify_client.service_api_client.ServiceAPIClient.post')
mocker.patch('app.notify_client.service_api_client.ServiceAPIClient.get',
return_value={'data': {'id': str(FAKE_TEMPLATE_ID)}})
client.delete_service_template(SERVICE_ONE_ID, FAKE_TEMPLATE_ID)
mock_post.assert_called_once_with(expected_url, data=expected_data)
assert call(f'service-{SERVICE_ONE_ID}-template-*') in mock_redis_delete_by_pattern.call_args_list
def test_client_gets_service(mocker):
client = ServiceAPIClient()
mock_get = mocker.patch.object(client, 'get', return_value={})
client.get_service('foo')
mock_get.assert_called_once_with('/service/foo')
@pytest.mark.parametrize('limit_days', [None, 30])
def test_client_gets_service_statistics(mocker, limit_days):
client = ServiceAPIClient()
mock_get = mocker.patch.object(client, 'get', return_value={'data': {'a': 'b'}})
ret = client.get_service_statistics('foo', limit_days)
assert ret == {'a': 'b'}
mock_get.assert_called_once_with('/service/foo/statistics', params={'limit_days': limit_days})
def test_client_only_updates_allowed_attributes(mocker):
mocker.patch('app.notify_client.current_user', id='1')
with pytest.raises(TypeError) as error:
ServiceAPIClient().update_service('service_id', foo='bar')
assert str(error.value) == 'Not allowed to update service attributes: foo'
def test_client_creates_service_with_correct_data(
mocker,
active_user_with_permissions,
fake_uuid,
):
client = ServiceAPIClient()
mock_post = mocker.patch.object(client, 'post', return_value={'data': {'id': None}})
mocker.patch('app.notify_client.current_user', id='123')
client.create_service(
'My first service',
'central_government',
1,
True,
fake_uuid,
'test@example.com',
)
mock_post.assert_called_once_with(
'/service',
dict(
# Autogenerated arguments
created_by='123',
active=True,
# service_name argument is coerced to name
name='My first service',
# The rest pass through with the same names
organisation_type='central_government',
message_limit=1,
restricted=True,
user_id=fake_uuid,
email_from='test@example.com',
),
)
def test_get_precompiled_template(mocker):
client = ServiceAPIClient()
mock_get = mocker.patch.object(client, 'get')
client.get_precompiled_template(SERVICE_ONE_ID)
mock_get.assert_called_once_with('/service/{}/template/precompiled'.format(SERVICE_ONE_ID))
@pytest.mark.parametrize('template_data, extra_args, expected_count', (
(
[],
{},
0,
),
(
[],
{'template_type': 'email'},
0,
),
(
[
{'template_type': 'email'},
{'template_type': 'sms'},
],
{},
2,
),
(
[
{'template_type': 'email'},
{'template_type': 'sms'},
],
{'template_type': 'email'},
1,
),
(
[
{'template_type': 'email'},
{'template_type': 'sms'},
],
{'template_type': 'letter'},
0,
),
))
def test_client_returns_count_of_service_templates(
notify_admin,
mocker,
template_data,
extra_args,
expected_count,
):
mocker.patch(
'app.service_api_client.get_service_templates',
return_value={'data': template_data}
)
assert service_api_client.count_service_templates(
SERVICE_ONE_ID, **extra_args
) == expected_count
@pytest.mark.parametrize(
(
'client_method,'
'extra_args,'
'expected_cache_get_calls,'
'cache_value,'
'expected_api_calls,'
'expected_cache_set_calls,'
'expected_return_value,'
),
[
(
service_api_client.get_service,
[SERVICE_ONE_ID],
[
call('service-{}'.format(SERVICE_ONE_ID))
],
b'{"data_from": "cache"}',
[],
[],
{'data_from': 'cache'},
),
(
service_api_client.get_service,
[SERVICE_ONE_ID],
[
call('service-{}'.format(SERVICE_ONE_ID))
],
None,
[
call('/service/{}'.format(SERVICE_ONE_ID))
],
[
call(
'service-{}'.format(SERVICE_ONE_ID),
'{"data_from": "api"}',
ex=604800,
)
],
{'data_from': 'api'},
),
(
service_api_client.get_service_template,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID],
[
call('service-{}-template-{}-version-None'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
b'{"data_from": "cache"}',
[],
[],
{'data_from': 'cache'},
),
(
service_api_client.get_service_template,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID],
[
call('service-{}-template-{}-version-None'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID)),
],
None,
[
call('/service/{}/template/{}'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
[
call(
'service-{}-template-{}-version-None'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'{"data_from": "api"}',
ex=604800,
),
],
{'data_from': 'api'},
),
(
service_api_client.get_service_template,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID, 1],
[
call('service-{}-template-{}-version-1'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
b'{"data_from": "cache"}',
[],
[],
{'data_from': 'cache'},
),
(
service_api_client.get_service_template,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID, 1],
[
call('service-{}-template-{}-version-1'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID)),
],
None,
[
call('/service/{}/template/{}/version/1'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
[
call(
'service-{}-template-{}-version-1'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'{"data_from": "api"}',
ex=604800,
),
],
{'data_from': 'api'},
),
(
service_api_client.get_service_templates,
[SERVICE_ONE_ID],
[
call('service-{}-templates'.format(SERVICE_ONE_ID))
],
b'{"data_from": "cache"}',
[],
[],
{'data_from': 'cache'},
),
(
service_api_client.get_service_templates,
[SERVICE_ONE_ID],
[
call('service-{}-templates'.format(SERVICE_ONE_ID))
],
None,
[
call('/service/{}/template?detailed=False'.format(SERVICE_ONE_ID))
],
[
call(
'service-{}-templates'.format(SERVICE_ONE_ID),
'{"data_from": "api"}',
ex=604800,
)
],
{'data_from': 'api'},
),
(
service_api_client.get_service_template_versions,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID],
[
call('service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
b'{"data_from": "cache"}',
[],
[],
{'data_from': 'cache'},
),
(
service_api_client.get_service_template_versions,
[SERVICE_ONE_ID, FAKE_TEMPLATE_ID],
[
call('service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID)),
],
None,
[
call('/service/{}/template/{}/versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID))
],
[
call(
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'{"data_from": "api"}',
ex=604800,
),
],
{'data_from': 'api'},
),
(
service_api_client.get_returned_letter_summary,
[SERVICE_ONE_ID],
[
call('service-{}-returned-letters-summary'.format(SERVICE_ONE_ID))
],
None,
[
call('service/{}/returned-letter-summary'.format(SERVICE_ONE_ID))
],
[
call(
'service-{}-returned-letters-summary'.format(SERVICE_ONE_ID),
'{"data_from": "api"}',
ex=604800,
)
],
{'data_from': 'api'},
),
(
service_api_client.get_returned_letter_statistics,
[SERVICE_ONE_ID],
[
call('service-{}-returned-letters-statistics'.format(SERVICE_ONE_ID))
],
None,
[
call('service/{}/returned-letter-statistics'.format(SERVICE_ONE_ID))
],
[
call(
'service-{}-returned-letters-statistics'.format(SERVICE_ONE_ID),
'{"data_from": "api"}',
ex=604800,
)
],
{'data_from': 'api'},
),
]
)
def test_returns_value_from_cache(
mocker,
client_method,
extra_args,
expected_cache_get_calls,
cache_value,
expected_return_value,
expected_api_calls,
expected_cache_set_calls,
):
mock_redis_get = mocker.patch(
'app.extensions.RedisClient.get',
return_value=cache_value,
)
mock_api_get = mocker.patch(
'app.notify_client.NotifyAdminAPIClient.get',
return_value={'data_from': 'api'},
)
mock_redis_set = mocker.patch(
'app.extensions.RedisClient.set',
)
assert client_method(*extra_args) == expected_return_value
assert mock_redis_get.call_args_list == expected_cache_get_calls
assert mock_api_get.call_args_list == expected_api_calls
assert mock_redis_set.call_args_list == expected_cache_set_calls
@pytest.mark.parametrize('client, method, extra_args, extra_kwargs', [
(service_api_client, 'update_service', [SERVICE_ONE_ID], {'name': 'foo'}),
(service_api_client, 'update_service_with_properties', [SERVICE_ONE_ID], {'properties': {}}),
(service_api_client, 'archive_service', [SERVICE_ONE_ID, []], {}),
(service_api_client, 'suspend_service', [SERVICE_ONE_ID], {}),
(service_api_client, 'resume_service', [SERVICE_ONE_ID], {}),
(service_api_client, 'remove_user_from_service', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'update_guest_list', [SERVICE_ONE_ID, {}], {}),
(service_api_client, 'create_service_inbound_api', [SERVICE_ONE_ID] + [''] * 3, {}),
(service_api_client, 'update_service_inbound_api', [SERVICE_ONE_ID] + [''] * 4, {}),
(service_api_client, 'add_reply_to_email_address', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'update_reply_to_email_address', [SERVICE_ONE_ID] + [''] * 2, {}),
(service_api_client, 'delete_reply_to_email_address', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'add_letter_contact', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'update_letter_contact', [SERVICE_ONE_ID] + [''] * 2, {}),
(service_api_client, 'delete_letter_contact', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'add_sms_sender', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'update_sms_sender', [SERVICE_ONE_ID] + [''] * 2, {}),
(service_api_client, 'delete_sms_sender', [SERVICE_ONE_ID, ''], {}),
(service_api_client, 'update_service_callback_api', [SERVICE_ONE_ID] + [''] * 4, {}),
(service_api_client, 'create_service_callback_api', [SERVICE_ONE_ID] + [''] * 3, {}),
(service_api_client, 'set_service_broadcast_settings', [SERVICE_ONE_ID, 'training', 'severe', 'all', []], {}),
(user_api_client, 'add_user_to_service', [SERVICE_ONE_ID, uuid4(), [], []], {}),
(invite_api_client, 'accept_invite', [SERVICE_ONE_ID, uuid4()], {}),
])
def test_deletes_service_cache(
notify_admin,
mock_get_user,
mock_get_service_templates,
mocker,
client,
method,
extra_args,
extra_kwargs,
):
mocker.patch('app.notify_client.current_user', id='1')
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mock_request = mocker.patch('notifications_python_client.base.BaseAPIClient.request')
getattr(client, method)(*extra_args, **extra_kwargs)
assert call('service-{}'.format(SERVICE_ONE_ID)) in mock_redis_delete.call_args_list
assert len(mock_request.call_args_list) == 1
@pytest.mark.parametrize('method, extra_args, expected_cache_deletes', [
('create_service_template', ['name', 'type_', 'content', SERVICE_ONE_ID], [
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('update_service_template', [FAKE_TEMPLATE_ID, 'foo', 'sms', 'bar', SERVICE_ONE_ID], [
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('redact_service_template', [SERVICE_ONE_ID, FAKE_TEMPLATE_ID], [
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('update_service_template_sender', [SERVICE_ONE_ID, FAKE_TEMPLATE_ID, 'foo'], [
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('update_service_template_postage', [SERVICE_ONE_ID, FAKE_TEMPLATE_ID, 'first'], [
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('delete_service_template', [SERVICE_ONE_ID, FAKE_TEMPLATE_ID], [
'service-{}-template-{}-versions'.format(SERVICE_ONE_ID, FAKE_TEMPLATE_ID),
'service-{}-templates'.format(SERVICE_ONE_ID),
]),
('archive_service', [SERVICE_ONE_ID, []], [
'service-{}-templates'.format(SERVICE_ONE_ID),
'service-{}'.format(SERVICE_ONE_ID),
]),
])
def test_deletes_caches_when_modifying_templates(
notify_admin,
mock_get_user,
mocker,
method,
extra_args,
expected_cache_deletes,
):
mocker.patch('app.notify_client.current_user', id='1')
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mock_redis_delete_by_pattern = mocker.patch('app.extensions.RedisClient.delete_cache_keys_by_pattern')
mock_request = mocker.patch('notifications_python_client.base.BaseAPIClient.request')
getattr(service_api_client, method)(*extra_args)
assert mock_redis_delete.call_args_list == [call(x) for x in expected_cache_deletes]
assert len(mock_request.call_args_list) == 1
if method != 'create_service_template':
# no deletes for template cach on create_service_template
assert len(mock_redis_delete_by_pattern.call_args_list) == 1
assert mock_redis_delete_by_pattern.call_args_list[0] == call(f'service-{SERVICE_ONE_ID}-template-*')
def test_deletes_cached_users_when_archiving_service(mocker, mock_get_service_templates):
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mock_redis_delete_by_pattern = mocker.patch('app.extensions.RedisClient.delete_cache_keys_by_pattern')
mocker.patch('notifications_python_client.base.BaseAPIClient.request', return_value={'data': ""})
service_api_client.archive_service(SERVICE_ONE_ID, ["my-user-id1", "my-user-id2"])
assert call('user-my-user-id1', 'user-my-user-id2') in mock_redis_delete.call_args_list
assert call(f'service-{SERVICE_ONE_ID}-template-*') in mock_redis_delete_by_pattern.call_args_list
def test_deletes_cached_users_when_changing_broadcast_service_settings(mocker):
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mocker.patch('notifications_python_client.base.BaseAPIClient.request', return_value={'data': ""})
service_api_client.set_service_broadcast_settings(SERVICE_ONE_ID,
'live',
'severe',
'all',
["my-user-id1", "my-user-id2"])
assert mock_redis_delete.call_args_list == [
call('user-my-user-id1', 'user-my-user-id2'),
call(f'service-{SERVICE_ONE_ID}'),
]
def test_client_gets_guest_list(mocker):
client = ServiceAPIClient()
mock_get = mocker.patch.object(client, 'get', return_value=['a', 'b', 'c'])
response = client.get_guest_list('foo')
assert response == ['a', 'b', 'c']
mock_get.assert_called_once_with(
url='/service/foo/guest-list',
)
def test_client_updates_guest_list(mocker):
client = ServiceAPIClient()
mock_put = mocker.patch.object(client, 'put')
client.update_guest_list('foo', data=['a', 'b', 'c'])
mock_put.assert_called_once_with(
url='/service/foo/guest-list',
data=['a', 'b', 'c'],
)
def test_client_doesnt_delete_service_template_cache_when_none_exist(
notify_admin,
mock_get_user,
mock_get_service_templates_when_no_templates_exist,
mocker
):
mocker.patch('app.notify_client.current_user', id='1')
mocker.patch('notifications_python_client.base.BaseAPIClient.request')
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mock_redis_delete_by_pattern = mocker.patch('app.extensions.RedisClient.delete_cache_keys_by_pattern')
service_api_client.update_reply_to_email_address(SERVICE_ONE_ID, uuid4(), 'foo@bar.com')
assert len(mock_redis_delete.call_args_list) == 1
assert mock_redis_delete.call_args_list[0] == call('service-{}'.format(SERVICE_ONE_ID))
assert len(mock_redis_delete_by_pattern.call_args_list) == 1
def test_client_deletes_service_template_cache_when_service_is_updated(
notify_admin,
mock_get_user,
mocker
):
mocker.patch('app.notify_client.current_user', id='1')
mocker.patch('notifications_python_client.base.BaseAPIClient.request')
mock_redis_delete = mocker.patch('app.extensions.RedisClient.delete')
mock_redis_delete_by_pattern = mocker.patch('app.extensions.RedisClient.delete_cache_keys_by_pattern')
service_api_client.update_reply_to_email_address(SERVICE_ONE_ID, uuid4(), 'foo@bar.com')
assert len(mock_redis_delete.call_args_list) == 1
assert mock_redis_delete.call_args_list[0] == call(f'service-{SERVICE_ONE_ID}')
assert mock_redis_delete_by_pattern.call_args_list[0] == call(f'service-{SERVICE_ONE_ID}-template-*')
def test_client_updates_service_with_allowed_attributes(
mocker,
):
client = ServiceAPIClient()
mock_post = mocker.patch.object(client, 'post', return_value={'data': {'id': None}})
mocker.patch('app.notify_client.current_user', id='123')
allowed_attributes = [
'active',
'consent_to_research',
'contact_link',
'count_as_live',
'email_branding',
'email_from',
'free_sms_fragment_limit',
'go_live_at',
'go_live_user',
'letter_branding',
'letter_contact_block',
'message_limit',
'name',
'notes',
'organisation_type',
'permissions',
'prefix_sms',
'rate_limit',
'reply_to_email_address',
'research_mode',
'restricted',
'sms_sender',
'volume_email',
'volume_letter',
'volume_sms',
]
attrs_dict = {}
for attr in allowed_attributes:
attrs_dict[attr] = "value"
client.update_service(
SERVICE_ONE_ID,
**attrs_dict
)
mock_post.assert_called_once_with(
f'/service/{SERVICE_ONE_ID}',
{**{'created_by': '123'}, **attrs_dict}
)