diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index ccac190ac..eaf898ddf 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -301,3 +301,25 @@ def send_canary_to_cbc_proxy(): message = f"Sending a canary message to CBC proxy with ID {identifier}" current_app.logger.info(message) cbc_proxy_client.send_canary(identifier) + + +@notify_celery.task(name='trigger-link-tests') +def trigger_link_tests(): + """ + Currently we only have one hardcoded CBC Proxy, which corresponds to one + CBC, and so currently we do not specify the CBC Proxy name + + In future we will have multiple CBC proxies, each proxy corresponding to + one MNO's CBC + + This task should invoke other tasks which do the actual link tests, eg: + for cbc_name in app.config.ENABLED_CBCS: + send_link_test_for_cbc(cbc_name) + + Alternatively this task could be configured to be a Celery group + """ + for _ in range(1): + identifier = str(uuid.uuid4()) + message = f"Sending a link test to CBC proxy with ID {identifier}" + current_app.logger.info(message) + cbc_proxy_client.send_link_test(identifier) diff --git a/app/clients/cbc_proxy.py b/app/clients/cbc_proxy.py index 57296fa03..d6f2716c9 100644 --- a/app/clients/cbc_proxy.py +++ b/app/clients/cbc_proxy.py @@ -32,6 +32,12 @@ class CBCProxyNoopClient: ): pass + def send_link_test( + self, + identifier, + ): + pass + def create_and_send_broadcast( self, identifier, headline, description, areas @@ -83,11 +89,33 @@ class CBCProxyClient: if 'FunctionError' in result: raise Exception('Function exited with unhandled exception') + def send_link_test( + self, + identifier, + ): + payload_bytes = bytes(json.dumps({ + 'message_type': 'test', + 'identifier': identifier, + }), encoding='utf8') + + result = self._lambda_client.invoke( + FunctionName='bt-ee-1-proxy', + InvocationType='RequestResponse', + Payload=payload_bytes, + ) + + if result['StatusCode'] > 299: + raise Exception('Could not invoke lambda') + + if 'FunctionError' in result: + raise Exception('Function exited with unhandled exception') + def create_and_send_broadcast( self, identifier, headline, description, areas, ): payload_bytes = bytes(json.dumps({ + 'message_type': 'alert', 'identifier': identifier, 'headline': headline, 'description': description, diff --git a/app/config.py b/app/config.py index 1ad9bcea7..f689e0201 100644 --- a/app/config.py +++ b/app/config.py @@ -308,6 +308,11 @@ class Config(object): 'schedule': timedelta(minutes=5), 'options': {'queue': QueueNames.PERIODIC} }, + 'trigger-link-tests': { + 'task': 'trigger-link-tests', + 'schedule': timedelta(minutes=5), + 'options': {'queue': QueueNames.PERIODIC} + }, } CELERY_QUEUES = [] diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index 2c6bf3750..c76632c2e 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -575,3 +575,22 @@ def test_send_canary_to_cbc_proxy_invokes_cbc_proxy_client( uuid.UUID(identifier) except BaseException: pytest.fail(f"{identifier} is not a valid uuid") + + +def test_trigger_link_tests_invokes_cbc_proxy_client( + mocker, +): + mock_send_link_test = mocker.patch( + 'app.cbc_proxy_client.send_link_test', + ) + + scheduled_tasks.trigger_link_tests() + + mock_send_link_test.assert_called + # the 0th argument of the call to send_link_test + identifier = mock_send_link_test.mock_calls[0][1][0] + + try: + uuid.UUID(identifier) + except BaseException: + pytest.fail(f"{identifier} is not a valid uuid") diff --git a/tests/app/clients/test_cbc_proxy.py b/tests/app/clients/test_cbc_proxy.py index 8368180c7..981c50ceb 100644 --- a/tests/app/clients/test_cbc_proxy.py +++ b/tests/app/clients/test_cbc_proxy.py @@ -74,6 +74,7 @@ def test_cbc_proxy_create_and_send_invokes_function(mocker, cbc_proxy): payload = json.loads(payload_bytes) assert payload['identifier'] == identifier + assert payload['message_type'] == 'alert' assert payload['headline'] == headline assert payload['description'] == description assert payload['areas'] == areas @@ -251,3 +252,89 @@ def test_cbc_proxy_send_canary_handles_function_error(mocker, cbc_proxy): InvocationType='RequestResponse', Payload=mocker.ANY, ) + + +def test_cbc_proxy_send_link_test_invokes_function(mocker, cbc_proxy): + identifier = str(uuid.uuid4()) + + ld_client_mock = mocker.patch.object( + cbc_proxy, + '_lambda_client', + create=True, + ) + + ld_client_mock.invoke.return_value = { + 'StatusCode': 200, + } + + cbc_proxy.send_link_test( + identifier=identifier, + ) + + ld_client_mock.invoke.assert_called_once_with( + FunctionName='bt-ee-1-proxy', + InvocationType='RequestResponse', + Payload=mocker.ANY, + ) + + kwargs = ld_client_mock.invoke.mock_calls[0][-1] + payload_bytes = kwargs['Payload'] + payload = json.loads(payload_bytes) + + assert payload['identifier'] == identifier + assert payload['message_type'] == 'test' + + +def test_cbc_proxy_send_link_test_handles_invoke_error(mocker, cbc_proxy): + identifier = str(uuid.uuid4()) + + ld_client_mock = mocker.patch.object( + cbc_proxy, + '_lambda_client', + create=True, + ) + + ld_client_mock.invoke.return_value = { + 'StatusCode': 400, + } + + with pytest.raises(Exception) as e: + cbc_proxy.send_link_test( + identifier=identifier, + ) + + assert e.match('Function exited with unhandled exception') + + ld_client_mock.invoke.assert_called_once_with( + FunctionName='bt-ee-1-proxy', + InvocationType='RequestResponse', + Payload=mocker.ANY, + ) + + +def test_cbc_proxy_send_link_test_handles_function_error(mocker, cbc_proxy): + identifier = str(uuid.uuid4()) + + ld_client_mock = mocker.patch.object( + cbc_proxy, + '_lambda_client', + create=True, + ) + + ld_client_mock.invoke.return_value = { + 'StatusCode': 200, + 'FunctionError': 'something', + } + + with pytest.raises(Exception) as e: + cbc_proxy.send_link_test( + identifier=identifier, + ) + + assert e.match('Could not invoke lambda') + + ld_client_mock.invoke.assert_called_once_with( + FunctionName='bt-ee-1-proxy', + InvocationType='RequestResponse', + Payload=mocker.ANY, + )