diff --git a/aries_cloudagent/core/conductor.py b/aries_cloudagent/core/conductor.py index f1551f1031..96746e6eaf 100644 --- a/aries_cloudagent/core/conductor.py +++ b/aries_cloudagent/core/conductor.py @@ -264,8 +264,9 @@ def inbound_message_router( lambda completed: self.dispatch_complete(message, completed), ) except (LedgerConfigError, LedgerTransactionError) as e: - LOGGER.error("Shutdown with %s", str(e)) - self.admin_server.notify_fatal_error() + LOGGER.error("Shutdown with ledger error %s", str(e)) + if self.admin_server: + self.admin_server.notify_fatal_error() raise def dispatch_complete(self, message: InboundMessage, completed: CompletedTask): @@ -330,8 +331,9 @@ def handle_not_returned(self, context: InjectionContext, outbound: OutboundMessa try: self.dispatcher.run_task(self.queue_outbound(context, outbound)) except (LedgerConfigError, LedgerTransactionError) as e: - LOGGER.error("Shutdown with %s", str(e)) - self.admin_server.notify_fatal_error() + LOGGER.error("Shutdown with ledger error %s", str(e)) + if self.admin_server: + self.admin_server.notify_fatal_error() raise async def queue_outbound( @@ -360,8 +362,9 @@ async def queue_outbound( LOGGER.exception("Error preparing outbound message for transmission") return except (LedgerConfigError, LedgerTransactionError) as e: - LOGGER.error("Shutdown with %s", str(e)) - self.admin_server.notify_fatal_error() + LOGGER.error("Shutdown with ledger error %s", str(e)) + if self.admin_server: + self.admin_server.notify_fatal_error() raise try: diff --git a/aries_cloudagent/core/tests/test_conductor.py b/aries_cloudagent/core/tests/test_conductor.py index 10819bfbb1..094482453e 100644 --- a/aries_cloudagent/core/tests/test_conductor.py +++ b/aries_cloudagent/core/tests/test_conductor.py @@ -35,11 +35,14 @@ class Config: test_settings = {"admin.webhook_urls": ["http://sample.webhook.ca"]} + test_settings_admin = { + "admin.webhook_urls": ["http://sample.webhook.ca"], + "admin.enabled": True, + } test_settings_with_queue = {"queue.enable_undelivered_queue": True} class TestDIDs: - test_seed = "testseed000000000000000000000001" test_did = "55GkHamhTU1ZbTbV2ab9DE" test_verkey = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx" @@ -223,7 +226,7 @@ async def test_inbound_message_handler(self): with async_mock.patch.object( conductor.dispatcher, "queue_message", autospec=True - ) as mock_dispatch: + ) as mock_dispatch_q: message_body = "{}" receipt = MessageReceipt(direct_response_mode="snail mail") @@ -231,11 +234,34 @@ async def test_inbound_message_handler(self): conductor.inbound_message_router(message, can_respond=False) - mock_dispatch.assert_called_once() - assert mock_dispatch.call_args[0][0] is message - assert mock_dispatch.call_args[0][1] == conductor.outbound_message_router - assert mock_dispatch.call_args[0][2] is None # admin webhook router - assert callable(mock_dispatch.call_args[0][3]) + mock_dispatch_q.assert_called_once() + assert mock_dispatch_q.call_args[0][0] is message + assert mock_dispatch_q.call_args[0][1] == conductor.outbound_message_router + assert mock_dispatch_q.call_args[0][2] is None # admin webhook router + assert callable(mock_dispatch_q.call_args[0][3]) + + async def test_inbound_message_handler_ledger_x(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) + conductor = test_module.Conductor(builder) + + await conductor.setup() + + with async_mock.patch.object( + conductor.dispatcher, "queue_message", autospec=True + ) as mock_dispatch_q, async_mock.patch.object( + conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() + ) as mock_notify: + mock_dispatch_q.side_effect = test_module.LedgerConfigError("ledger down") + + message_body = "{}" + receipt = MessageReceipt(direct_response_mode="snail mail") + message = InboundMessage(message_body, receipt) + + with self.assertRaises(test_module.LedgerConfigError): + conductor.inbound_message_router(message, can_respond=False) + + mock_dispatch_q.assert_called_once() + mock_notify.assert_called_once() async def test_outbound_message_handler_return_route(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) @@ -368,7 +394,7 @@ async def test_handle_nots(self): with async_mock.patch.object( test_module, "ConnectionManager" ) as mock_conn_mgr, async_mock.patch.object( - conductor.dispatcher, "run_task", async_mock.CoroutineMock() + conductor.dispatcher, "run_task", async_mock.MagicMock() ) as mock_run_task: mock_conn_mgr.return_value.get_connection_targets = ( async_mock.CoroutineMock() @@ -384,6 +410,67 @@ async def test_handle_nots(self): await conductor.queue_outbound(conductor.context, message) mock_run_task.assert_called_once() + async def test_handle_not_returned_ledger_x(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) + conductor = test_module.Conductor(builder) + + await conductor.setup() + + with async_mock.patch.object( + conductor.dispatcher, "run_task", async_mock.MagicMock() + ) as mock_dispatch_run, async_mock.patch.object( + conductor, "queue_outbound", async_mock.CoroutineMock() + ) as mock_queue, async_mock.patch.object( + conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() + ) as mock_notify: + mock_dispatch_run.side_effect = test_module.LedgerConfigError( + "No such ledger" + ) + + payload = "{}" + message = OutboundMessage( + payload=payload, + connection_id="dummy-conn-id", + reply_to_verkey=TestDIDs.test_verkey, + ) + + with self.assertRaises(test_module.LedgerConfigError): + conductor.handle_not_returned(conductor.context, message) + + mock_dispatch_run.assert_called_once() + mock_notify.assert_called_once() + + async def test_queue_outbound_ledger_x(self): + builder: ContextBuilder = StubContextBuilder(self.test_settings_admin) + conductor = test_module.Conductor(builder) + + await conductor.setup() + + with async_mock.patch.object( + test_module, "ConnectionManager", autospec=True + ) as conn_mgr, async_mock.patch.object( + conductor.dispatcher, "run_task", async_mock.MagicMock() + ) as mock_dispatch_run, async_mock.patch.object( + conductor.admin_server, "notify_fatal_error", async_mock.MagicMock() + ) as mock_notify: + conn_mgr.return_value.get_connection_targets = async_mock.CoroutineMock() + mock_dispatch_run.side_effect = test_module.LedgerConfigError( + "No such ledger" + ) + + payload = "{}" + message = OutboundMessage( + payload=payload, + connection_id="dummy-conn-id", + reply_to_verkey=TestDIDs.test_verkey, + ) + + with self.assertRaises(test_module.LedgerConfigError): + await conductor.queue_outbound(conductor.context, message) + + mock_dispatch_run.assert_called_once() + mock_notify.assert_called_once() + async def test_admin(self): builder: ContextBuilder = StubContextBuilder(self.test_settings) builder.update_settings({"admin.enabled": "1"})